meson icon indicating copy to clipboard operation
meson copied to clipboard

Improve the error reporting in meson

Open alexandre-janniaux opened this issue 1 year ago • 15 comments

Hi,

~This MR is in a RFC state for now, many things need to be improved before merging,~ but I'd like to have feedback on how the error is improved and how to handle the line reporting itself before.

This MR stems from a very simple case where I found the error reporting very confusing with the following code:

if host_system = 'android'

When running meson, we would only get the following confusing error:

meson.build:6:17: ERROR: Cannot perform 'if' operation on void statement.

This MR thrive to add the necessary infrastructure and convention to report better errors with a way to solve the issue. Typically, the previous example would now output the following error:

meson.build:6:3: ERROR: Assignment are returning void and not valid in if clause.
meson.build:6:3:  | if host_system = 'android'
meson.build:6:3:  |    ^^^^^^^^^^^^^^
meson.build:6:3:  |    Did you forget a = here?

The current implementation is conservative to where the errors were not extended to this new information.

Many questions are arising from that regarding the formatting:

  • [ ] Should we append meson.build:6:3: in front of every line or lighten the presentation?
  • [ ] Should we append the line number on location where there is code?
  • [ ] How should we handle multi-line errors?
  • [ ] Should we test those errors and the span (which looks wrong here) in the CI?

I also implemented the line reporting in an ugly way, by storing the relevant line in every (well for now, only IfClauseNode and AssignmentNode, but that's one of the hypothesis of the code) AST node, but some nodes are spanning on multiple lines and there can be multiple nodes per line, so it looks a bit ugly even though we can make python use a single string in the back. We can also only report the relevant part of the code by referencing self.code[] or referencing the relevant substring when raise is happening. What would you prefer?

alexandre-janniaux avatar Aug 24 '23 09:08 alexandre-janniaux

Better error reporting is always great.

Played a bit with wrong statements in if condition, the problem is not limited to assignment:

  • if message('hello') -> ERROR: Cannot perform 'if' operation on void statement..
  • if fs.copyfile('foo.c') -> ERROR: Object <CustomTargetHolder foo.c@cus: ['/home/xclaesse/.local/bin/meson', '--internal', 'copy', '@INPUT@', '@OUTPUT@']> of type CustomTarget does not support the bool() operator.

xclaesse avatar Aug 24 '23 13:08 xclaesse

Played a bit with wrong statements in if condition, the problem is not limited to assignment:

Of course yes, as of now this is just a PoC, I'd like to improve where the error marker lands also so that it's easier to track with the eyes, and improve the line reporting since every node would need to include that, which is a bit ugly as-is.

Thanks a lot for some additional checks! That's very interesting given we'll typically need to unwind back in the evaluation for the fs.copyfile back to improve the reporting. Would you like to improve the errors after this message yourself or should I include that in the MR?

alexandre-janniaux avatar Aug 24 '23 13:08 alexandre-janniaux

I like this idea. Developers love when tooling is very explicit and makes their live easier, which this does. Thanks for taking this on. FYI you can mark PRs as drafts too.

I recently started learning Typst, and the compiler is amazing with errors.

tristan957 avatar Aug 25 '23 17:08 tristan957

All the linting jobs are failing

tristan957 avatar Sep 06 '23 19:09 tristan957

I've addressed the issues from the two linters, but there are still issues with mypy, mostly because of kwargs. I've made the necessary changes to make it work on recent python, but I'm not sure of what should be done. The remaining issues are because the needed type annotations are not available:

mesonbuild/utils/core.py:39:32: error: Name "T.TypedDict" is not defined  [name-defined]
mesonbuild/utils/core.py:40:11: error: Name "T.NotRequired" is not defined  [name-defined]
mesonbuild/utils/core.py:41:13: error: Name "T.NotRequired" is not defined  [name-defined]
mesonbuild/utils/core.py:42:12: error: Name "T.NotRequired" is not defined  [name-defined]
mesonbuild/utils/core.py:54: error: Name "T.Unpack" is not defined  [name-defined]
mesonbuild/interpreterbase/interpreterbase.py:76: error: Name "T.Unpack" is not defined  [name-defined]

Should I try to get rid of the necessity for kwargs?

alexandre-janniaux avatar Oct 08 '23 21:10 alexandre-janniaux

cc @dcbaker

tristan957 avatar Oct 09 '23 14:10 tristan957

For now, I've switched the **kwargs to T.Any for now, I'll wait for python 3.12 to improve the parameter typing for the exceptions.

I've also fixed the lint errors, and changed the error reporting for void function inside if conditions so that it works with every void function, but now they will be evaluated before. (Unfortunately, I don't have any other solution to get the type for now).

