Skip to content

Commit 0c44e86

Browse files
committed
fix(analyzer): propagate redefined variable types through break statements in loops
Signed-off-by: azjezz <[email protected]>
1 parent a8b0afb commit 0c44e86

File tree

4 files changed

+45
-0
lines changed

4 files changed

+45
-0
lines changed

crates/analyzer/src/statement/loop/break.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,22 @@ impl<'ast, 'arena> Analyzable<'ast, 'arena> for Break<'arena> {
8787
let redefined_vars =
8888
block_context.get_redefined_locals(&loop_scope.parent_context_variables, false, &mut removed_var_ids);
8989

90+
for (var_id, parent_type) in &loop_scope.parent_context_variables {
91+
if !redefined_vars.contains_key(var_id) {
92+
loop_scope.possibly_redefined_loop_parent_variables.insert(
93+
*var_id,
94+
Rc::new(add_optional_union_type(
95+
(**parent_type).clone(),
96+
loop_scope
97+
.possibly_redefined_loop_parent_variables
98+
.get(var_id)
99+
.map(std::convert::AsRef::as_ref),
100+
context.codebase,
101+
)),
102+
);
103+
}
104+
}
105+
90106
for (var_id, var_type) in redefined_vars {
91107
loop_scope.possibly_redefined_loop_parent_variables.insert(
92108
var_id,

crates/analyzer/src/statement/loop/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -734,6 +734,13 @@ fn analyze<'ctx, 'ast, 'arena>(
734734
*variable_id,
735735
Rc::new(combine_union_types(variable_type, possibly_defined_type, codebase, true)),
736736
);
737+
} else if let Some(possibly_redefined_type) =
738+
loop_scope.possibly_redefined_loop_parent_variables.get(variable_id)
739+
{
740+
loop_parent_context.locals.insert(
741+
*variable_id,
742+
Rc::new(combine_union_types(variable_type, possibly_redefined_type, codebase, true)),
743+
);
737744
}
738745
} else {
739746
loop_parent_context.locals.insert(*variable_id, variable_type.clone());
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
function takeString(string $s): void
4+
{
5+
takeString($s);
6+
}
7+
8+
function testSimpleBreakNarrowing(null|string $value): string
9+
{
10+
while (true) {
11+
if ($value === null) {
12+
$value = 'default';
13+
break;
14+
}
15+
break;
16+
}
17+
18+
takeString($value);
19+
20+
return $value;
21+
}

crates/analyzer/tests/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ test_case!(array_shape_fields);
3838
test_case!(assert_concrete_to_template_type);
3939
test_case!(assert_generic_array_key_is_array_key);
4040
test_case!(bare_identifier_in_array_access);
41+
test_case!(break_narrowing);
4142
test_case!(by_reference_invalidation);
4243
test_case!(class_like_constant_access);
4344
test_case!(collection_types);

0 commit comments

Comments
 (0)