diff --git a/src/cfengine_cli/lint.py b/src/cfengine_cli/lint.py index 50165dc..0355603 100644 --- a/src/cfengine_cli/lint.py +++ b/src/cfengine_cli/lint.py @@ -634,6 +634,47 @@ def _lint_node( f"Error: '{name}' is not a defined body. Only bodies may be called with '{state.attribute_name}' {location}" ) return 1 + if node.type == "attribute_name" and state.promise_type and state.attribute_name: + promise_type_data = syntax_data.BUILTIN_PROMISE_TYPES.get( + state.promise_type, {} + ) + promise_type_attrs = promise_type_data.get("attributes", {}) + if state.attribute_name not in promise_type_attrs: + _highlight_range(node, lines) + print( + f"Error: Invalid attribute '{state.attribute_name}' for promise type '{state.promise_type}' {location}" + ) + return 1 + if ( + state.block_keyword == "promise" + and node.type == "attribute_name" + and state.attribute_name + not in ( + None, + "path", + "interpreter", + ) + ): + _highlight_range(node, lines) + print( + f"Error: Invalid attribute name '{state.attribute_name}' in '{state.block_name}' custom promise type definition {location}" + ) + return 1 + if node.type == "call": + call, _, *args, _ = node.children # f ( a1 , a2 , a..N ) + call = _text(call) + args = list(filter(",".__ne__, iter(_text(x) for x in args))) + + if call in syntax_data.BUILTIN_FUNCTIONS: + variadic = syntax_data.BUILTIN_FUNCTIONS.get(call, {}).get("variadic", True) + params = syntax_data.BUILTIN_FUNCTIONS.get(call, {}).get("parameters", {}) + if not variadic and (len(params) != len(args)): + _highlight_range(node, lines) + print( + f"Error: Expected {len(params)} arguments, received {len(args)} {location}" + ) + return 1 + # TODO: Handle variadic functions with varying number of required arguments (0-N, 1-N, 2-N and so on) return 0 diff --git a/tests/lint/012_invalid_attributes.expected.txt b/tests/lint/012_invalid_attributes.expected.txt new file mode 100644 index 0000000..b54202f --- /dev/null +++ b/tests/lint/012_invalid_attributes.expected.txt @@ -0,0 +1,12 @@ + + path => "/var/cfengine/inputs/modules/promises/git.py"; + blah => "something"; + ^--^ +Error: Invalid attribute name 'blah' in 'git' custom promise type definition at tests/lint/012_invalid_attributes.x.cf:5:3 + + slist => {}, + bar => ""; + ^-^ +Error: Invalid attribute 'bar' for promise type 'vars' at tests/lint/012_invalid_attributes.x.cf:12:7 +FAIL: tests/lint/012_invalid_attributes.x.cf (2 errors) +Failure, 2 errors in total. diff --git a/tests/lint/012_invalid_attributes.x.cf b/tests/lint/012_invalid_attributes.x.cf new file mode 100644 index 0000000..90faa66 --- /dev/null +++ b/tests/lint/012_invalid_attributes.x.cf @@ -0,0 +1,13 @@ +promise agent git +{ + interpreter => "/usr/bin/python3"; + path => "/var/cfengine/inputs/modules/promises/git.py"; + blah => "something"; +} +bundle agent foo +{ + vars: + "error" + slist => {}, + bar => ""; +} diff --git a/tests/lint/013_function_call_arg_count.cf b/tests/lint/013_function_call_arg_count.cf new file mode 100644 index 0000000..b6b7593 --- /dev/null +++ b/tests/lint/013_function_call_arg_count.cf @@ -0,0 +1,8 @@ +bundle agent main +{ +vars: + "string" + string => canonify("test"); +reports: + "Test => $(string)"; +} diff --git a/tests/lint/013_function_call_arg_count.expected.txt b/tests/lint/013_function_call_arg_count.expected.txt new file mode 100644 index 0000000..1fe0635 --- /dev/null +++ b/tests/lint/013_function_call_arg_count.expected.txt @@ -0,0 +1,12 @@ + + "string1" + string => canonify(); + ^--------^ +Error: Expected 1 arguments, received 0 at tests/lint/013_function_call_arg_count.x.cf:5:15 + + "string3" + string => canonify("test", "test"); + ^----------------------^ +Error: Expected 1 arguments, received 2 at tests/lint/013_function_call_arg_count.x.cf:9:15 +FAIL: tests/lint/013_function_call_arg_count.x.cf (2 errors) +Failure, 2 errors in total. diff --git a/tests/lint/013_function_call_arg_count.x.cf b/tests/lint/013_function_call_arg_count.x.cf new file mode 100644 index 0000000..ac4fb6d --- /dev/null +++ b/tests/lint/013_function_call_arg_count.x.cf @@ -0,0 +1,14 @@ +bundle agent main +{ +vars: + "string1" + string => canonify(); + "string2" + string => canonify("test"); + "string3" + string => canonify("test", "test"); +reports: + "Test1 => $(string1)"; + "Test2 => $(string2)"; + "Test3 => $(string3)"; +}