mirror of
https://github.com/mesonbuild/meson.git
synced 2026-11-06 05:16:42 +00:00
interpreterbase: warn on redundant Meson version checks
We might encounter a Meson version check that always evaluates to true
or to false:
project('t', 'c', meson_version: '>=0.60.0')
if meson.version().version_compare('>=0.55.0')
v = 1
endif
if meson.version().version_compare('<0.60.0')
v = 2
endif
Print warnings in such cases.
Co-authored-by: Benjamin Gilbert <bgilbert@backtick.net>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
1 parent
0bfffde31b
commit
b108299627
5 files changed
+112
-8
No files matched your search
@@ -311,15 +311,19 @@ class InterpreterBase:
|
||||
res = result.operator_call(MesonOperator.BOOL, None)
|
||||
if not isinstance(res, bool):
|
||||
raise InvalidCode(f'If clause {result!r} does not evaluate to true or false.')
|
||||
if res:
|
||||
prev_meson_version = mesonlib.project_meson_versions[self.subproject]
|
||||
if self.tmp_meson_version:
|
||||
mesonlib.project_meson_versions[self.subproject] = self.tmp_meson_version
|
||||
try:
|
||||
prev_meson_version = mesonlib.project_meson_versions[self.subproject]
|
||||
if self.tmp_meson_version and isinstance(prev_meson_version, mesonlib.Range):
|
||||
always = prev_meson_version.always(self.tmp_meson_version)
|
||||
if always is not None:
|
||||
mlog.warning(f"Conditional on version '{self.tmp_meson_version}' always evaluates to {str(always).lower()}",
|
||||
location=self.current_node)
|
||||
mesonlib.project_meson_versions[self.subproject] = prev_meson_version.intersect(self.tmp_meson_version)
|
||||
try:
|
||||
if res:
|
||||
self.evaluate_codeblock(i.block)
|
||||
finally:
|
||||
mesonlib.project_meson_versions[self.subproject] = prev_meson_version
|
||||
return None
|
||||
return None
|
||||
finally:
|
||||
mesonlib.project_meson_versions[self.subproject] = prev_meson_version
|
||||
if not isinstance(node.elseblock, mparser.EmptyNode):
|
||||
self.evaluate_codeblock(node.elseblock.block)
|
||||
return None
|
||||
|
||||
@@ -1095,6 +1095,18 @@ class Range(T.Generic[_V]):
|
||||
result.__post_init__()
|
||||
return result
|
||||
|
||||
def always(self, inner: Range[_V]) -> T.Optional[bool]:
|
||||
"""Check if inner is always true or always false given self.
|
||||
|
||||
Returns True if inner is always satisfied by any value in self,
|
||||
False if no value in self satisfies inner, None if indeterminate."""
|
||||
narrowed = self.intersect(inner)
|
||||
if narrowed.is_empty:
|
||||
return False
|
||||
if narrowed == self:
|
||||
return True
|
||||
return None
|
||||
|
||||
|
||||
# note that Range is immutable, so no need to have Range() | None
|
||||
def version_check_to_range(checks: T.List[str], start: Range[Version] = Range()) -> Range[Version]:
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
# written by Benjamin Gilbert <bgilbert@backtick.net>
|
||||
project('t', 'c', meson_version: '>=0.60.0')
|
||||
if meson.version().version_compare('>=0.55.0')
|
||||
v = 1
|
||||
endif
|
||||
if meson.version().version_compare('<0.60.0')
|
||||
v = 2
|
||||
endif
|
||||
# narrowing: <0.60 is indeterminate, but >=0.40 is always true within >=0.50, <0.60
|
||||
if meson.version().version_compare('<99.0')
|
||||
if meson.version().version_compare('>=0.40')
|
||||
v = 3
|
||||
endif
|
||||
endif
|
||||
|
||||
# <= vs <, should not warn
|
||||
if meson.version().version_compare('<=99.0')
|
||||
if meson.version().version_compare('<99.0')
|
||||
v = 4
|
||||
endif
|
||||
endif
|
||||
|
||||
# hide warnings about multiple args to version_compare()
|
||||
if meson.version().version_compare('>1.10.0')
|
||||
if meson.version().version_compare('<99.0')
|
||||
if meson.version().version_compare('<99.0', '>=0.40')
|
||||
v = 5
|
||||
endif
|
||||
if meson.version().version_compare('>=0.40', '<99.0')
|
||||
v = 6
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"stdout": [
|
||||
{
|
||||
"line": "test cases/common/295 redundant version check/meson.build:3: WARNING: Conditional on version '>= 0.55.0' always evaluates to true"
|
||||
},
|
||||
{
|
||||
"line": "test cases/common/295 redundant version check/meson.build:6: WARNING: Conditional on version '< 0.60.0' always evaluates to false"
|
||||
},
|
||||
{
|
||||
"line": "test cases/common/295 redundant version check/meson.build:11: WARNING: Conditional on version '>= 0.40' always evaluates to true"
|
||||
},
|
||||
{
|
||||
"line": "test cases/common/295 redundant version check/meson.build:26: WARNING: Conditional on version '>= 0.40, < 99.0' always evaluates to true"
|
||||
},
|
||||
{
|
||||
"line": "test cases/common/295 redundant version check/meson.build:29: WARNING: Conditional on version '>= 0.40, < 99.0' always evaluates to true"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -292,6 +292,42 @@ class VersionComparisonTests(unittest.TestCase):
|
||||
r = a.intersect(b)
|
||||
self.assertTrue(r.is_empty)
|
||||
|
||||
def test_range_always(self):
|
||||
"""Range.always checks if inner is always true/false given outer."""
|
||||
outer = Range(min=5, min_eq=True, max=10, max_eq=True)
|
||||
unbounded = Range()
|
||||
empty = Range(min=7, max=3, min_eq=True, max_eq=True)
|
||||
|
||||
# inner fully contains outer => true
|
||||
self.assertTrue(outer.always(Range(min=3, min_eq=True)))
|
||||
self.assertTrue(outer.always(Range(max=12, max_eq=True)))
|
||||
self.assertTrue(outer.always(unbounded))
|
||||
|
||||
# inner same as outer => true
|
||||
self.assertTrue(outer.always(Range(min=5, min_eq=True, max=10, max_eq=True)))
|
||||
|
||||
# inner doesn't overlap outer => false
|
||||
self.assertFalse(outer.always(Range(min=11, min_eq=True)))
|
||||
self.assertFalse(outer.always(Range(max=4, max_eq=True)))
|
||||
|
||||
# inner partially overlaps => indeterminate
|
||||
self.assertIsNone(outer.always(Range(min=7, min_eq=True)))
|
||||
self.assertIsNone(outer.always(Range(max=8, max_eq=True)))
|
||||
|
||||
# exclusive boundaries count as a partial overlap
|
||||
self.assertIsNone(outer.always(Range(min=5, min_eq=False)))
|
||||
self.assertIsNone(outer.always(Range(max=10, max_eq=False)))
|
||||
|
||||
# outer is unbounded
|
||||
self.assertIsNone(unbounded.always(Range(min=5, min_eq=True)))
|
||||
self.assertTrue(unbounded.always(unbounded))
|
||||
self.assertFalse(unbounded.always(empty))
|
||||
|
||||
# outer is empty => always false (intersection is empty)
|
||||
self.assertFalse(empty.always(Range(min=5, min_eq=True)))
|
||||
self.assertFalse(empty.always(unbounded))
|
||||
self.assertFalse(empty.always(empty))
|
||||
|
||||
def test_range_str(self):
|
||||
"""Test Range.__str__."""
|
||||
self.assertEqual(str(Range()), '(any)')
|
||||
|
||||
Reference in new issue
Block a user