Skip to content

Commit c7ad0f3

Browse files
committed
refactor flow
1 parent 2608aa1 commit c7ad0f3

File tree

26 files changed

+1398
-1722
lines changed

26 files changed

+1398
-1722
lines changed

crates/emmylua_code_analysis/src/compilation/analyzer/doc/type_ref_tags.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,7 @@ use emmylua_parser::{
66
};
77

88
use crate::{
9-
compilation::analyzer::{
10-
bind_type::bind_type, flow::CastAction, unresolve::UnResolveModuleRef,
11-
},
9+
compilation::analyzer::{bind_type::bind_type, unresolve::UnResolveModuleRef},
1210
db_index::{
1311
LuaDeclId, LuaDocParamInfo, LuaDocReturnInfo, LuaMemberId, LuaOperator, LuaSemanticDeclId,
1412
LuaSignatureId, LuaType,
@@ -188,6 +186,12 @@ pub fn analyze_return(analyzer: &mut DocAnalyzer, tag: LuaDocTagReturn) -> Optio
188186
Some(())
189187
}
190188

189+
enum CastAction {
190+
Add,
191+
Remove,
192+
Force,
193+
}
194+
191195
pub fn analyze_return_cast(analyzer: &mut DocAnalyzer, tag: LuaDocTagReturnCast) -> Option<()> {
192196
if let Some(LuaSemanticDeclId::Signature(signature_id)) = get_owner_id(analyzer) {
193197
let name_token = tag.get_name_token()?;
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
use emmylua_parser::{BinaryOperator, LuaAstNode, LuaAstToken, LuaComment, LuaDocTag};
2+
3+
use crate::{
4+
compilation::analyzer::flow::{
5+
binder::FlowBinder,
6+
flow_node::{FlowAssertion, FlowId, FlowNodeKind},
7+
},
8+
InFiled, LuaType, LuaVarRefId,
9+
};
10+
11+
enum CastAction {
12+
Add,
13+
Remove,
14+
Force,
15+
}
16+
17+
pub fn bind_comment(
18+
binder: &mut FlowBinder,
19+
lua_comment: LuaComment,
20+
mut current: FlowId,
21+
) -> FlowId {
22+
let cast_tags = lua_comment.get_doc_tags().filter_map(|it| match it {
23+
LuaDocTag::Cast(cast) => Some(cast),
24+
_ => None,
25+
});
26+
27+
for cast_tag in cast_tags {
28+
let Some(name_token) = cast_tag.get_name_token() else {
29+
continue;
30+
};
31+
let name = name_token.get_name_text();
32+
let decl = binder
33+
.db
34+
.get_decl_index()
35+
.get_decl_tree(&binder.file_id)
36+
.and_then(|decl_tree| decl_tree.find_local_decl(&name, name_token.get_position()));
37+
let var_ref_id = match decl {
38+
Some(decl) => LuaVarRefId::DeclId(decl.get_id()),
39+
None => LuaVarRefId::Name(name.into()),
40+
};
41+
42+
let mut assertions = vec![];
43+
for cast_op_type in cast_tag.get_op_types() {
44+
let action = match cast_op_type.get_op() {
45+
Some(op) => {
46+
if op.get_op() == BinaryOperator::OpAdd {
47+
CastAction::Add
48+
} else {
49+
CastAction::Remove
50+
}
51+
}
52+
None => CastAction::Force,
53+
};
54+
55+
if cast_op_type.is_nullable() {
56+
match action {
57+
CastAction::Add => {
58+
assertions.push(FlowAssertion::TypeAdd(var_ref_id.clone(), LuaType::Nil));
59+
}
60+
CastAction::Remove => {
61+
assertions
62+
.push(FlowAssertion::TypeRemove(var_ref_id.clone(), LuaType::Nil));
63+
}
64+
_ => {}
65+
}
66+
} else if let Some(doc_typ) = cast_op_type.get_type() {
67+
let file_id = binder.file_id;
68+
let key = InFiled::new(file_id, doc_typ.get_syntax_id());
69+
let typ = match binder.context.cast_flow.get(&key) {
70+
Some(t) => t.clone(),
71+
None => continue,
72+
};
73+
74+
match action {
75+
CastAction::Add => {
76+
assertions.push(FlowAssertion::TypeAdd(var_ref_id.clone(), typ.clone()));
77+
}
78+
CastAction::Remove => {
79+
assertions.push(FlowAssertion::TypeRemove(var_ref_id.clone(), typ.clone()));
80+
}
81+
CastAction::Force => {
82+
assertions.push(FlowAssertion::TypeForce(var_ref_id.clone(), typ.clone()));
83+
}
84+
}
85+
}
86+
}
87+
88+
for assertion in assertions {
89+
let flow_assertion = assertion.clone();
90+
let flow_id = binder.create_node(FlowNodeKind::Assertion(flow_assertion.into()));
91+
binder.add_antecedent(flow_id, current);
92+
current = flow_id;
93+
}
94+
}
95+
96+
current
97+
}
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
use std::ops::Deref;
2+
3+
use emmylua_parser::{
4+
BinaryOperator, LuaAst, LuaBinaryExpr, LuaCallExpr, LuaExpr, LuaLiteralExpr, LuaLiteralToken,
5+
};
6+
use smol_str::SmolStr;
7+
8+
use crate::{
9+
compilation::analyzer::flow::{
10+
bind_analyze::{bind_each_child, exprs::bind_expr},
11+
binder::FlowBinder,
12+
flow_node::{FlowAssertion, FlowId, FlowNodeKind},
13+
},
14+
LuaType,
15+
};
16+
17+
pub fn bind_binary_expr(
18+
binder: &mut FlowBinder,
19+
binary_expr: LuaBinaryExpr,
20+
current: FlowId,
21+
) -> FlowId {
22+
let Some(op_token) = binary_expr.get_op_token() else {
23+
return current;
24+
};
25+
26+
match op_token.get_op() {
27+
BinaryOperator::OpAnd => bind_and_expr(binder, binary_expr, current),
28+
BinaryOperator::OpOr => bind_or_expr(binder, binary_expr, current),
29+
BinaryOperator::OpEq => bind_eq_expr(binder, binary_expr, current),
30+
BinaryOperator::OpNe => bind_ne_expr(binder, binary_expr, current),
31+
_ => {
32+
bind_each_child(binder, LuaAst::LuaBinaryExpr(binary_expr.clone()), current);
33+
current
34+
}
35+
}
36+
}
37+
38+
fn bind_and_expr(binder: &mut FlowBinder, binary_expr: LuaBinaryExpr, current: FlowId) -> FlowId {
39+
let Some((left, right)) = binary_expr.get_exprs() else {
40+
return current;
41+
};
42+
43+
let left_flow_id = bind_expr(binder, left, current);
44+
let right_flow_id = bind_expr(binder, right, left_flow_id);
45+
right_flow_id
46+
}
47+
48+
fn bind_eq_expr(binder: &mut FlowBinder, binary_expr: LuaBinaryExpr, current: FlowId) -> FlowId {
49+
let Some((left, right)) = binary_expr.get_exprs() else {
50+
return current;
51+
};
52+
53+
if let LuaExpr::CallExpr(call_expr) = &left {
54+
if call_expr.is_type() {
55+
if let LuaExpr::LiteralExpr(literal) = &right {
56+
if let Some(flow_assertion) =
57+
get_type_guard_assertion(binder, call_expr.clone(), literal.clone(), current)
58+
{
59+
let flow_id =
60+
binder.create_node(FlowNodeKind::Assertion(flow_assertion.into()));
61+
binder.add_antecedent(flow_id, current);
62+
return flow_id;
63+
}
64+
}
65+
}
66+
} else if let LuaExpr::CallExpr(call_expr) = &right {
67+
if call_expr.is_type() {
68+
if let LuaExpr::LiteralExpr(literal) = &left {
69+
if let Some(flow_assertion) =
70+
get_type_guard_assertion(binder, call_expr.clone(), literal.clone(), current)
71+
{
72+
let flow_id =
73+
binder.create_node(FlowNodeKind::Assertion(flow_assertion.into()));
74+
binder.add_antecedent(flow_id, current);
75+
return flow_id;
76+
}
77+
}
78+
}
79+
}
80+
81+
if let LuaExpr::LiteralExpr(left_literal) = &left {
82+
return bind_eq_literal_expr(binder, right.clone(), left_literal.clone(), current);
83+
} else if let LuaExpr::LiteralExpr(right_literal) = &right {
84+
return bind_eq_literal_expr(binder, left.clone(), right_literal.clone(), current);
85+
}
86+
87+
current
88+
}
89+
90+
fn bind_eq_literal_expr(
91+
binder: &mut FlowBinder,
92+
expr: LuaExpr,
93+
literal_expr: LuaLiteralExpr,
94+
current: FlowId,
95+
) -> FlowId {
96+
let literal_token = match literal_expr.get_literal() {
97+
Some(token) => token,
98+
None => return current,
99+
};
100+
let expr_flow_id = bind_expr(binder, expr, current);
101+
if expr_flow_id == current {
102+
return current;
103+
}
104+
105+
let mut var_ref_id = None;
106+
if let Some(flow_node) = binder.get_flow(expr_flow_id) {
107+
match &flow_node.kind {
108+
FlowNodeKind::Assertion(assertion) => match assertion.deref() {
109+
FlowAssertion::Truthy(ref_id) => {
110+
var_ref_id = Some(ref_id.clone());
111+
}
112+
_ => {}
113+
},
114+
_ => {}
115+
}
116+
};
117+
118+
let Some(var_ref_id) = var_ref_id else {
119+
return current;
120+
};
121+
122+
let flow_assertion = match literal_token {
123+
LuaLiteralToken::Nil(_) => FlowAssertion::TypeGuard(var_ref_id, LuaType::Nil),
124+
LuaLiteralToken::Bool(value) => {
125+
FlowAssertion::TypeForce(var_ref_id, LuaType::BooleanConst(value.is_true()))
126+
}
127+
LuaLiteralToken::Number(value) => {
128+
if value.is_int() {
129+
FlowAssertion::TypeForce(var_ref_id, LuaType::IntegerConst(value.get_int_value()))
130+
} else {
131+
FlowAssertion::TypeForce(var_ref_id, LuaType::FloatConst(value.get_float_value()))
132+
}
133+
}
134+
LuaLiteralToken::String(value) => FlowAssertion::TypeForce(
135+
var_ref_id,
136+
LuaType::StringConst(SmolStr::new(value.get_value()).into()),
137+
),
138+
_ => return current,
139+
};
140+
141+
let flow_id = binder.create_node(FlowNodeKind::Assertion(flow_assertion.into()));
142+
binder.add_antecedent(flow_id, current);
143+
144+
flow_id
145+
}
146+
147+
fn bind_ne_expr(binder: &mut FlowBinder, binary_expr: LuaBinaryExpr, current: FlowId) -> FlowId {
148+
let flow_id = bind_eq_expr(binder, binary_expr, current);
149+
if flow_id == current {
150+
return current;
151+
}
152+
153+
if let Some(flow_node) = binder.get_flow(flow_id) {
154+
if let FlowNodeKind::Assertion(assertion) = &flow_node.kind {
155+
let negated_assertion = assertion.get_negation();
156+
let antecedent = binder.get_antecedents(flow_id).cloned();
157+
let negated_flow_id = binder.create_node_with_antecedent(
158+
FlowNodeKind::Assertion(negated_assertion.into()),
159+
antecedent,
160+
);
161+
return negated_flow_id;
162+
}
163+
}
164+
165+
current
166+
}
167+
168+
fn get_type_guard_assertion(
169+
binder: &mut FlowBinder,
170+
call_expr: LuaCallExpr,
171+
literal_expr: LuaLiteralExpr,
172+
current: FlowId,
173+
) -> Option<FlowAssertion> {
174+
let first_arg = call_expr.get_args_list()?.get_args().next()?;
175+
let flow_id = bind_expr(binder, first_arg, current);
176+
let flow_node = binder.get_flow(flow_id)?;
177+
let var_ref_id = match &flow_node.kind {
178+
FlowNodeKind::Assertion(cond) => {
179+
let var_ref_id = match cond.deref() {
180+
FlowAssertion::Truthy(var_ref_id) => var_ref_id,
181+
_ => return None,
182+
};
183+
184+
var_ref_id.clone()
185+
}
186+
_ => {
187+
return None;
188+
}
189+
};
190+
191+
let type_literal = match literal_expr.get_literal()? {
192+
LuaLiteralToken::String(string) => string.get_value(),
193+
_ => return None,
194+
};
195+
196+
let flow_assertion: FlowAssertion = match type_literal.as_str() {
197+
"number" => FlowAssertion::TypeGuard(var_ref_id, LuaType::Number),
198+
"string" => FlowAssertion::TypeGuard(var_ref_id, LuaType::String),
199+
"boolean" => FlowAssertion::TypeGuard(var_ref_id, LuaType::Boolean),
200+
"table" => FlowAssertion::TypeGuard(var_ref_id, LuaType::Table),
201+
"function" => FlowAssertion::TypeGuard(var_ref_id, LuaType::Function),
202+
"userdata" => FlowAssertion::TypeGuard(var_ref_id, LuaType::Userdata),
203+
"thread" => FlowAssertion::TypeGuard(var_ref_id, LuaType::Thread),
204+
"nil" => FlowAssertion::TypeGuard(var_ref_id, LuaType::Nil),
205+
_ => return None,
206+
};
207+
208+
Some(flow_assertion)
209+
}
210+
211+
fn bind_or_expr(binder: &mut FlowBinder, binary_expr: LuaBinaryExpr, current: FlowId) -> FlowId {
212+
let Some((left, right)) = binary_expr.get_exprs() else {
213+
return current;
214+
};
215+
216+
let left_flow_id = bind_expr(binder, left, current);
217+
let mut left_ne_flow_id = None;
218+
if let Some(flow_node) = binder.get_flow(left_flow_id) {
219+
if let FlowNodeKind::Assertion(assertion) = &flow_node.kind {
220+
let ne_assertion = assertion.get_negation();
221+
let antecedent = binder.get_antecedents(left_flow_id).cloned();
222+
left_ne_flow_id = Some(binder.create_node_with_antecedent(
223+
FlowNodeKind::Assertion(ne_assertion.into()),
224+
antecedent,
225+
));
226+
}
227+
}
228+
229+
let Some(left_ne_flow_id) = left_ne_flow_id else {
230+
return current;
231+
};
232+
233+
let right_flow_id = bind_expr(binder, right, left_ne_flow_id);
234+
right_flow_id
235+
}

0 commit comments

Comments
 (0)