fix: separate unreachable conditions from never types#1098
Conversation
There was a problem hiding this comment.
I've reviewed the code changes. Here are my observations:
Issues Found
1. Potential Logic Error in finish_unreachable_condition
- File:
get_type_at_flow.rs(line ~1545-1552) - Issue: When
FlowMode::Normalis used inside an unreachable branch, the function returnsSchedulerStep::ContinueWalk(walk)without modifying the type. This could cause the type to propagate incorrectly through unreachable code paths. - Recommendation: Consider whether
Normalmode inside unreachable branches should also returnLuaType::Neverto prevent type pollution.
2. Missing Test for Edge Case
- Issue: The new tests cover
true/falsereturn conditions and plain function calls, but there's no test for:- Nested conditions (e.g.,
if a() then if b() then ... end end) - Mixed conditions with
elseifbranches - Conditions with
and/oroperators
- Nested conditions (e.g.,
- Recommendation: Add tests for these edge cases to ensure the new
Unreachableaction works correctly in complex scenarios.
3. Potential Performance Concern
- File:
get_type_at_flow.rs(line ~1320-1330) - Issue: The new code checks
branch_flow_ids.len()and handles 0 and 1 cases separately. For single predecessors, it continues the walk without creating a merge query. This is correct but adds a branch in a hot path. - Recommendation: Consider if this check could be optimized or if the single-predecessor case could be handled earlier in the flow graph construction.
4. Inconsistent Naming
- File:
mod.rs(cache) - Issue: The enum variant
MergeBranchis named differently from the relatedIgnoreConditionsvariant.MergeBranchdescribes what it does, whileIgnoreConditionsdescribes how it behaves. - Recommendation: Consider renaming to
MergeContributionorBranchContributionfor consistency with the "what it does" pattern.
Positive Aspects
- Clear separation of concerns: The new
Unreachableaction cleanly separates unreachable conditions from type results. - Good test coverage: The three new tests cover basic scenarios for function calls in conditions.
- Backward compatible: The changes maintain existing behavior for non-merge queries.
Minor Suggestions
- Documentation: Add comments explaining why
MergeBranchmode returnsNeverwhileNormalmode continues the walk infinish_unreachable_condition. - Type safety: Consider making
FlowModeimplementCopyexplicitly (it already derives it, but explicit documentation would help).
Overall, the changes look well-structured and address the issue of unreachable condition edges in merge scenarios. The logic is sound, but additional edge case testing would improve confidence.
There was a problem hiding this comment.
Code Review
This pull request refactors control flow analysis to better handle unreachable condition edges. It introduces a new FlowMode enum (Normal, MergeBranch, and IgnoreConditions) and a ConditionFlowAction::Unreachable action. This allows unreachable conditions to contribute never to merged types while letting point queries inside impossible branches continue to their antecedents. The feedback highlights three locations in get_type_at_flow.rs where hardcoding FlowMode::Normal discards the MergeBranch mode, which can prevent unreachable condition edges from correctly contributing never to the merged type.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
3e26dbd to
84f482f
Compare
Truthiness checks for call conditions treated an impossible branch as `Result(Never)`. That made the queried symbol look like `never` while walking the branch, so unrelated callable values inside impossible `if`/`else` bodies could be narrowed to non-callable. Add an explicit unreachable condition action and handle it by query mode. Normal point queries continue past an unreachable condition edge so unrelated symbols keep their declared types. Merge-branch queries finish with `never`, so unreachable branch predecessors still drop out of the merged type. Carry branch reachability as an internal flow-query result instead of inferring it from `LuaType::Never`. Assignment and cast continuations can then distinguish an unreachable predecessor from an ordinary type result that happens to be `never`. Preserve merge-branch mode through assignment, tag-cast, and correlated condition subqueries. That prevents assignments, annotated assignments, and casts inside impossible branches from contributing their statement-local type effects to the final merge. Rename flow modes around their actual use and keep single-predecessor branch labels out of merge mode. Add coverage for plain call conditions, always-false and always-true call-condition branches, and unreachable assignment, annotated-assignment, and cast merge contributions. Assisted-by: Codex
84f482f to
66b0434
Compare
Problem
Truthiness checks for call conditions treated an impossible branch as a
neverresult for the queried symbol. That made unrelated symbols inside impossibleif/elsebodies look likenever, which could surface as bogus diagnostics such as a callable local becoming non-callable inside the branch.Solution
Add an explicit unreachable condition action and handle it by flow query mode. Normal point queries continue past unreachable condition edges so unrelated symbols keep their declared types. Merge-branch queries convert an unreachable condition edge into
never, preserving branch-merge behavior where impossible predecessors should not contribute to the final union.This also renames the flow modes around their actual use, keeps single-predecessor branch labels out of merge mode, and adds regression coverage for plain call conditions plus always-false and always-true call-condition branches.
Tests
cargo fmt --all --checkgit diff --checkcargo test -p emmylua_code_analysisTMPDIR=/private/tmp cargo test