Skip to content

Commit 1ee6431

Browse files
committed
update
1 parent c48d23f commit 1ee6431

7 files changed

Lines changed: 365 additions & 25 deletions

File tree

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
use emmylua_parser::{
2+
BinaryOperator, LuaAstNode, LuaBinaryExpr, LuaCallExpr, LuaChunk, LuaExpr, LuaLiteralToken,
3+
LuaNameExpr,
4+
};
5+
6+
use crate::{
7+
semantic::infer::infer_name::narrow::{
8+
condition_flow::InferConditionFlow, get_single_antecedent,
9+
get_type_at_flow::get_type_at_flow, ResultTypeOrContinue,
10+
},
11+
DbIndex, FlowNode, FlowTree, InferFailReason, LuaDeclId, LuaInferCache, LuaType, TypeOps,
12+
};
13+
14+
pub fn get_type_at_binary_expr(
15+
db: &DbIndex,
16+
tree: &FlowTree,
17+
cache: &mut LuaInferCache,
18+
root: &LuaChunk,
19+
decl_id: LuaDeclId,
20+
flow_node: &FlowNode,
21+
binary_expr: LuaBinaryExpr,
22+
condition_flow: InferConditionFlow,
23+
) -> Result<ResultTypeOrContinue, InferFailReason> {
24+
let Some(op_token) = binary_expr.get_op_token() else {
25+
return Ok(ResultTypeOrContinue::Continue);
26+
};
27+
28+
let Some((left_expr, right_expr)) = binary_expr.get_exprs() else {
29+
return Ok(ResultTypeOrContinue::Continue);
30+
};
31+
32+
match op_token.get_op() {
33+
BinaryOperator::OpLt
34+
| BinaryOperator::OpLe
35+
| BinaryOperator::OpGt
36+
| BinaryOperator::OpGe => {
37+
// todo check number range
38+
}
39+
BinaryOperator::OpEq => {
40+
let result_type = maybe_type_guard_bianry(
41+
db,
42+
tree,
43+
cache,
44+
root,
45+
decl_id,
46+
flow_node,
47+
left_expr,
48+
right_expr,
49+
condition_flow,
50+
)?;
51+
if let ResultTypeOrContinue::Result(result_type) = result_type {
52+
return Ok(ResultTypeOrContinue::Result(result_type));
53+
}
54+
}
55+
BinaryOperator::OpNe => {
56+
let result_type = maybe_type_guard_bianry(
57+
db,
58+
tree,
59+
cache,
60+
root,
61+
decl_id,
62+
flow_node,
63+
left_expr,
64+
right_expr,
65+
condition_flow.get_negated(),
66+
)?;
67+
if let ResultTypeOrContinue::Result(result_type) = result_type {
68+
return Ok(ResultTypeOrContinue::Result(result_type));
69+
}
70+
}
71+
_ => {}
72+
}
73+
74+
Ok(ResultTypeOrContinue::Continue)
75+
}
76+
77+
fn maybe_type_guard_bianry(
78+
db: &DbIndex,
79+
tree: &FlowTree,
80+
cache: &mut LuaInferCache,
81+
root: &LuaChunk,
82+
decl_id: LuaDeclId,
83+
flow_node: &FlowNode,
84+
left_expr: LuaExpr,
85+
right_expr: LuaExpr,
86+
condition_flow: InferConditionFlow,
87+
) -> Result<ResultTypeOrContinue, InferFailReason> {
88+
let mut type_guard_expr: Option<LuaCallExpr> = None;
89+
let mut literal_string = String::new();
90+
if let LuaExpr::CallExpr(call_expr) = left_expr {
91+
if call_expr.is_type() {
92+
type_guard_expr = Some(call_expr);
93+
if let LuaExpr::LiteralExpr(literal_expr) = right_expr {
94+
match literal_expr.get_literal() {
95+
Some(LuaLiteralToken::String(s)) => {
96+
literal_string = s.get_value();
97+
}
98+
_ => return Ok(ResultTypeOrContinue::Continue),
99+
}
100+
}
101+
}
102+
} else if let LuaExpr::CallExpr(call_expr) = right_expr {
103+
if call_expr.is_type() {
104+
type_guard_expr = Some(call_expr);
105+
if let LuaExpr::LiteralExpr(literal_expr) = left_expr {
106+
match literal_expr.get_literal() {
107+
Some(LuaLiteralToken::String(s)) => {
108+
literal_string = s.get_value();
109+
}
110+
_ => return Ok(ResultTypeOrContinue::Continue),
111+
}
112+
}
113+
}
114+
}
115+
116+
if type_guard_expr.is_none() || literal_string.is_empty() {
117+
return Ok(ResultTypeOrContinue::Continue);
118+
}
119+
120+
let Some(arg_list) = type_guard_expr.unwrap().get_args_list() else {
121+
return Ok(ResultTypeOrContinue::Continue);
122+
};
123+
124+
let Some(arg) = arg_list.get_args().next() else {
125+
return Ok(ResultTypeOrContinue::Continue);
126+
};
127+
128+
let LuaExpr::NameExpr(name_expr) = arg else {
129+
return Ok(ResultTypeOrContinue::Continue);
130+
};
131+
132+
let maybe_ref_decl_id = db
133+
.get_reference_index()
134+
.get_var_reference_decl(&cache.get_file_id(), name_expr.get_range());
135+
136+
let Some(ref_decl_id) = maybe_ref_decl_id else {
137+
// If we found a reference declaration ID, we can use it
138+
return Ok(ResultTypeOrContinue::Continue);
139+
};
140+
141+
if ref_decl_id != decl_id {
142+
return Ok(ResultTypeOrContinue::Continue);
143+
}
144+
145+
let anatecedent_flow_id = get_single_antecedent(tree, flow_node)?;
146+
let antecedent_type =
147+
get_type_at_flow(db, tree, cache, root, ref_decl_id, anatecedent_flow_id)?;
148+
149+
let narrow = match literal_string.as_str() {
150+
"number" => LuaType::Number,
151+
"string" => LuaType::String,
152+
"boolean" => LuaType::Boolean,
153+
"table" => LuaType::Table,
154+
"function" => LuaType::Function,
155+
"thread" => LuaType::Thread,
156+
"userdata" => LuaType::Userdata,
157+
"nil" => LuaType::Nil,
158+
_ => {
159+
// If the type is not recognized, we cannot narrow it
160+
return Ok(ResultTypeOrContinue::Continue);
161+
}
162+
};
163+
164+
let result_type = match condition_flow {
165+
InferConditionFlow::TrueCondition => TypeOps::Narrow.apply(db, &antecedent_type, &narrow),
166+
InferConditionFlow::FalseCondition => TypeOps::Remove.apply(db, &antecedent_type, &narrow),
167+
};
168+
169+
Ok(ResultTypeOrContinue::Result(result_type))
170+
}
171+
172+
fn maybe_var_eq(
173+
db: &DbIndex,
174+
tree: &FlowTree,
175+
cache: &mut LuaInferCache,
176+
root: &LuaChunk,
177+
decl_id: LuaDeclId,
178+
flow_node: &FlowNode,
179+
left_expr: LuaExpr,
180+
right_expr: LuaExpr,
181+
) -> Result<ResultTypeOrContinue, InferFailReason> {
182+
Ok(ResultTypeOrContinue::Continue)
183+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
use emmylua_parser::{LuaCallExpr, LuaChunk};
2+
3+
use crate::{
4+
semantic::infer::infer_name::narrow::{condition_flow::InferConditionFlow, ResultTypeOrContinue}, DbIndex, FlowNode, FlowTree,
5+
InferFailReason, LuaDeclId, LuaInferCache,
6+
};
7+
8+
#[allow(unused)]
9+
pub fn get_type_at_call_expr(
10+
db: &DbIndex,
11+
tree: &FlowTree,
12+
cache: &mut LuaInferCache,
13+
root: &LuaChunk,
14+
decl_id: LuaDeclId,
15+
flow_node: &FlowNode,
16+
call_expr: LuaCallExpr,
17+
condition_flow: InferConditionFlow
18+
) -> Result<ResultTypeOrContinue, InferFailReason> {
19+
// let Some(call_prefix) = call_expr.get_prefix_expr() else {
20+
// return Ok(ResultTypeOrContinue::Continue);
21+
// };
22+
23+
24+
Ok(ResultTypeOrContinue::Continue)
25+
}
26+
27+
// pub fn get_type_cast_call(call_prefix: LuaExpr) {
28+
// let
29+
// }
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
use emmylua_parser::{LuaChunk, LuaIndexExpr};
2+
3+
use crate::{
4+
semantic::infer::infer_name::narrow::{
5+
condition_flow::InferConditionFlow, ResultTypeOrContinue,
6+
},
7+
DbIndex, FlowNode, FlowTree, InferFailReason, LuaDeclId, LuaInferCache,
8+
};
9+
10+
#[allow(unused)]
11+
pub fn get_type_at_index_expr(
12+
db: &DbIndex,
13+
tree: &FlowTree,
14+
cache: &mut LuaInferCache,
15+
root: &LuaChunk,
16+
decl_id: LuaDeclId,
17+
flow_node: &FlowNode,
18+
index_expr: LuaIndexExpr,
19+
condition_flow: InferConditionFlow,
20+
) -> Result<ResultTypeOrContinue, InferFailReason> {
21+
Ok(ResultTypeOrContinue::Continue)
22+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
mod binary_flow;
2+
mod call_flow;
3+
mod index_flow;
4+
5+
use emmylua_parser::{LuaAstNode, LuaChunk, LuaExpr, LuaNameExpr};
6+
7+
use crate::{
8+
semantic::infer::infer_name::narrow::{
9+
condition_flow::{
10+
self, binary_flow::get_type_at_binary_expr, call_flow::get_type_at_call_expr, index_flow::get_type_at_index_expr
11+
},
12+
get_single_antecedent,
13+
get_type_at_flow::get_type_at_flow,
14+
ResultTypeOrContinue,
15+
}, DbIndex, FlowNode, FlowTree, InferFailReason, LuaDeclId, LuaInferCache, LuaType, TypeOps
16+
};
17+
18+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19+
pub enum InferConditionFlow {
20+
TrueCondition,
21+
FalseCondition,
22+
}
23+
24+
impl InferConditionFlow {
25+
pub fn get_negated(&self) -> Self {
26+
match self {
27+
InferConditionFlow::TrueCondition => InferConditionFlow::FalseCondition,
28+
InferConditionFlow::FalseCondition => InferConditionFlow::TrueCondition,
29+
}
30+
}
31+
}
32+
33+
pub fn get_type_at_condition_flow(
34+
db: &DbIndex,
35+
tree: &FlowTree,
36+
cache: &mut LuaInferCache,
37+
root: &LuaChunk,
38+
decl_id: LuaDeclId,
39+
flow_node: &FlowNode,
40+
condition: LuaExpr,
41+
condition_flow: InferConditionFlow,
42+
) -> Result<ResultTypeOrContinue, InferFailReason> {
43+
match condition {
44+
LuaExpr::NameExpr(name_expr) => {
45+
get_type_at_name_expr(db, tree, cache, root, decl_id, flow_node, name_expr, condition_flow)
46+
}
47+
LuaExpr::CallExpr(call_expr) => {
48+
get_type_at_call_expr(db, tree, cache, root, decl_id, flow_node, call_expr, condition_flow)
49+
}
50+
LuaExpr::IndexExpr(index_expr) => {
51+
get_type_at_index_expr(db, tree, cache, root, decl_id, flow_node, index_expr, condition_flow)
52+
}
53+
LuaExpr::TableExpr(_) | LuaExpr::LiteralExpr(_) | LuaExpr::ClosureExpr(_) => {
54+
Ok(ResultTypeOrContinue::Continue)
55+
}
56+
LuaExpr::BinaryExpr(binary_expr) => {
57+
get_type_at_binary_expr(db, tree, cache, root, decl_id, flow_node, binary_expr, condition_flow)
58+
}
59+
LuaExpr::UnaryExpr(_) => Ok(ResultTypeOrContinue::Continue),
60+
LuaExpr::ParenExpr(paren_expr) => {
61+
let Some(inner_expr) = paren_expr.get_expr() else {
62+
return Ok(ResultTypeOrContinue::Continue);
63+
};
64+
65+
get_type_at_condition_flow(db, tree, cache, root, decl_id, flow_node, inner_expr, condition_flow)
66+
}
67+
}
68+
}
69+
70+
fn get_type_at_name_expr(
71+
db: &DbIndex,
72+
tree: &FlowTree,
73+
cache: &mut LuaInferCache,
74+
root: &LuaChunk,
75+
decl_id: LuaDeclId,
76+
flow_node: &FlowNode,
77+
name_expr: LuaNameExpr,
78+
condition_flow: InferConditionFlow,
79+
) -> Result<ResultTypeOrContinue, InferFailReason> {
80+
let file_id = cache.get_file_id();
81+
let ref_decl_id = db
82+
.get_reference_index()
83+
.get_var_reference_decl(&file_id, name_expr.get_range())
84+
.ok_or(InferFailReason::None)?;
85+
86+
if ref_decl_id != decl_id {
87+
return Ok(ResultTypeOrContinue::Continue);
88+
}
89+
90+
let antecedent_flow_id = get_single_antecedent(tree, flow_node)?;
91+
let antecedent_type = get_type_at_flow(db, tree, cache, root, ref_decl_id, antecedent_flow_id)?;
92+
93+
let result_type = match condition_flow {
94+
InferConditionFlow::FalseCondition => TypeOps::NarrowFalseOrNil.apply_source(db, &antecedent_type),
95+
InferConditionFlow::TrueCondition => TypeOps::RemoveNilOrFalse.apply_source(db, &antecedent_type),
96+
};
97+
98+
Ok(ResultTypeOrContinue::Result(result_type))
99+
}

crates/emmylua_code_analysis/src/semantic/infer/infer_name/narrow/get_type_at_condition_flow.rs

Lines changed: 0 additions & 18 deletions
This file was deleted.

0 commit comments

Comments
 (0)