Skip to content

Commit bcc92ca

Browse files
committed
fix(flow): treat break-capable constant loops as divergence
Keep constant-condition loops from being marked as proven infinite when the body can break. This preserves the stronger is_infinite signal for loops that really cannot exit, while letting MissingReturn accept a later explicit return when the loop can break at runtime. Add regression coverage for both constant-loop shapes before a trailing return.
1 parent 94f34c8 commit bcc92ca

2 files changed

Lines changed: 43 additions & 3 deletions

File tree

crates/emmylua_code_analysis/src/compilation/analyzer/lua/func_body.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -178,8 +178,8 @@ where
178178
return_points: body.return_points,
179179
can_fall_through: body.can_break,
180180
can_break: false,
181-
is_infinite: body.can_fall_through || body.is_infinite,
182-
may_diverge: body.may_diverge,
181+
is_infinite: (body.can_fall_through && !body.can_break) || body.is_infinite,
182+
may_diverge: (body.can_fall_through && body.can_break) || body.may_diverge,
183183
})
184184
}
185185
ConditionState::Dynamic => {
@@ -225,7 +225,11 @@ where
225225
flow.can_fall_through = true;
226226
}
227227
ConditionState::Falsy => {
228-
flow.is_infinite = true;
228+
if body.can_break {
229+
flow.may_diverge = true;
230+
} else {
231+
flow.is_infinite = true;
232+
}
229233
}
230234
ConditionState::Dynamic => {
231235
flow.can_fall_through = true;

crates/emmylua_code_analysis/src/diagnostic/test/check_return_count_test.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,42 @@ mod tests {
706706
);
707707
}
708708

709+
#[test]
710+
fn test_missing_return_accepts_truthy_while_with_break_before_return() {
711+
assert_missing_return_ok(
712+
r#"
713+
---@return number
714+
local function foo(done)
715+
while true do
716+
if done then
717+
break
718+
end
719+
end
720+
721+
return 1
722+
end
723+
"#,
724+
);
725+
}
726+
727+
#[test]
728+
fn test_missing_return_accepts_infinite_repeat_with_break_before_return() {
729+
assert_missing_return_ok(
730+
r#"
731+
---@return number
732+
local function foo(done)
733+
repeat
734+
if done then
735+
break
736+
end
737+
until false
738+
739+
return 1
740+
end
741+
"#,
742+
);
743+
}
744+
709745
#[test]
710746
fn test_missing_return_rejects_dynamic_while_with_infinite_body_before_return() {
711747
assert_missing_return_error(

0 commit comments

Comments
 (0)