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

Function walk_body_forward

src/completion/variable/forward_walk.rs:2178–2271  ·  view source on GitHub ↗

Walk a sequence of statements top-to-bottom, updating `scope` at each step. Stops when a statement's start offset reaches or exceeds `ctx.cursor_offset`. After this function returns, `scope.get("$varName")` contains the types of `$varName` at the cursor position.

(
    statements: impl Iterator<Item = &'b Statement<'b>>,
    scope: &mut ScopeState,
    ctx: &ForwardWalkCtx<'_>,
)

Source from the content-addressed store, hash-verified

2176/// After this function returns, `scope.get("$varName")` contains the
2177/// types of `$varName` at the cursor position.
2178pub(crate) fn walk_body_forward<'b>(
2179 statements: impl Iterator<Item = &'b Statement<'b>>,
2180 scope: &mut ScopeState,
2181 ctx: &ForwardWalkCtx<'_>,
2182) {
2183 // When the diagnostic scope cache is active, record snapshots at
2184 // every statement boundary — even inside branches (if/else, try,
2185 // foreach, loops). Without this, member accesses inside branch
2186 // bodies would only see the scope from before the branch started,
2187 // missing assignments made inside the branch and causing false-
2188 // positive diagnostics.
2189 let record_snapshots = is_diagnostic_scope_active();
2190
2191 for stmt in statements {
2192 // Stop when we have passed the cursor. We use `>` rather than
2193 // `>=` so that a statement whose start offset exactly equals the
2194 // cursor is still processed. This matters when hovering on the
2195 // LHS variable of an assignment: the cursor sits at the first
2196 // token of the statement, and the user expects to see the *result*
2197 // type of the assignment, not the type from before it.
2198 if stmt.span().start.offset > ctx.cursor_offset {
2199 break;
2200 }
2201
2202 // Check whether the cursor is inside a closure/arrow function
2203 // within this statement. If so, we need to resolve within
2204 // that closure's scope instead.
2205 let stmt_span = stmt.span();
2206 if ctx.cursor_offset >= stmt_span.start.offset
2207 && ctx.cursor_offset <= stmt_span.end.offset
2208 && try_enter_closure(stmt, scope, ctx)
2209 {
2210 return;
2211 }
2212
2213 // On the completion path, when the cursor is inside a ternary
2214 // instanceof branch or match(true) arm, apply narrowing to the
2215 // scope so the variable lookup sees the narrowed type.
2216 let cursor_inside_stmt = ctx.cursor_offset >= stmt_span.start.offset
2217 && ctx.cursor_offset <= stmt_span.end.offset;
2218
2219 if record_snapshots {
2220 record_scope_snapshot(stmt_span.start.offset, scope);
2221 }
2222
2223 process_statement(stmt, scope, ctx);
2224
2225 if cursor_inside_stmt && !record_snapshots {
2226 let expr_opt = match stmt {
2227 Statement::Expression(es) => Some(es.expression),
2228 Statement::Return(ret) => ret.value,
2229 _ => None,
2230 };
2231 if let Some(expr) = expr_opt {
2232 apply_cursor_ternary_narrowing(expr, scope, ctx);
2233 }
2234
2235 // Also apply narrowing inside if/while/for conditions.

Callers 14

resolve_in_method_bodyFunction · 0.85
resolve_in_function_bodyFunction · 0.85
resolve_in_top_levelFunction · 0.85
process_statementFunction · 0.85
process_if_colon_bodyFunction · 0.85
process_foreachFunction · 0.85
process_whileFunction · 0.85
process_forFunction · 0.85
process_do_whileFunction · 0.85
process_tryFunction · 0.85

Calls 6

try_enter_closureFunction · 0.85
record_scope_snapshotFunction · 0.85
process_statementFunction · 0.85

Tested by

no test coverage detected