@@ -109,9 +109,13 @@ def parse_variable_usages(script_text: str) -> Set[str]:
109109 return {name for name in candidates if name and name not in SPECIAL_VARS }
110110
111111
112- def parse_positional_usages (script_text : str ) -> Tuple [Set [int ], bool ]:
112+ def parse_positional_usages (script_text : str , exclude_function_params : Optional [ Set [ str ]] = None ) -> Tuple [Set [int ], bool ]:
113113 """Extract positional parameter indices and varargs usage from script.
114114
115+ Args:
116+ script_text: The script content
117+ exclude_function_params: Set of function parameter indices to exclude (e.g., {'1', '2'})
118+
115119 Returns:
116120 Tuple of (positional_indices, varargs_present)
117121 """
@@ -121,9 +125,34 @@ def parse_positional_usages(script_text: str) -> Tuple[Set[int], bool]:
121125 indices = {int (m ) for m in digit_pattern .findall (script_text )}
122126 varargs = bool (varargs_pattern .search (script_text ))
123127
128+ # Exclude function parameters that are used with iterator macros
129+ if exclude_function_params :
130+ indices = {idx for idx in indices if str (idx ) not in exclude_function_params }
131+
124132 return indices , varargs
125133
126134
135+ def _extract_function_parameters (script_text : str , function_target ) -> Set [str ]:
136+ """Extract function parameter variables used within a function that has an iteration macro.
137+
138+ Args:
139+ script_text: The full script content
140+ function_target: The MacroTarget representing the function
141+
142+ Returns:
143+ Set of function parameter variable names (e.g., {'1', '2', '3'})
144+ """
145+ lines = script_text .split ('\n ' )
146+ function_lines = lines [function_target .start_line :function_target .end_line + 1 ]
147+ function_content = '\n ' .join (function_lines )
148+
149+ # Find all positional parameter usages within the function
150+ digit_pattern = re .compile (r"\$([1-9][0-9]*)" )
151+ param_indices = {m for m in digit_pattern .findall (function_content )}
152+
153+ return param_indices
154+
155+
127156@analyzer (order = 20 )
128157def analyze_variable_usages (context : AnalysisContext ) -> None :
129158 """Find all variables referenced in the script."""
@@ -142,15 +171,28 @@ def analyze_loop_variables(context: AnalysisContext) -> None:
142171 context .loop_vars = parse_loop_variables (context .script_text )
143172
144173
145- @analyzer (order = 21.5 )
174+ @analyzer (order = 45 )
146175def identify_macro_iterator_variables (context : AnalysisContext ) -> None :
147176 """Identify iterator variables from iteration macros to exclude from undefined variables."""
148177 try :
149178 from .macros .parser import macro_parser
179+ from .macros .processor import macro_processor
180+
181+ # Extract variable types from annotations and pass to macro processor
182+ variable_types = {}
183+
184+ # Get types from argument annotations (if available)
185+ if context .annotations :
186+ for var_name , annotation in context .annotations .items ():
187+ variable_types [var_name ] = annotation .type
188+
189+ # Set variable types in macro processor
190+ macro_processor .set_variable_types (variable_types )
150191
151192 # Find all iteration macro comments
152193 macro_comments = macro_parser .find_macro_comments (context .script_text )
153194 iterator_vars = set ()
195+ function_param_vars = set ()
154196
155197 for comment in macro_comments :
156198 if comment .macro_type == 'iteration' :
@@ -161,30 +203,39 @@ def identify_macro_iterator_variables(context: AnalysisContext) -> None:
161203 if target :
162204 iteration_macro = macro_parser .parse_iteration_macro (comment , target )
163205 iterator_vars .add (iteration_macro .iterator_var )
206+
207+ # If this macro targets a function, identify function parameters used within that function
208+ if target .target_type == 'function' :
209+ function_params = _extract_function_parameters (context .script_text , target )
210+ function_param_vars .update (function_params )
211+
164212 except Exception :
165213 # If parsing fails, continue with other macros
166214 pass
167215
168- # Store iterator variables in temp_data for use in next analyzer
216+ # Store iterator variables and function parameter variables in temp_data for use in next analyzer
169217 context .temp_data ['macro_iterator_vars' ] = iterator_vars
218+ context .temp_data ['macro_function_param_vars' ] = function_param_vars
170219
171220 except ImportError :
172221 # If macro modules aren't available, skip this step
173222 context .temp_data ['macro_iterator_vars' ] = set ()
223+ context .temp_data ['macro_function_param_vars' ] = set ()
174224
175225
176- @analyzer (order = 22 )
226+ @analyzer (order = 46 )
177227def analyze_undefined_variables (context : AnalysisContext ) -> None :
178228 """Identify variables that are used but not defined in the script."""
179- # Get iterator variables identified by macro analysis
229+ # Get iterator variables and function parameter variables identified by macro analysis
180230 macro_iterator_vars = context .temp_data .get ('macro_iterator_vars' , set ())
231+ macro_function_param_vars = context .temp_data .get ('macro_function_param_vars' , set ())
181232
182- # Exclude iterator variables and loop variables from undefined variables
183- undefined_vars = context .all_used_vars - context .defined_vars - macro_iterator_vars - context .loop_vars
233+ # Exclude iterator variables, function parameter variables, and loop variables from undefined variables
234+ undefined_vars = context .all_used_vars - context .defined_vars - macro_iterator_vars - macro_function_param_vars - context .loop_vars
184235 context .undefined_vars = {name : None for name in sorted (undefined_vars )}
185236
186237
187- @analyzer (order = 23 )
238+ @analyzer (order = 47 )
188239def analyze_environment_variables (context : AnalysisContext ) -> None :
189240 """Separate undefined variables into those with environment defaults and truly undefined."""
190241 env_vars : Dict [str , str ] = {}
@@ -200,10 +251,13 @@ def analyze_environment_variables(context: AnalysisContext) -> None:
200251 context .undefined_vars = remaining_undefined
201252
202253
203- @analyzer (order = 30 )
254+ @analyzer (order = 49 )
204255def analyze_positional_parameters (context : AnalysisContext ) -> None :
205256 """Detect positional parameter usage and varargs references in the script."""
206- indices , varargs = parse_positional_usages (context .script_text )
257+ # Get function parameter variables that are used with iterator macros
258+ macro_function_param_vars = context .temp_data .get ('macro_function_param_vars' , set ())
259+
260+ indices , varargs = parse_positional_usages (context .script_text , exclude_function_params = macro_function_param_vars )
207261 context .positional_indices = indices
208262 context .varargs = varargs
209263
0 commit comments