I have a few changes on commit message to make and the mlog patches to re-improve, and an issue with the project with assignment where the carets are not displayed correctly, and this should be good to go.

alexandre-janniaux avatar Oct 10 '23 07:10 alexandre-janniaux

Test failures.

tristan957 avatar Mar 16 '24 16:03 tristan957

Unfortunately, I'm not sure to understand the remaining errors:

CI:

=================================== FAILURES ===================================
___________ LinuxlikeTests.test_generate_gir_with_address_sanitizer ____________
[gw2] linux -- Python 3.11.6 /usr/bin/python3

self = <unittests.linuxliketests.LinuxlikeTests testMethod=test_generate_gir_with_address_sanitizer>

    @skip_if_not_base_option('b_sanitize')
    def test_generate_gir_with_address_sanitizer(self):
        if is_cygwin():
            raise SkipTest('asan not available on Cygwin')
        if is_openbsd():
            raise SkipTest('-fsanitize=address is not supported on OpenBSD')
    
        testdir = os.path.join(self.framework_test_dir, '7 gnome')
        self.init(testdir, extra_args=['-Db_sanitize=address', '-Db_lundef=false'])
>       self.build()

unittests/linuxliketests.py:318: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/usr/lib/python3.11/subprocess.py:550: in run
    stdout, stderr = process.communicate(input, timeout=timeout)
/usr/lib/python3.11/subprocess.py:1209: in communicate
    stdout, stderr = self._communicate(input, endtime, timeout)
