Skip to content

Commit 4838f18

Browse files
committed
optimize or infer and remove nil
Close #392
1 parent b8ddd52 commit 4838f18

File tree

10 files changed

+109
-33
lines changed

10 files changed

+109
-33
lines changed

crates/emmylua_code_analysis/src/compilation/analyzer/flow/var_analyze/broadcast_up.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,12 @@ pub fn broadcast_up(
8484

8585
if let LuaExpr::LiteralExpr(literal) = expr {
8686
let type_assert = match literal.get_literal()? {
87-
LuaLiteralToken::Nil(_) => TypeAssertion::NotExist,
87+
LuaLiteralToken::Nil(_) => TypeAssertion::Force(LuaType::Nil),
8888
LuaLiteralToken::Bool(b) => {
8989
if b.is_true() {
90-
TypeAssertion::Exist
90+
TypeAssertion::Force(LuaType::BooleanConst(true))
9191
} else {
92-
TypeAssertion::NotExist
92+
TypeAssertion::Force(LuaType::BooleanConst(false))
9393
}
9494
}
9595
LuaLiteralToken::Number(i) => {
@@ -127,12 +127,12 @@ pub fn broadcast_up(
127127

128128
if let LuaExpr::LiteralExpr(literal) = expr {
129129
let type_assert = match literal.get_literal()? {
130-
LuaLiteralToken::Nil(_) => TypeAssertion::Exist,
130+
LuaLiteralToken::Nil(_) => TypeAssertion::Remove(LuaType::Nil),
131131
LuaLiteralToken::Bool(b) => {
132132
if b.is_true() {
133-
TypeAssertion::NotExist
133+
TypeAssertion::Remove(LuaType::BooleanConst(true))
134134
} else {
135-
TypeAssertion::Exist
135+
TypeAssertion::Remove(LuaType::BooleanConst(false))
136136
}
137137
}
138138
LuaLiteralToken::Number(i) => {

crates/emmylua_code_analysis/src/db_index/type/type_assert.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ impl TypeAssertion {
4848
source: LuaType,
4949
) -> Result<LuaType, InferFailReason> {
5050
match self {
51-
TypeAssertion::Exist => Ok(TypeOps::Remove.apply(&source, &LuaType::Nil)),
51+
TypeAssertion::Exist => Ok(TypeOps::RemoveNilOrFalse.apply_source(&source)),
5252
TypeAssertion::NotExist => Ok(TypeOps::NarrowFalseOrNil.apply_source(&source)),
5353
TypeAssertion::Narrow(t) => Ok(TypeOps::Narrow.apply(&source, t)),
5454
TypeAssertion::Add(lua_type) => Ok(TypeOps::Union.apply(&source, lua_type)),

crates/emmylua_code_analysis/src/db_index/type/type_ops/false_or_nil_type.rs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::LuaType;
1+
use crate::{LuaType, LuaUnionType};
22

33
use super::TypeOps;
44

@@ -9,3 +9,34 @@ pub fn narrow_false_or_nil(t: LuaType) -> LuaType {
99

1010
return TypeOps::Narrow.apply(&t, &LuaType::Nil);
1111
}
12+
13+
pub fn remove_false_or_nil(t: LuaType) -> LuaType {
14+
match t {
15+
LuaType::Nil => LuaType::Unknown,
16+
LuaType::BooleanConst(false) => LuaType::Unknown,
17+
LuaType::DocBooleanConst(false) => LuaType::Unknown,
18+
LuaType::Boolean => LuaType::BooleanConst(true),
19+
LuaType::Union(u) => {
20+
let types = u.get_types();
21+
let mut new_types = vec![];
22+
for it in types.iter() {
23+
match it {
24+
LuaType::Nil => {}
25+
LuaType::BooleanConst(false) => {}
26+
LuaType::DocBooleanConst(false) => {}
27+
LuaType::Boolean => new_types.push(LuaType::BooleanConst(true)),
28+
_ => new_types.push(it.clone()),
29+
}
30+
}
31+
32+
if new_types.is_empty() {
33+
return LuaType::Unknown;
34+
} else if new_types.len() == 1 {
35+
return new_types[0].clone();
36+
} else {
37+
return LuaType::Union(LuaUnionType::new(new_types).into());
38+
}
39+
}
40+
_ => t,
41+
}
42+
}

crates/emmylua_code_analysis/src/db_index/type/type_ops/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ pub enum TypeOps {
1313
Union,
1414
/// Remove a type from the source type
1515
Remove,
16+
/// Remove a type from the source type, but keep the source type
17+
RemoveNilOrFalse,
1618
/// Force a type to the source type
1719
Narrow,
1820
/// Only keep the false or nil type
@@ -38,6 +40,7 @@ impl TypeOps {
3840
pub fn apply_source(&self, source: &LuaType) -> LuaType {
3941
match self {
4042
TypeOps::NarrowFalseOrNil => false_or_nil_type::narrow_false_or_nil(source.clone()),
43+
TypeOps::RemoveNilOrFalse => false_or_nil_type::remove_false_or_nil(source.clone()),
4144
_ => source.clone(),
4245
}
4346
}

crates/emmylua_code_analysis/src/db_index/type/type_ops/remove_type.rs

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,11 @@ pub fn remove_type(source: LuaType, removed_type: LuaType) -> Option<LuaType> {
1010
}
1111

1212
match &removed_type {
13-
LuaType::Nil => match &source {
14-
LuaType::Boolean => return Some(LuaType::BooleanConst(true)),
15-
LuaType::BooleanConst(b) => {
16-
if *b {
17-
return Some(LuaType::BooleanConst(true));
18-
} else {
19-
return None;
20-
}
21-
}
22-
LuaType::DocBooleanConst(b) => {
23-
if *b {
24-
return Some(LuaType::DocBooleanConst(true));
25-
} else {
26-
return None;
27-
}
13+
LuaType::Nil => {
14+
if source.is_nil() {
15+
return None;
2816
}
29-
_ => {}
30-
},
17+
}
3118
LuaType::Boolean => {
3219
if source.is_boolean() {
3320
return None;

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,4 @@ mod undefined_doc_param_test;
2121
mod undefined_field_test;
2222
mod undefined_global_test;
2323
mod unnecessary_assert_test;
24+
mod unnecessary_if_test;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#[cfg(test)]
2+
mod test {
3+
use crate::{DiagnosticCode, VirtualWorkspace};
4+
5+
#[test]
6+
fn test_issue_392() {
7+
let mut ws = VirtualWorkspace::new();
8+
assert!(ws.check_code_for(DiagnosticCode::UnnecessaryIf,
9+
r#"
10+
local a = false ---@type boolean|nil
11+
if a == nil or a then -- Unnecessary `if` statement: this condition is always truthy [unnecessary-if]
12+
print('a is not false')
13+
end
14+
"#
15+
));
16+
}
17+
}

crates/emmylua_code_analysis/src/semantic/infer/infer_binary/infer_binary_or.rs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ pub fn special_or_rule(
66
db: &DbIndex,
77
left_type: &LuaType,
88
right_type: &LuaType,
9-
_: LuaExpr,
9+
left_expr: LuaExpr,
1010
right_expr: LuaExpr,
1111
) -> Option<LuaType> {
1212
match right_expr {
@@ -23,10 +23,17 @@ pub fn special_or_rule(
2323
}
2424
}
2525
LuaExpr::LiteralExpr(_) => {
26-
if !right_type.is_nil() {
27-
if check_type_compact(db, &left_type, &right_type).is_ok() {
28-
return Some(TypeOps::Remove.apply(&left_type, &LuaType::Nil));
29-
}
26+
match left_expr {
27+
LuaExpr::CallExpr(_) | LuaExpr::NameExpr(_) | LuaExpr::IndexExpr(_) => {}
28+
_ => return None,
29+
}
30+
31+
if right_type.is_nil() || left_type.is_const() {
32+
return None;
33+
}
34+
35+
if check_type_compact(db, &left_type, &right_type).is_ok() {
36+
return Some(TypeOps::Remove.apply(&left_type, &LuaType::Nil));
3037
}
3138
}
3239

@@ -43,6 +50,5 @@ pub fn infer_binary_expr_or(left: LuaType, right: LuaType) -> InferResult {
4350
return Ok(right);
4451
}
4552

46-
// if check_type_compact(db, source, compact_type)
47-
Ok(TypeOps::Union.apply(&TypeOps::Remove.apply(&left, &LuaType::Nil), &right))
53+
Ok(TypeOps::Union.apply(&&TypeOps::RemoveNilOrFalse.apply_source(&left), &right))
4854
}

crates/emmylua_code_analysis/src/semantic/infer/infer_binary/mod.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,39 @@ pub fn infer_binary_expr(
2626
if let Some(ty) = special_or_rule(db, &left_type, &right_type, left, right) {
2727
return Ok(ty);
2828
}
29+
} else if !matches!(op, BinaryOperator::OpAnd | BinaryOperator::OpOr) {
30+
if let Some(ty) = infer_union_binary_expr(db, op, &left_type, &right_type) {
31+
return Ok(ty);
32+
}
2933
}
3034

3135
infer_binary_expr_type(db, left_type, right_type, op)
3236
}
3337

38+
fn infer_union_binary_expr(
39+
db: &DbIndex,
40+
op: BinaryOperator,
41+
left_type: &LuaType,
42+
right_type: &LuaType,
43+
) -> Option<LuaType> {
44+
let (u, other) = if let LuaType::Union(u) = left_type {
45+
(u, right_type)
46+
} else if let LuaType::Union(u) = right_type {
47+
(u, left_type)
48+
} else {
49+
return None;
50+
};
51+
52+
let mut result = LuaType::Unknown;
53+
let types = u.get_types();
54+
for ty in types.iter() {
55+
if let Ok(ty) = infer_binary_expr_type(db, ty.clone(), other.clone(), op) {
56+
result = TypeOps::Union.apply(&result, &ty);
57+
}
58+
}
59+
return Some(result);
60+
}
61+
3462
fn infer_binary_expr_type(
3563
db: &DbIndex,
3664
left_type: LuaType,

crates/emmylua_doc_cli/src/markdown_generator/init_tl.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ pub fn init_tl(override_template: Option<PathBuf>) -> Option<Tera> {
1818

1919
if let Some(override_template) = override_template {
2020
if !override_template.exists() {
21-
eprintln!("Override template directory does not exist: {:?}", override_template);
21+
eprintln!(
22+
"Override template directory does not exist: {:?}",
23+
override_template
24+
);
2225
return None;
2326
}
2427

0 commit comments

Comments
 (0)