@@ -291,34 +291,41 @@ def end_file(self) -> None:
291291 self .walking = False
292292 self .policy_file = None
293293
294- def add_bundle (self , name : str ) -> None :
295- """This is called during discovery wherever a bundle is defined.
296-
297- For example:
298- bundle agent my_bundle {}
294+ def _add_definition (self , name : str , node : Node , definitions : dict ) -> None :
295+ """Add a definition (bundle or body) to the given dictionary.
299296
297+ The value for each qualified name is a list of definitions, since
298+ the same name can appear multiple times (e.g. inside macro if/else
299+ branches). Each definition records the file, line, and parameter
300+ list.
300301 """
301302 name = _qualify (name , self .namespace )
302- # TODO: In the future we will record more information than True, like:
303- # - Can be a list / dict of all places a bundle with that
304- # qualified name is defined in cases there are duplicates.
305- # - Can record the location of each definition
306- # - Can record the parameters / signature
307- # - Can record whether the bundle is inside a macro
308- # - Can have a list of classes and vars defined inside
309- self .bundles [name ] = {"is_defined" : True }
310-
311- def add_body (self , name : str ) -> None :
312- """This is called during discovery wherever a body is defined.
303+ assert self .policy_file
304+ n = node .next_named_sibling
305+ if n and n .type == "parameter_list" :
306+ _ , * args , _ = n .children
307+ parameters = list (filter ("," .__ne__ , iter (_text (x ) for x in args )))
308+ else :
309+ parameters = []
310+ definition = {
311+ "filename" : self .policy_file .filename ,
312+ "line" : node .range .start_point [0 ] + 1 ,
313+ "parameters" : parameters ,
314+ }
315+ if name not in definitions :
316+ definitions [name ] = []
317+ definitions [name ].append (definition )
313318
314- For example:
315- body contain my_body {}
319+ def add_bundle (self , name : str , node : Node ) -> None :
320+ """This is called during discovery wherever a bundle is defined."""
321+ self ._add_definition (name , node , self .bundles )
316322
317- Control bundles are a special case, so would not be called for:
318- body file control {}
323+ def add_body (self , name : str , node : Node ) -> None :
324+ """This is called during discovery wherever a body is defined.
325+
326+ Control bodies are a special case and should not be passed here.
319327 """
320- name = _qualify (name , self .namespace )
321- self .bodies [name ] = {"is_defined" : True }
328+ self ._add_definition (name , node , self .bodies )
322329
323330 def add_promise_type (self , name : str ) -> None :
324331 """This is called during discovery wherever a custom promise type is
@@ -479,28 +486,19 @@ def _discover_node(node: Node, state: State) -> int:
479486 name = _text (node )
480487 if name == "control" :
481488 return 0 # No need to define control blocks
482- state .add_body (name )
483- qualified_name = _qualify (name , state .namespace )
484- if (n := node .next_named_sibling ) and n .type == "parameter_list" :
485- _ , * args , _ = n .children
486- args = list (filter ("," .__ne__ , iter (_text (x ) for x in args )))
487- state .bodies [qualified_name ].update ({"parameters" : args })
489+ state .add_body (name , node )
488490 return 0
489491
490492 # Define bundles:
491493 if node .type == "bundle_block_name" :
492494 name = _text (node )
493- qualified_name = _qualify (name , state .namespace )
494- state .add_bundle (name )
495- if (n := node .next_named_sibling ) and n .type == "parameter_list" :
496- _ , * args , _ = n .children
497- args = list (filter ("," .__ne__ , iter (_text (x ) for x in args )))
498- state .bundles [qualified_name ].update ({"parameters" : args })
495+ state .add_bundle (name , node )
499496 return 0
500497
501498 # Define custom promise types:
502499 if node .type == "promise_block_name" :
503- state .add_promise_type (_text (node ))
500+ name = _text (node )
501+ state .add_promise_type (name )
504502 return 0
505503
506504 return 0
@@ -727,19 +725,25 @@ def _lint_node(
727725
728726 qualified_name = _qualify (call , state .namespace )
729727 if qualified_name in state .bundles :
730- max_args = len (state .bundles [qualified_name ].get ("parameters" , []))
731- if max_args != len (args ):
728+ definitions = state .bundles [qualified_name ]
729+ valid_counts = {len (d .get ("parameters" , [])) for d in definitions }
730+ if len (args ) not in valid_counts :
732731 _highlight_range (node , lines )
732+ counts = sorted (valid_counts )
733+ expected = " or " .join (str (c ) for c in counts )
733734 print (
734- f"Error: Expected { max_args } arguments, received { len (args )} for bundle '{ call } ' { location } "
735+ f"Error: Expected { expected } arguments, received { len (args )} for bundle '{ call } ' { location } "
735736 )
736737 return 1
737738 if qualified_name in state .bodies :
738- max_args = len (state .bodies [qualified_name ].get ("parameters" , []))
739- if max_args != len (args ):
739+ definitions = state .bodies [qualified_name ]
740+ valid_counts = {len (d .get ("parameters" , [])) for d in definitions }
741+ if len (args ) not in valid_counts :
740742 _highlight_range (node , lines )
743+ counts = sorted (valid_counts )
744+ expected = " or " .join (str (c ) for c in counts )
741745 print (
742- f"Error: Expected { max_args } arguments, received { len (args )} for body '{ call } ' { location } "
746+ f"Error: Expected { expected } arguments, received { len (args )} for body '{ call } ' { location } "
743747 )
744748 return 1
745749
0 commit comments