diff --git a/src/cfengine_cli/lint.py b/src/cfengine_cli/lint.py index 0355603..f5cfe25 100644 --- a/src/cfengine_cli/lint.py +++ b/src/cfengine_cli/lint.py @@ -287,7 +287,7 @@ def add_bundle(self, name: str) -> None: # - Can record the parameters / signature # - Can record whether the bundle is inside a macro # - Can have a list of classes and vars defined inside - self.bundles[name] = True + self.bundles[name] = {"is_defined": True} def add_body(self, name: str) -> None: """This is called during discovery wherever a body is defined. @@ -299,7 +299,7 @@ def add_body(self, name: str) -> None: body file control {} """ name = _qualify(name, self.namespace) - self.bodies[name] = True + self.bodies[name] = {"is_defined": True} def add_promise_type(self, name: str) -> None: """This is called during discovery wherever a custom promise type is @@ -461,12 +461,22 @@ def _discover_node(node: Node, state: State) -> int: if name == "control": return 0 # No need to define control blocks state.add_body(name) + qualified_name = _qualify(name, state.namespace) + if (n := node.next_named_sibling) and n.type == "parameter_list": + _, *args, _ = n.children + args = list(filter(",".__ne__, iter(_text(x) for x in args))) + state.bodies[qualified_name].update({"parameters": args}) return 0 # Define bundles: if node.type == "bundle_block_name": name = _text(node) + qualified_name = _qualify(name, state.namespace) state.add_bundle(name) + if (n := node.next_named_sibling) and n.type == "parameter_list": + _, *args, _ = n.children + args = list(filter(",".__ne__, iter(_text(x) for x in args))) + state.bundles[qualified_name].update({"parameters": args}) return 0 # Define custom promise types: @@ -661,8 +671,12 @@ def _lint_node( ) return 1 if node.type == "call": + known_faulty_defs = {"regex_replace"} call, _, *args, _ = node.children # f ( a1 , a2 , a..N ) call = _text(call) + if call in known_faulty_defs: + return 0 + args = list(filter(",".__ne__, iter(_text(x) for x in args))) if call in syntax_data.BUILTIN_FUNCTIONS: @@ -671,10 +685,28 @@ def _lint_node( if not variadic and (len(params) != len(args)): _highlight_range(node, lines) print( - f"Error: Expected {len(params)} arguments, received {len(args)} {location}" + f"Error: Expected {len(params)} arguments, received {len(args)} for function '{call}'{location}" ) return 1 # TODO: Handle variadic functions with varying number of required arguments (0-N, 1-N, 2-N and so on) + qualified_name = _qualify(call, state.namespace) + if qualified_name in state.bundles: + params = state.bundles[qualified_name].get("parameters", []) + if len(params) != len(args): + _highlight_range(node, lines) + print( + f"Error: Expected {len(params)} arguments, received {len(args)} for bundle '{call}' {location}" + ) + return 1 + if qualified_name in state.bodies: + params = state.bodies[qualified_name].get("parameters", []) + if len(params) != len(args): + _highlight_range(node, lines) + print( + f"Error: Expected {len(params)} arguments, received {len(args)} for body '{call}'{location}" + ) + return 1 + return 0 diff --git a/tests/lint/013_function_call_arg_count.expected.txt b/tests/lint/013_function_call_arg_count.expected.txt index 1fe0635..da38cf7 100644 --- a/tests/lint/013_function_call_arg_count.expected.txt +++ b/tests/lint/013_function_call_arg_count.expected.txt @@ -2,11 +2,11 @@ "string1" string => canonify(); ^--------^ -Error: Expected 1 arguments, received 0 at tests/lint/013_function_call_arg_count.x.cf:5:15 +Error: Expected 1 arguments, received 0 for function 'canonify'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 +Error: Expected 1 arguments, received 2 for function 'canonify'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/014_num_args_bundle_body.cf b/tests/lint/014_num_args_bundle_body.cf new file mode 100644 index 0000000..9be08c9 --- /dev/null +++ b/tests/lint/014_num_args_bundle_body.cf @@ -0,0 +1,11 @@ +bundle agent main +{ + methods: + "test" + usebundle => test("a", "b"); +} +bundle agent test(a, b) +{ + reports: + "$(a) to $(b)"; +} diff --git a/tests/lint/014_num_args_bundle_body.expected.txt b/tests/lint/014_num_args_bundle_body.expected.txt new file mode 100644 index 0000000..e0bcc7d --- /dev/null +++ b/tests/lint/014_num_args_bundle_body.expected.txt @@ -0,0 +1,17 @@ + + "test1" + usebundle => test(); + ^----^ +Error: Expected 2 arguments, received 0 for bundle 'test' at tests/lint/014_num_args_bundle_body.x.cf:5:20 + + "test2" + usebundle => test("a"); + ^-------^ +Error: Expected 2 arguments, received 1 for bundle 'test' at tests/lint/014_num_args_bundle_body.x.cf:7:20 + + "test4" + usebundle => test("a", "b", "c"); + ^-----------------^ +Error: Expected 2 arguments, received 3 for bundle 'test' at tests/lint/014_num_args_bundle_body.x.cf:11:20 +FAIL: tests/lint/014_num_args_bundle_body.x.cf (3 errors) +Failure, 3 errors in total. diff --git a/tests/lint/014_num_args_bundle_body.x.cf b/tests/lint/014_num_args_bundle_body.x.cf new file mode 100644 index 0000000..c1cd0fa --- /dev/null +++ b/tests/lint/014_num_args_bundle_body.x.cf @@ -0,0 +1,17 @@ +bundle agent main +{ + methods: + "test1" + usebundle => test(); + "test2" + usebundle => test("a"); + "test3" + usebundle => test("a", "b"); + "test4" + usebundle => test("a", "b", "c"); +} +bundle agent test(a, b) +{ + reports: + "$(a) to $(b)"; +}