/usr/lib/python3.11/subprocess.py:2109: in _communicate
    self._check_timeout(endtime, orig_timeout, stdout, stderr)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Popen: returncode: -9 args: ['/usr/bin/ninja', '-w', 'dupbuild=err', '-d', ...>
endtime = 865.065316947, orig_timeout = 300
stdout_seq = [b"ninja explain: deps for 'resources/simple-resources-test.p/meson-generated_.._simple-resources.c.o' are missing\nni...rated-resources-test.p/generated-main.c.o is dirty\nninja explain: resources/generated-resources-test is dirty\n", ...]
stderr_seq = None, skip_check_and_raise = False

    def _check_timeout(self, endtime, orig_timeout, stdout_seq, stderr_seq,
                       skip_check_and_raise=False):
        """Convenience for checking if a timeout has expired."""
        if endtime is None:
            return
        if skip_check_and_raise or _time() > endtime:
>           raise TimeoutExpired(
                    self.args, orig_timeout,
                    output=b''.join(stdout_seq) if stdout_seq else None,
                    stderr=b''.join(stderr_seq) if stderr_seq else None)
E           subprocess.TimeoutExpired: Command '['/usr/bin/ninja', '-w', 'dupbuild=err', '-d', 'explain']' timed out after 300 seconds

/usr/lib/python3.11/subprocess.py:1253: TimeoutExpired
...
FAILED unittests/linuxliketests.py::LinuxlikeTests::test_generate_gir_with_address_sanitizer - subprocess.TimeoutExpired: Command '['/usr/bin/ninja', '-w', 'dupbuild=err', '-d', 'explain']' timed out after 300 seconds

Local:

╰─$ python run_unittests.py -v LinuxlikeTests.test_generate_gir_with_address_sanitizer
 ____________

Meson build system 1.4.99 Unit Tests
================================================================================================ test session starts ================================================================================================
platform linux -- Python 3.11.8, pytest-7.4.4, pluggy-1.4.0 -- /usr/bin/python
cachedir: .pytest_cache
rootdir: /home/alexandre/workspace/videolabs/meson
configfile: setup.cfg
plugins: xdist-3.5.0
collected 514 items / 513 deselected / 1 selected                                                                                                                                                                   

unittests/linuxliketests.py::LinuxlikeTests::test_generate_gir_with_address_sanitizer PASSED                                                                                                                  [100%]

================================================================================================= warnings summary ==================================================================================================
unittests/linuxliketests.py::LinuxlikeTests::test_generate_gir_with_address_sanitizer
unittests/linuxliketests.py::LinuxlikeTests::test_generate_gir_with_address_sanitizer
unittests/linuxliketests.py::LinuxlikeTests::test_generate_gir_with_address_sanitizer
  /home/alexandre/workspace/videolabs/meson/mesonbuild/utils/universal.py:1507: EncodingWarning: UTF-8 Mode affects locale.getpreferredencoding(). Consider locale.getencoding() instead.
    encoding = locale.getpreferredencoding()

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=================================================================================== 1 passed, 513 deselected, 3 warnings in 4.96s ===================================================================================
Total time: 5.199 seconds
zsh: command not found: ____________

Is it really related to my changes?

alexandre-janniaux avatar Mar 18 '24 07:03 alexandre-janniaux

I do have an error with testsetup and test_cross_pkg_config_option, but they are also happening from the master branch for me too:

======================================== short test summary info =========================================
FAILED unittests/linuxcrosstests.py::LinuxCrossMingwTests::test_cross_pkg_config_option - subprocess.CalledProcessError: Command '['/usr/bin/python', '/home/alexandre/workspace/videolabs/meso...
FAILED unittests/allplatformstests.py::AllPlatformTests::test_testsetups - AssertionError: 'TEST_ENV is set' not found in 'Log of Meson test suite run on 2024-03-18T08:35:42.23...

alexandre-janniaux avatar Mar 18 '24 07:03 alexandre-janniaux

testsetup was failing because I needed to export a valid DEBUGINFOD url, it's ok now.

I guess the pkg_config test is failing because it's using env[PKG_CONFIG]: /usr/bin/x86_64-w64-mingw32-pkg-config which will redirect to my system sysroot on archlinux. Since the test seems to be providing a dependency in a different sysroot, the dependency cannot be found.

alexandre-janniaux avatar Mar 18 '24 07:03 alexandre-janniaux

When do you think this PR will be ready for review?

tristan957 avatar Mar 21 '24 16:03 tristan957

If you're not ready for review or to land could you convert the PR to a draft? that helps people know what the current state of the PR is

dcbaker avatar Mar 21 '24 16:03 dcbaker

I've removed the warning. I think that the MR is ready, now that I could check in the test what kind of result it would output and understand the contribution process of meson a bit more, but I'm still open to change if that's requested.

alexandre-janniaux avatar Mar 21 '24 21:03 alexandre-janniaux

Ok, after rebasing another time, the tests are now passing.

alexandre-janniaux avatar Apr 08 '24 12:04 alexandre-janniaux

Looks like tests are still failing. A cleaned up git history would also be appreciated!

tristan957 avatar Aug 07 '24 22:08 tristan957

Looks like tests are still failing. A cleaned up git history would also be appreciated!

Indeed, I've pushed to synchronize between my two machines but the MR is back to not being 100% ready. I've added error handling in mparser.py in this MR because of test that were now failing (from the Failing/ directory, but still failing because they were not matching the expected output).

image

It led to a few fixes on the original MR though, which is nice. I've also changed the behaviour of the caret addition to default to one caret, as it hugely simplify when only a single caret needs to be added and avoid underlining the whole line if the end column is not yet specified.

I'm still working on the intermediate patches so that it doesn't break the code in the middle of the branch. Thanks for checking the MR! I'll post again when this is finished.

alexandre-janniaux avatar Aug 08 '24 07:08 alexandre-janniaux