MCPcopy Create free account
hub / github.com/PHPantom-dev/phpantom_lsp / process_foreach

Function process_foreach

src/completion/variable/forward_walk.rs:6109–6368  ·  view source on GitHub ↗

Process a `foreach` statement.

(foreach: &'b Foreach<'b>, scope: &mut ScopeState, ctx: &ForwardWalkCtx<'_>)

Source from the content-addressed store, hash-verified

6107
6108/// Process a `foreach` statement.
6109fn process_foreach<'b>(foreach: &'b Foreach<'b>, scope: &mut ScopeState, ctx: &ForwardWalkCtx<'_>) {
6110 let loop_depth = enter_loop();
6111
6112 // Hard limit: skip the body entirely at excessive nesting depth.
6113 if loop_depth > MAX_LOOP_DEPTH {
6114 leave_loop(loop_depth);
6115 return;
6116 }
6117
6118 // Apply any standalone `/** @var Type $var */` docblocks that precede
6119 // the foreach keyword. These are not separate AST statements (the
6120 // parser attaches them as comments to the foreach), so they won't be
6121 // processed by `process_expression_statement`. Without this, variables
6122 // typed only via docblock (common in Blade templates) won't be in scope
6123 // when the iterable expression is resolved.
6124 //
6125 // We extract all variables referenced in the foreach expression and
6126 // check for @var annotations for each one.
6127 let foreach_offset = foreach.foreach.span().start.offset as usize;
6128 if let Expression::Variable(Variable::Direct(dv)) = foreach.expression {
6129 let var_name = format!("${}", dv.name);
6130 if scope.get(dv.name).is_empty()
6131 && let Some(var_type) =
6132 crate::docblock::find_var_raw_type_in_source(ctx.content, foreach_offset, &var_name)
6133 {
6134 let resolved = resolve_type_to_resolved_types(
6135 &crate::util::resolve_php_type_names(&var_type, ctx.class_loader),
6136 ctx,
6137 );
6138 scope.set(dv.name, resolved);
6139 }
6140 } else {
6141 // For complex expressions like `$users->active()->byName()`,
6142 // extract the base variable and resolve its type from @var.
6143 let expr_start = foreach.expression.span().start.offset as usize;
6144 let expr_end = foreach.expression.span().end.offset as usize;
6145 if let Some(expr_text) = ctx.content.get(expr_start..expr_end) {
6146 // Extract the base variable (e.g. "$users" from "$users->active()->byName()")
6147 if let Some(base_end) = expr_text.find("->").or_else(|| expr_text.find("::")) {
6148 let base_var = expr_text[..base_end].trim();
6149 if let Some(scope_key) = base_var.strip_prefix('$')
6150 && scope.get(scope_key).is_empty()
6151 && let Some(var_type) = crate::docblock::find_var_raw_type_in_source(
6152 ctx.content,
6153 foreach_offset,
6154 base_var,
6155 )
6156 {
6157 let resolved = resolve_type_to_resolved_types(
6158 &crate::util::resolve_php_type_names(&var_type, ctx.class_loader),
6159 ctx,
6160 );
6161 scope.set(scope_key, resolved);
6162 }
6163 }
6164 }
6165 }
6166

Callers 1

process_statementFunction · 0.85

Calls 15

enter_loopFunction · 0.85
leave_loopFunction · 0.85
resolve_php_type_namesFunction · 0.85
bind_foreach_valueFunction · 0.85
bind_foreach_keyFunction · 0.85
extract_foreach_var_nameFunction · 0.85

Tested by

no test coverage detected