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

Function process_while

src/completion/variable/forward_walk.rs:6914–7041  ·  view source on GitHub ↗

Process a `while` loop. Uses the same two-pass strategy as `process_foreach` and `process_for`: the first pass discovers all variable assignments inside the loop body, the results are merged back into the pre-loop scope, and the final pass re-walks with full visibility of loop-carried assignments.

(while_stmt: &'b While<'b>, scope: &mut ScopeState, ctx: &ForwardWalkCtx<'_>)

Source from the content-addressed store, hash-verified

6912/// pre-loop scope, and the final pass re-walks with full visibility
6913/// of loop-carried assignments.
6914fn process_while<'b>(while_stmt: &'b While<'b>, scope: &mut ScopeState, ctx: &ForwardWalkCtx<'_>) {
6915 let loop_depth = enter_loop();
6916
6917 // Hard limit: skip the body entirely at excessive nesting depth.
6918 if loop_depth > MAX_LOOP_DEPTH {
6919 leave_loop(loop_depth);
6920 return;
6921 }
6922
6923 // Record `&&` chain snapshots for the while condition.
6924 record_and_chain_snapshots(while_stmt.condition, scope, ctx);
6925
6926 let pre_loop_scope = scope.clone();
6927
6928 // The while body executes when the condition is truthy, so apply
6929 // condition narrowing (instanceof, phpstan-assert-if-true, etc.).
6930 // This must happen AFTER saving pre_loop_scope so the narrowing
6931 // only affects the loop body, not the post-loop scope.
6932 apply_condition_narrowing(while_stmt.condition, scope, ctx);
6933
6934 // When the cursor is inside the loop body (completion path), discovery
6935 // passes must walk the ENTIRE body; the final pass uses the real
6936 // cursor_offset so it stops at the cursor as usual.
6937 let body_span = match &while_stmt.body {
6938 WhileBody::Statement(inner) => inner.span(),
6939 WhileBody::ColonDelimited(body) => body.span(),
6940 };
6941 let cursor_in_body =
6942 ctx.cursor_offset >= body_span.start.offset && ctx.cursor_offset <= body_span.end.offset;
6943 let discovery_ctx = if cursor_in_body && !is_diagnostic_scope_active() {
6944 ctx.with_cursor_offset(u32::MAX)
6945 } else {
6946 ctx.with_cursor_offset(ctx.cursor_offset)
6947 };
6948
6949 // Assignment in condition: `while ($x = expr())`
6950 process_condition_assignment(while_stmt.condition, scope, ctx);
6951
6952 // Pass-by-reference in condition: `while (preg_match(..., $matches))`
6953 seed_pass_by_ref_in_condition(while_stmt.condition, scope, ctx);
6954
6955 // Record a snapshot after condition processing (same reasoning as
6956 // the corresponding snapshot in `process_if`).
6957 if is_diagnostic_scope_active() {
6958 let body_start = match &while_stmt.body {
6959 WhileBody::Statement(inner) => inner.span().start.offset,
6960 WhileBody::ColonDelimited(body) => body.colon.start.offset,
6961 };
6962 record_scope_snapshot(body_start, scope);
6963 }
6964
6965 // ── Assignment-depth-bounded loop iteration ─────────────────
6966 let body_stmts: Vec<&Statement<'b>> = match &while_stmt.body {
6967 WhileBody::Statement(inner) => vec![*inner],
6968 WhileBody::ColonDelimited(body) => body.statements.iter().collect(),
6969 };
6970 let assignment_depth =
6971 clamp_iterations_for_depth(assignment_map_depth(&body_stmts), loop_depth);

Callers 1

process_statementFunction · 0.85

Calls 15

enter_loopFunction · 0.85
leave_loopFunction · 0.85
record_scope_snapshotFunction · 0.85
assignment_map_depthFunction · 0.85
walk_body_forwardFunction · 0.85
scope_has_changesFunction · 0.85

Tested by

no test coverage detected