diff options
| -rw-r--r-- | mesonbuild/ast/interpreter.py | 30 | ||||
| -rw-r--r-- | mesonbuild/interpreterbase/__init__.py | 2 | ||||
| -rw-r--r-- | mesonbuild/interpreterbase/baseobjects.py | 4 | ||||
| -rw-r--r-- | test cases/rewrite/7 tricky dataflow/addSrc.json | 5 | ||||
| -rw-r--r-- | test cases/rewrite/7 tricky dataflow/info.json | 5 | ||||
| -rw-r--r-- | test cases/rewrite/7 tricky dataflow/meson.build | 6 | ||||
| -rw-r--r-- | test cases/unit/120 rewrite/meson.build | 6 | ||||
| -rw-r--r-- | unittests/rewritetests.py | 1 |
8 files changed, 49 insertions, 10 deletions
diff --git a/mesonbuild/ast/interpreter.py b/mesonbuild/ast/interpreter.py index 473ad9119..62c4839a7 100644 --- a/mesonbuild/ast/interpreter.py +++ b/mesonbuild/ast/interpreter.py @@ -13,7 +13,7 @@ from dataclasses import dataclass import itertools from pathlib import Path -from .. import mparser, mesonlib +from .. import mparser, mesonlib, mlog from .. import environment from ..interpreterbase import ( @@ -26,6 +26,7 @@ from ..interpreterbase import ( default_resolve_key, is_disabled, UnknownValue, + UndefinedVariable, InterpreterObject, ) @@ -460,14 +461,14 @@ class AstInterpreter(InterpreterBase): self.nesting.pop() for var_name in self.cur_assignments: potential_values = [] - oldval = self.get_cur_value(var_name, allow_none=True) - if oldval is not None: + oldval = self.get_cur_value_if_defined(var_name) + if not isinstance(oldval, UndefinedVariable): potential_values.append(oldval) for nesting, value in self.cur_assignments[var_name]: if len(nesting) > len(self.nesting): potential_values.append(value) self.cur_assignments[var_name] = [(nesting, v) for (nesting, v) in self.cur_assignments[var_name] if len(nesting) <= len(self.nesting)] - if len(potential_values) > 1 or (len(potential_values) > 0 and oldval is None): + if len(potential_values) > 1 or (len(potential_values) > 0 and isinstance(oldval, UndefinedVariable)): uv = UnknownValue() for pv in potential_values: self.dataflow_dag.add_edge(pv, uv) @@ -484,19 +485,28 @@ class AstInterpreter(InterpreterBase): raise TypeError return ret - def get_cur_value(self, var_name: str, allow_none: bool = False) -> T.Union[BaseNode, UnknownValue, None]: + def get_cur_value_if_defined(self, var_name: str) -> T.Union[BaseNode, UnknownValue, UndefinedVariable]: if var_name in {'meson', 'host_machine', 'build_machine', 'target_machine'}: return UnknownValue() - ret = None + ret: T.Union[BaseNode, UnknownValue, UndefinedVariable] = UndefinedVariable() for nesting, value in reversed(self.cur_assignments[var_name]): if len(self.nesting) >= len(nesting) and self.nesting[:len(nesting)] == nesting: ret = value break - if ret is None and allow_none: - return ret - if ret is None and self.tainted: + if isinstance(ret, UndefinedVariable) and self.tainted: + return UnknownValue() + return ret + + def get_cur_value(self, var_name: str) -> T.Union[BaseNode, UnknownValue]: + ret = self.get_cur_value_if_defined(var_name) + if isinstance(ret, UndefinedVariable): + path = mlog.get_relative_path(Path(self.current_node.filename), Path(os.getcwd())) + mlog.warning(f"{path}:{self.current_node.lineno}:{self.current_node.colno} will always crash if executed, since a variable named `{var_name}` is not defined") + # We could add more advanced analysis of code referencing undefined + # variables, but it is probably not worth the effort and the + # complexity. So we do the simplest thing, returning an + # UnknownValue. return UnknownValue() - assert ret is not None return ret # The function `node_to_runtime_value` takes a node of the ast as an diff --git a/mesonbuild/interpreterbase/__init__.py b/mesonbuild/interpreterbase/__init__.py index 473379307..88fa706b9 100644 --- a/mesonbuild/interpreterbase/__init__.py +++ b/mesonbuild/interpreterbase/__init__.py @@ -61,6 +61,7 @@ __all__ = [ 'HoldableTypes', 'UnknownValue', + 'UndefinedVariable', ] from .baseobjects import ( @@ -85,6 +86,7 @@ from .baseobjects import ( HoldableTypes, UnknownValue, + UndefinedVariable, ) from .decorators import ( diff --git a/mesonbuild/interpreterbase/baseobjects.py b/mesonbuild/interpreterbase/baseobjects.py index 7cda5729f..5ad6f4351 100644 --- a/mesonbuild/interpreterbase/baseobjects.py +++ b/mesonbuild/interpreterbase/baseobjects.py @@ -127,6 +127,10 @@ class UnknownValue(MesonInterpreterObject): limitations in our code or because the value differs from machine to machine.''' +class UndefinedVariable(MesonInterpreterObject): + '''This class is only used for the rewriter/static introspection tool and + represents the `value` a meson-variable has if it was never written to.''' + HoldableTypes = (HoldableObject, int, bool, str, list, dict) TYPE_HoldableTypes = T.Union[TYPE_var, HoldableObject] InterpreterObjectTypeVar = T.TypeVar('InterpreterObjectTypeVar', bound=TYPE_HoldableTypes) diff --git a/test cases/rewrite/7 tricky dataflow/addSrc.json b/test cases/rewrite/7 tricky dataflow/addSrc.json index 1b6aa485b..17e42926f 100644 --- a/test cases/rewrite/7 tricky dataflow/addSrc.json +++ b/test cases/rewrite/7 tricky dataflow/addSrc.json @@ -68,5 +68,10 @@ "type": "target", "target": "tgt6", "operation": "info" + }, + { + "type": "target", + "target": "tgt7", + "operation": "info" } ] diff --git a/test cases/rewrite/7 tricky dataflow/info.json b/test cases/rewrite/7 tricky dataflow/info.json index 5fc65dd8c..8d4ac553a 100644 --- a/test cases/rewrite/7 tricky dataflow/info.json +++ b/test cases/rewrite/7 tricky dataflow/info.json @@ -28,5 +28,10 @@ "type": "target", "target": "tgt6", "operation": "info" + }, + { + "type": "target", + "target": "tgt7", + "operation": "info" } ] diff --git a/test cases/rewrite/7 tricky dataflow/meson.build b/test cases/rewrite/7 tricky dataflow/meson.build index 4c183ed97..ab572ea59 100644 --- a/test cases/rewrite/7 tricky dataflow/meson.build +++ b/test cases/rewrite/7 tricky dataflow/meson.build @@ -33,3 +33,9 @@ dont_add_here_6 = ['foo.c'] gen = generator(find_program('cp'), output: '@BASENAME@_copy.c', arguments: ['@INPUT@', '@OUTPUT@']) generated = gen.process(dont_add_here_6) executable('tgt6', generated) + +if false + # Should produce a warning, but should not crash + var = not_defined_1 + executable('tgt7', not_defined_2, var) +endif diff --git a/test cases/unit/120 rewrite/meson.build b/test cases/unit/120 rewrite/meson.build index 545bb0fde..654b09d16 100644 --- a/test cases/unit/120 rewrite/meson.build +++ b/test cases/unit/120 rewrite/meson.build @@ -190,5 +190,11 @@ if a \ debug('help!') endif +if false + # Should produce a warning, but should not crash + foo = not_defined + message(not_defined) +endif + # End of file comment with no linebreak
\ No newline at end of file diff --git a/unittests/rewritetests.py b/unittests/rewritetests.py index 75405efc8..767c29187 100644 --- a/unittests/rewritetests.py +++ b/unittests/rewritetests.py @@ -439,6 +439,7 @@ class RewriterTests(BasePlatformTests): 'tgt4@exe': {'name': 'tgt4', 'sources': ['unknown'], 'extra_files': []}, 'tgt5@exe': {'name': 'tgt5', 'sources': ['unknown', 'new.c'], 'extra_files': []}, 'tgt6@exe': {'name': 'tgt6', 'sources': ['unknown', 'new.c'], 'extra_files': []}, + 'tgt7@exe': {'name': 'tgt7', 'sources': ['unknown', 'unknown'], 'extra_files': []}, } } self.assertEqualIgnoreOrder(out, expected) |
