Process a `do-while` loop. Uses the same assignment-depth-bounded iteration as `process_foreach`: a cheap AST walk determines the dependency chain depth, then the body is re-walked up to that many times with fixed-point early exit. Unlike `for`/`while`, the body of a `do-while` always executes at least once, so we do NOT merge with a pre-loop scope at the end.
(dw: &'b DoWhile<'b>, scope: &mut ScopeState, ctx: &ForwardWalkCtx<'_>)
| 7150 | /// Unlike `for`/`while`, the body of a `do-while` always executes at |
| 7151 | /// least once, so we do NOT merge with a pre-loop scope at the end. |
| 7152 | fn process_do_while<'b>(dw: &'b DoWhile<'b>, scope: &mut ScopeState, ctx: &ForwardWalkCtx<'_>) { |
| 7153 | let loop_depth = enter_loop(); |
| 7154 | |
| 7155 | // Hard limit: skip the body entirely at excessive nesting depth. |
| 7156 | if loop_depth > MAX_LOOP_DEPTH { |
| 7157 | leave_loop(loop_depth); |
| 7158 | return; |
| 7159 | } |
| 7160 | |
| 7161 | let pre_loop_scope = scope.clone(); |
| 7162 | |
| 7163 | // ── Assignment-depth-bounded loop iteration ───────────────── |
| 7164 | let body_stmts: Vec<&Statement<'b>> = vec![dw.statement]; |
| 7165 | let assignment_depth = |
| 7166 | clamp_iterations_for_depth(assignment_map_depth(&body_stmts), loop_depth); |
| 7167 | |
| 7168 | // ── Initial walk (always performed) ───────────────────────── |
| 7169 | walk_body_forward(std::iter::once(dw.statement), scope, ctx); |
| 7170 | |
| 7171 | // ── Re-walk iterations (only if types changed) ────────────── |
| 7172 | for _iteration in 0..assignment_depth.saturating_sub(1) { |
| 7173 | if !scope_has_changes(&pre_loop_scope, scope) { |
| 7174 | break; |
| 7175 | } |
| 7176 | |
| 7177 | let mut next_scope = pre_loop_scope.clone(); |
| 7178 | next_scope.merge_branch(scope); |
| 7179 | process_condition_assignment(dw.condition, &mut next_scope, ctx); |
| 7180 | seed_pass_by_ref_in_condition(dw.condition, &mut next_scope, ctx); |
| 7181 | *scope = next_scope; |
| 7182 | |
| 7183 | walk_body_forward(std::iter::once(dw.statement), scope, ctx); |
| 7184 | } |
| 7185 | |
| 7186 | // After the do-while loop, the condition evaluated to false (that's |
| 7187 | // why the loop exited). Apply the inverse of the condition to narrow |
| 7188 | // types. For example: `do { $a = getA(); } while ($a !== null);` |
| 7189 | // => after loop, $a is null. |
| 7190 | apply_condition_narrowing_inverse(dw.condition, scope, ctx); |
| 7191 | |
| 7192 | leave_loop(loop_depth); |
| 7193 | } |
| 7194 | |
| 7195 | /// Process a `try-catch-finally` statement. |
| 7196 | fn process_try<'b>(try_stmt: &'b Try<'b>, scope: &mut ScopeState, ctx: &ForwardWalkCtx<'_>) { |
no test coverage detected