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

@ -80,6 +80,22 @@ class permittedKwargs:
return wrapped 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): class InterpreterException(mesonlib.MesonException):
pass pass

@ -1735,6 +1735,16 @@ int main(int argc, char **argv) {
]: ]:
self.assertRegex(out, re.escape(expected)) 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): def test_templates(self):
ninja = detect_ninja() ninja = detect_ninja()
if ninja is None: 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