Process a `switch` statement. Each case arm is walked on a clone of the pre-switch scope so that assignments in one arm don't leak into another. After all arms are walked, the resulting scopes are merged (union of types), matching the runtime behaviour where only one arm executes. Fall-through cases (cases with no statements) share their scope with the next non-empty case, mirroring PHP semanti
(switch: &'b Switch<'b>, scope: &mut ScopeState, ctx: &ForwardWalkCtx<'_>)
| 7301 | /// Fall-through cases (cases with no statements) share their scope |
| 7302 | /// with the next non-empty case, mirroring PHP semantics. |
| 7303 | fn process_switch<'b>(switch: &'b Switch<'b>, scope: &mut ScopeState, ctx: &ForwardWalkCtx<'_>) { |
| 7304 | let pre_switch_scope = scope.clone(); |
| 7305 | let cases: Vec<_> = switch.body.cases().iter().collect(); |
| 7306 | |
| 7307 | if cases.is_empty() { |
| 7308 | return; |
| 7309 | } |
| 7310 | |
| 7311 | let mut branch_scopes: Vec<ScopeState> = Vec::new(); |
| 7312 | let mut has_default = false; |
| 7313 | |
| 7314 | // Walk cases, accumulating fall-through groups. |
| 7315 | let mut accumulated_stmts: Vec<&Statement<'b>> = Vec::new(); |
| 7316 | for case in &cases { |
| 7317 | if case.is_default() { |
| 7318 | has_default = true; |
| 7319 | } |
| 7320 | |
| 7321 | let stmts: Vec<_> = case.statements().iter().collect(); |
| 7322 | if stmts.is_empty() { |
| 7323 | // Fall-through: no statements, will share scope with next case. |
| 7324 | continue; |
| 7325 | } |
| 7326 | |
| 7327 | accumulated_stmts.extend(stmts); |
| 7328 | |
| 7329 | let mut case_scope = pre_switch_scope.clone(); |
| 7330 | walk_body_forward(accumulated_stmts.iter().copied(), &mut case_scope, ctx); |
| 7331 | branch_scopes.push(case_scope); |
| 7332 | accumulated_stmts.clear(); |
| 7333 | } |
| 7334 | |
| 7335 | // Handle trailing fall-through cases (empty cases at the end). |
| 7336 | if !accumulated_stmts.is_empty() { |
| 7337 | let mut case_scope = pre_switch_scope.clone(); |
| 7338 | walk_body_forward(accumulated_stmts.iter().copied(), &mut case_scope, ctx); |
| 7339 | branch_scopes.push(case_scope); |
| 7340 | } |
| 7341 | |
| 7342 | if branch_scopes.is_empty() { |
| 7343 | return; |
| 7344 | } |
| 7345 | |
| 7346 | // Merge all branch scopes. |
| 7347 | let mut merged = branch_scopes[0].clone(); |
| 7348 | for s in &branch_scopes[1..] { |
| 7349 | merged.merge_branch(s); |
| 7350 | } |
| 7351 | |
| 7352 | // If there is no default case, the switch might not execute any |
| 7353 | // arm at all, so merge with the pre-switch scope. |
| 7354 | if !has_default { |
| 7355 | merged.merge_branch(&pre_switch_scope); |
| 7356 | } |
| 7357 | |
| 7358 | *scope = merged; |
| 7359 | } |
| 7360 |
no test coverage detected