fix(type-check): accept exhaustive boolean unions - #1228
Conversation
Treat the true | false literal union as covering boolean values during assignability checks, while preserving structural annotation unions. Reuse LuaType::from_vec in both document type inference paths to remove duplicate union flattening logic. Fixes EmmyLuaLs#1226 Assisted-by: Codex
There was a problem hiding this comment.
I'll review the code changes for potential issues:
Issues Found:
1. Loss of Union Type Flattening (Critical)
Files: infer_type.rs and infer_doc_type.rs
The original code flattened nested union types, e.g., (A | B) | (C | D) would become [A, B, C, D]. The new code creates nested unions like [Union(A, B), Union(C, D)]. This can cause:
- Performance degradation: Nested unions require recursive traversal during type checking
- Type equality issues:
(A | B) | Cmay not be considered equal toA | B | Cin all code paths - Potential bugs in type display and serialization
Recommendation: Keep the flattening logic but consider using a helper function to reduce duplication between the two files.
2. Boolean Literal Check Inconsistency (Potential Bug)
File: complex_type/mod.rs
if matches!(compact_type, LuaType::Boolean) {
let contains_literal = |value| {
sub_types.contains(&LuaType::BooleanConst(value))
|| sub_types.contains(&LuaType::DocBooleanConst(value))
};Issues:
- Mixed literal types: The check requires both
trueandfalseto be present, but doesn't handle mixed cases likeBooleanConst(true) | DocBooleanConst(false) - Missing
Booleantype: If the union containsLuaType::Boolean(the general boolean type), it should also cover both literals, but this isn't checked - Early return skips other checks: Returning
Ok(())immediately bypasses any additional type checking that might be needed
Recommendation:
if matches!(compact_type, LuaType::Boolean) {
let has_true = sub_types.iter().any(|t| matches!(t,
LuaType::BooleanConst(true) | LuaType::DocBooleanConst(true) | LuaType::Boolean));
let has_false = sub_types.iter().any(|t| matches!(t,
LuaType::BooleanConst(false) | LuaType::DocBooleanConst(false) | LuaType::Boolean));
if has_true && has_false {
return Ok(());
}
}3. Test Coverage Gap
File: return_type_mismatch_test.rs
The test only covers the case where the annotation is boolean and the return is true | false. It doesn't test:
- The reverse case (annotation is
true | false, return isboolean) - Nested union types like
(true | false) | boolean - The case where only one boolean literal is present
4. Code Duplication
The union flattening logic was duplicated in two files (infer_type.rs and infer_doc_type.rs). While the new code reduces duplication, it introduces the same logic in both places. Consider extracting a shared helper function.
Summary of Recommendations:
- Critical: Restore union flattening to avoid nested union types
- Important: Fix the boolean literal check to handle all cases (mixed literal types, general
Booleantype) - Moderate: Add more comprehensive tests for boolean type checking
- Minor: Extract shared union handling logic to a common utility function
The changes appear to be addressing issue #1226 about boolean type mismatches, but the implementation has some edge cases that need attention.
|
I’m refactoring the type-checking system, and it’s nearly complete. I think it would be better for me to handle this issue. |
|
Doesn't it matter? AI can trivially resolve any conflicts. |
Problem
booleanvalues were rejected where the equivalenttrue | falseliteralunion was expected.
Solution
checking.
Tests
cargo test -p emmylua_code_analysiscargo fmt --all -- --checkcargo clippy -p emmylua_code_analysis --all-targets --all-features --locked -- -D warningsFixes #1226