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

Function walk_closures_in_expr

src/completion/variable/forward_walk.rs:433–626  ·  view source on GitHub ↗

Recursively scan an expression tree for closures/arrow functions and walk their bodies with properly seeded scopes. When a closure/arrow function is found: 1. Build a scope for the closure body (fresh for closures, cloned from outer for arrow functions). 2. Seed the scope with parameter types (using callable inference from the enclosing call's signature when parameters are untyped). 3. Walk the b

(
    expr: &'b Expression<'b>,
    outer_scope: &ScopeState,
    ctx: &ForwardWalkCtx<'_>,
    inferred_params: Option<&[PhpType]>,
)

Source from the content-addressed store, hash-verified

431/// passes the inferred types so untyped parameters get the correct
432/// types.
433fn walk_closures_in_expr<'b>(
434 expr: &'b Expression<'b>,
435 outer_scope: &ScopeState,
436 ctx: &ForwardWalkCtx<'_>,
437 inferred_params: Option<&[PhpType]>,
438) {
439 match expr {
440 Expression::Closure(closure) => {
441 // Build a fresh scope for the closure.
442 let mut closure_scope = ScopeState::new();
443
444 // PHP closures implicitly capture `$this` from the
445 // enclosing class method. Seed it from the outer scope
446 // so that `$this->prop` inside the closure resolves
447 // without calling `resolve_variable_types`.
448 let this_types = outer_scope.get("$this");
449 if !this_types.is_empty() {
450 closure_scope.set("$this", this_types.to_vec());
451 }
452
453 // Seed with `use(...)` variables from the outer scope.
454 if let Some(ref use_clause) = closure.use_clause {
455 for use_var in use_clause.variables.iter() {
456 let var_name = use_var.variable.name.to_string();
457 let from_outer = outer_scope.get(&var_name);
458 if !from_outer.is_empty() {
459 closure_scope.set(&var_name, from_outer.to_vec());
460 }
461 }
462 }
463
464 // Seed with parameter types, using callable inference when
465 // available. Filter out any inferred params whose base
466 // type is unresolvable (e.g. PHPStan pseudo-types like
467 // `collection-of<T>`) so they don't poison the scope —
468 // the param simply won't be seeded, which is better than
469 // skipping the entire closure body.
470 let inferred = inferred_params.unwrap_or(&[]);
471 let filtered_inferred = filter_resolvable_inferred_params(inferred, ctx);
472 seed_closure_params(
473 &mut closure_scope,
474 &closure.parameter_list,
475 closure.span().start.offset,
476 &filtered_inferred,
477 ctx,
478 );
479
480 // Record the scope at the body start.
481 let body_span = closure.body.span();
482 record_scope_snapshot(body_span.start.offset, &closure_scope);
483
484 // Walk the closure body.
485 walk_body_for_diagnostics(closure.body.statements.iter(), &mut closure_scope, ctx);
486
487 // Record at body end (closure scope).
488 record_scope_snapshot(body_span.end.offset, &closure_scope);
489
490 // Restore the outer scope immediately after the closure body

Callers 3

walk_closures_in_callFunction · 0.85

Calls 11

seed_closure_paramsFunction · 0.85
record_scope_snapshotFunction · 0.85
walk_closures_in_callFunction · 0.85
setMethod · 0.80
iterMethod · 0.80
cloneMethod · 0.80
getMethod · 0.45
is_emptyMethod · 0.45

Tested by

no test coverage detected