Warn if non-permitted keyword arguments are given to compiler methods

This can help future generations avoid mistakes like this:
edb1c66239

To avoid breaking builds, this is currently just an error. After
sufficient time has passed this can hopefully become a hard error,
similarly to the already-existing `permittedKwargs` warnings.
pull/3127/head
Ran Benita 7 years ago
parent 6ec401af4b
commit 4d8e4654cb
  1. 107
      mesonbuild/interpreter.py
  2. 16
      mesonbuild/interpreterbase.py
  3. 10
      run_unittests.py
  4. 5
      test cases/unit/23 non-permitted kwargs/meson.build

@ -25,7 +25,7 @@ from .mesonlib import FileMode, Popen_safe, listify, extract_as_list
from .dependencies import ExternalProgram
from .dependencies import InternalDependency, Dependency, DependencyException
from .interpreterbase import InterpreterBase
from .interpreterbase import check_stringlist, noPosargs, noKwargs, stringArgs, permittedKwargs
from .interpreterbase import check_stringlist, noPosargs, noKwargs, stringArgs, permittedKwargs, permittedMethodKwargs
from .interpreterbase import InterpreterException, InvalidArguments, InvalidCode
from .interpreterbase import InterpreterObject, MutableInterpreterObject, Disabler
from .modules import ModuleReturnValue
@ -718,9 +718,11 @@ class CompilerHolder(InterpreterObject):
'symbols_have_underscore_prefix': self.symbols_have_underscore_prefix_method,
})
@permittedMethodKwargs({})
def version_method(self, args, kwargs):
return self.compiler.version
@permittedMethodKwargs({})
def cmd_array_method(self, args, kwargs):
return self.compiler.exelist
@ -760,6 +762,11 @@ class CompilerHolder(InterpreterObject):
deps = final_deps
return deps
@permittedMethodKwargs({
'prefix',
'args',
'dependencies',
})
def alignment_method(self, args, kwargs):
if len(args) != 1:
raise InterpreterException('Alignment method takes exactly one positional argument.')
@ -774,6 +781,13 @@ class CompilerHolder(InterpreterObject):
mlog.log('Checking for alignment of "', mlog.bold(typename), '": ', result, sep='')
return result
@permittedMethodKwargs({
'name',
'no_builtin_args',
'include_directories',
'args',
'dependencies',
})
def run_method(self, args, kwargs):
if len(args) != 1:
raise InterpreterException('Run method takes exactly one positional argument.')
@ -799,9 +813,11 @@ class CompilerHolder(InterpreterObject):
mlog.log('Checking if "', mlog.bold(testname), '" runs: ', h, sep='')
return TryRunResultHolder(result)
@permittedMethodKwargs({})
def get_id_method(self, args, kwargs):
return self.compiler.get_id()
@permittedMethodKwargs({})
def symbols_have_underscore_prefix_method(self, args, kwargs):
'''
Check if the compiler prefixes _ (underscore) to global C symbols
@ -809,6 +825,7 @@ class CompilerHolder(InterpreterObject):
'''
return self.compiler.symbols_have_underscore_prefix(self.environment)
@permittedMethodKwargs({})
def unittest_args_method(self, args, kwargs):
'''
This function is deprecated and should not be used.
@ -818,6 +835,13 @@ class CompilerHolder(InterpreterObject):
raise InterpreterException('This {} compiler has no feature arguments.'.format(self.compiler.get_display_language()))
return self.compiler.get_feature_args({'unittest': 'true'})
@permittedMethodKwargs({
'prefix',
'no_builtin_args',
'include_directories',
'args',
'dependencies',
})
def has_member_method(self, args, kwargs):
if len(args) != 2:
raise InterpreterException('Has_member takes exactly two arguments.')
@ -839,6 +863,13 @@ class CompilerHolder(InterpreterObject):
'" has member "', mlog.bold(membername), '": ', hadtxt, sep='')
return had
@permittedMethodKwargs({
'prefix',
'no_builtin_args',
'include_directories',
'args',
'dependencies',
})
def has_members_method(self, args, kwargs):
check_stringlist(args)
typename = args[0]
@ -859,6 +890,13 @@ class CompilerHolder(InterpreterObject):
'" has members ', members, ': ', hadtxt, sep='')
return had
@permittedMethodKwargs({
'prefix',
'no_builtin_args',
'include_directories',
'args',
'dependencies',
})
def has_function_method(self, args, kwargs):
if len(args) != 1:
raise InterpreterException('Has_function takes exactly one argument.')
@ -877,6 +915,13 @@ class CompilerHolder(InterpreterObject):
mlog.log('Checking for function "', mlog.bold(funcname), '": ', hadtxt, sep='')
return had
@permittedMethodKwargs({
'prefix',
'no_builtin_args',
'include_directories',
'args',
'dependencies',
})
def has_type_method(self, args, kwargs):
if len(args) != 1:
raise InterpreterException('Has_type takes exactly one argument.')
@ -895,6 +940,16 @@ class CompilerHolder(InterpreterObject):
mlog.log('Checking for type "', mlog.bold(typename), '": ', hadtxt, sep='')
return had
@permittedMethodKwargs({
'prefix',
'low',
'high',
'guess',
'no_builtin_args',
'include_directories',
'args',
'dependencies',
})
def compute_int_method(self, args, kwargs):
if len(args) != 1:
raise InterpreterException('Compute_int takes exactly one argument.')
@ -918,6 +973,13 @@ class CompilerHolder(InterpreterObject):
mlog.log('Computing int of "%s": %d' % (expression, res))
return res
@permittedMethodKwargs({
'prefix',
'no_builtin_args',
'include_directories',
'args',
'dependencies',
})
def sizeof_method(self, args, kwargs):
if len(args) != 1:
raise InterpreterException('Sizeof takes exactly one argument.')
@ -932,6 +994,13 @@ class CompilerHolder(InterpreterObject):
mlog.log('Checking for size of "%s": %d' % (element, esize))
return esize
@permittedMethodKwargs({
'prefix',
'no_builtin_args',
'include_directories',
'args',
'dependencies',
})
def get_define_method(self, args, kwargs):
if len(args) != 1:
raise InterpreterException('get_define() takes exactly one argument.')
@ -946,6 +1015,13 @@ class CompilerHolder(InterpreterObject):
mlog.log('Fetching value of define "%s": %s' % (element, value))
return value
@permittedMethodKwargs({
'name',
'no_builtin_args',
'include_directories',
'args',
'dependencies',
})
def compiles_method(self, args, kwargs):
if len(args) != 1:
raise InterpreterException('compiles method takes exactly one argument.')
@ -969,6 +1045,13 @@ class CompilerHolder(InterpreterObject):
mlog.log('Checking if "', mlog.bold(testname), '" compiles: ', h, sep='')
return result
@permittedMethodKwargs({
'name',
'no_builtin_args',
'include_directories',
'args',
'dependencies',
})
def links_method(self, args, kwargs):
if len(args) != 1:
raise InterpreterException('links method takes exactly one argument.')
@ -992,6 +1075,13 @@ class CompilerHolder(InterpreterObject):
mlog.log('Checking if "', mlog.bold(testname), '" links: ', h, sep='')
return result
@permittedMethodKwargs({
'prefix',
'no_builtin_args',
'include_directories',
'args',
'dependencies',
})
def has_header_method(self, args, kwargs):
if len(args) != 1:
raise InterpreterException('has_header method takes exactly one argument.')
@ -1010,6 +1100,13 @@ class CompilerHolder(InterpreterObject):
mlog.log('Has header "%s":' % hname, h)
return haz
@permittedMethodKwargs({
'prefix',
'no_builtin_args',
'include_directories',
'args',
'dependencies',
})
def has_header_symbol_method(self, args, kwargs):
if len(args) != 2:
raise InterpreterException('has_header_symbol method takes exactly two arguments.')
@ -1029,6 +1126,10 @@ class CompilerHolder(InterpreterObject):
mlog.log('Header <{0}> has symbol "{1}":'.format(hname, symbol), h)
return haz
@permittedMethodKwargs({
'required',
'dirs',
})
def find_library_method(self, args, kwargs):
# TODO add dependencies support?
if len(args) != 1:
@ -1050,6 +1151,7 @@ class CompilerHolder(InterpreterObject):
self.compiler.language)
return ExternalLibraryHolder(lib)
@permittedMethodKwargs({})
def has_argument_method(self, args, kwargs):
args = mesonlib.stringlistify(args)
if len(args) != 1:
@ -1062,6 +1164,7 @@ class CompilerHolder(InterpreterObject):
mlog.log('Compiler for {} supports argument {}:'.format(self.compiler.get_display_language(), args[0]), h)
return result
@permittedMethodKwargs({})
def has_multi_arguments_method(self, args, kwargs):
args = mesonlib.stringlistify(args)
result = self.compiler.has_multi_arguments(args, self.environment)
@ -1075,6 +1178,7 @@ class CompilerHolder(InterpreterObject):
h)
return result
@permittedMethodKwargs({})
def get_supported_arguments_method(self, args, kwargs):
args = mesonlib.stringlistify(args)
result = self.compiler.get_supported_arguments(args, self.environment)
@ -1090,6 +1194,7 @@ class CompilerHolder(InterpreterObject):
h)
return result
@permittedMethodKwargs({})
def first_supported_argument_method(self, args, kwargs):
for i in mesonlib.stringlistify(args):
if self.compiler.has_argument(i, self.environment):

@ -80,6 +80,22 @@ class permittedKwargs:
return wrapped
class permittedMethodKwargs:
def __init__(self, permitted):
self.permitted = permitted
def __call__(self, f):
@wraps(f)
def wrapped(obj, args, kwargs):
for k in kwargs:
if k not in self.permitted:
mlog.warning('''Passed invalid keyword argument "{}".'''.format(k))
mlog.warning('This will become a hard error in the future.')
return f(obj, args, kwargs)
return wrapped
class InterpreterException(mesonlib.MesonException):
pass

@ -1735,6 +1735,16 @@ int main(int argc, char **argv) {
]:
self.assertRegex(out, re.escape(expected))
def test_permitted_method_kwargs(self):
tdir = os.path.join(self.unit_test_dir, '23 non-permitted kwargs')
out = self.init(tdir)
for expected in [
r'WARNING: Passed invalid keyword argument "prefixxx".',
r'WARNING: Passed invalid keyword argument "argsxx".',
r'WARNING: Passed invalid keyword argument "invalidxx".',
]:
self.assertRegex(out, re.escape(expected))
def test_templates(self):
ninja = detect_ninja()
if ninja is None:

@ -0,0 +1,5 @@
project('non-permitted kwargs', 'c')
cc = meson.get_compiler('c')
cc.has_header_symbol('stdio.h', 'printf', prefixxx: '#define XXX')
cc.links('int main(){}', argsxx: '')
cc.get_id(invalidxx: '')
Loading…
Cancel
Save