Skip to content

Commit 89d68d8

Browse files
committed
special handle for pcall in check await in sync
1 parent 5feb8b3 commit 89d68d8

File tree

2 files changed

+121
-14
lines changed

2 files changed

+121
-14
lines changed
Lines changed: 88 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
use emmylua_parser::{LuaAstNode, LuaCallExpr, LuaClosureExpr};
1+
use emmylua_parser::{LuaAstNode, LuaCallArgList, LuaCallExpr, LuaClosureExpr, LuaExpr};
22
use rowan::NodeOrToken;
33

4-
use crate::{DiagnosticCode, LuaPropertyOwnerId, LuaSignatureId, SemanticModel};
4+
use crate::{DiagnosticCode, LuaPropertyOwnerId, LuaSignatureId, LuaType, SemanticModel};
55

66
use super::DiagnosticContext;
77

@@ -10,7 +10,8 @@ pub const CODES: &[DiagnosticCode] = &[DiagnosticCode::AwaitInSync];
1010
pub fn check(context: &mut DiagnosticContext, semantic_model: &SemanticModel) -> Option<()> {
1111
let root = semantic_model.get_root().clone();
1212
for call_expr in root.descendants::<LuaCallExpr>() {
13-
check_call_expr(context, semantic_model, call_expr);
13+
check_call_expr(context, semantic_model, call_expr.clone());
14+
check_pcall_or_xpcall(context, semantic_model, call_expr);
1415
}
1516

1617
Some(())
@@ -21,9 +22,9 @@ fn check_call_expr(
2122
semantic_model: &SemanticModel,
2223
call_expr: LuaCallExpr,
2324
) -> Option<()> {
24-
let prefix_node = call_expr.get_prefix_expr()?;
25+
let prefix_expr = call_expr.get_prefix_expr()?;
2526
let property_owner =
26-
semantic_model.get_property_owner_id(NodeOrToken::Node(prefix_node.syntax().clone()))?;
27+
semantic_model.get_property_owner_id(NodeOrToken::Node(prefix_expr.syntax().clone()))?;
2728

2829
let property = semantic_model
2930
.get_db()
@@ -33,7 +34,7 @@ fn check_call_expr(
3334
if !check_call_is_in_async_function(semantic_model, call_expr).unwrap_or(false) {
3435
context.add_diagnostic(
3536
DiagnosticCode::AwaitInSync,
36-
prefix_node.get_range(),
37+
prefix_expr.get_range(),
3738
"await in sync function".to_string(),
3839
None,
3940
);
@@ -43,17 +44,90 @@ fn check_call_expr(
4344
Some(())
4445
}
4546

47+
fn check_pcall_or_xpcall(
48+
context: &mut DiagnosticContext,
49+
semantic_model: &SemanticModel,
50+
call_expr: LuaCallExpr,
51+
) -> Option<()> {
52+
let prefix_expr = call_expr.get_prefix_expr()?;
53+
if let LuaExpr::NameExpr(name_expr) = prefix_expr {
54+
let name = name_expr.get_name_text()?;
55+
if name == "pcall" || name == "xpcall" {
56+
let arg_list = call_expr.get_args_list()?;
57+
let first_arg = arg_list.get_args().next()?;
58+
let range = first_arg.get_range();
59+
let arg_type = semantic_model.infer_expr(first_arg)?;
60+
let is_async = match &arg_type {
61+
LuaType::DocFunction(f) => f.is_async(),
62+
LuaType::Signature(sig) => {
63+
let property_owner = LuaPropertyOwnerId::Signature(*sig);
64+
65+
let property = semantic_model
66+
.get_db()
67+
.get_property_index()
68+
.get_property(property_owner)?;
69+
property.is_async
70+
}
71+
_ => return None,
72+
};
73+
74+
if is_async {
75+
if !check_call_is_in_async_function(semantic_model, call_expr).unwrap_or(false) {
76+
context.add_diagnostic(
77+
DiagnosticCode::AwaitInSync,
78+
range,
79+
"await in sync function".to_string(),
80+
None,
81+
);
82+
}
83+
}
84+
}
85+
}
86+
87+
Some(())
88+
}
89+
4690
fn check_call_is_in_async_function(
4791
semantic_model: &SemanticModel,
4892
call_expr: LuaCallExpr,
4993
) -> Option<bool> {
5094
let file_id = semantic_model.get_file_id();
51-
let closure = call_expr.ancestors::<LuaClosureExpr>().next()?;
52-
let signature_id = LuaSignatureId::from_closure(file_id, &closure);
53-
let property_owner = LuaPropertyOwnerId::Signature(signature_id);
54-
let property = semantic_model
55-
.get_db()
56-
.get_property_index()
57-
.get_property(property_owner)?;
58-
Some(property.is_async)
95+
let closures = call_expr.ancestors::<LuaClosureExpr>();
96+
for closure in closures {
97+
let signature_id = LuaSignatureId::from_closure(file_id, &closure);
98+
let property_owner = LuaPropertyOwnerId::Signature(signature_id);
99+
let is_async = match semantic_model
100+
.get_db()
101+
.get_property_index()
102+
.get_property(property_owner)
103+
{
104+
Some(p) => p.is_async,
105+
None => false,
106+
};
107+
if is_async {
108+
return Some(true);
109+
}
110+
111+
if !is_in_pcall_or_xpcall(closure).unwrap_or(false) {
112+
break;
113+
}
114+
}
115+
116+
Some(false)
117+
}
118+
119+
// special case
120+
fn is_in_pcall_or_xpcall(closure: LuaClosureExpr) -> Option<bool> {
121+
let call_expr = closure
122+
.get_parent::<LuaCallArgList>()?
123+
.get_parent::<LuaCallExpr>()?;
124+
let prefix_expr = call_expr.get_prefix_expr()?;
125+
if let LuaExpr::NameExpr(name_expr) = prefix_expr {
126+
let name = name_expr.get_name_text()?;
127+
if name == "pcall" || name == "xpcall" {
128+
return Some(true);
129+
}
130+
}
131+
132+
Some(false)
59133
}

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,37 @@ mod test {
8383
"#
8484
));
8585
}
86+
87+
#[test]
88+
fn test_issue_101() {
89+
let mut ws = crate::VirtualWorkspace::new();
90+
91+
ws.def(
92+
r#"
93+
--- @async
94+
function bar() end
95+
"#,
96+
);
97+
98+
assert!(ws.check_code_for(
99+
DiagnosticCode::AwaitInSync,
100+
r#"
101+
--- @async
102+
function foo()
103+
pcall(function()
104+
bar() -- EXPECTED OK
105+
end)
106+
end
107+
"#
108+
));
109+
110+
assert!(!ws.check_code_for(
111+
DiagnosticCode::AwaitInSync,
112+
r#"
113+
function baz()
114+
pcall(bar)
115+
end
116+
"#
117+
));
118+
}
86119
}

0 commit comments

Comments
 (0)