Skip to content

Commit e780561

Browse files
authored
Merge pull request #155 from xuhuanzy/diagnostic
diagnostic
2 parents b8bdcb4 + 56b8847 commit e780561

File tree

18 files changed

+317
-25
lines changed

18 files changed

+317
-25
lines changed

crates/emmylua_code_analysis/locales/lint.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,16 @@ Annotations specify that at least %{min} return value(s) are required, found %{r
113113
en: 'Annotations specify that at least %{min} return value(s) are required, found %{rmin} returned here instead.'
114114
zh_CN: '至少需要 %{min} 个返回值,但此处只返回了 %{rmin} 个'
115115
zh_HK: '至少需要 %{min} 個回傳值,但此處只返回 %{rmin} 個'
116+
Cannot use `...` outside a vararg function.:
117+
en: Cannot use `...` outside a vararg function.
118+
zh_CN: '不能在非可变参数函数中使用 `...`'
119+
zh_HK: '不能在非可變參數函數中使用 `...`'
120+
'Undefined doc param: `%{name}`':
121+
en: 'Undefined doc param: `%{name}`'
122+
zh_CN: '指向了未定义的参数 `%{name}`'
123+
zh_HK: '指向了未定義的參數 `%{name}`'
124+
'Undefined field: `%{name}`':
125+
en: 'Undefined field: `%{name}`'
126+
zh_CN: '未定义字段 `%{name}`'
127+
zh_HK: '未定義字段 `%{name}`'
128+

crates/emmylua_code_analysis/src/diagnostic/checker/missing_return_value.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,21 @@ fn check_return_stat(
2828

2929
let signature_id = LuaSignatureId::from_closure(semantic_model.get_file_id(), &closure_expr);
3030
let signature = context.db.get_signature_index().get(&signature_id)?;
31-
let return_types = signature.get_return_types();
31+
let min_return_types = signature
32+
.get_return_types()
33+
.iter()
34+
.filter(|ty| !ty.is_optional())
35+
.cloned()
36+
.collect::<Vec<_>>();
3237

33-
// 如果没有返回值注解, 则不检查
3438
if signature.resolve_return != SignatureReturnStatus::DocResolve {
35-
return Some(());
39+
return None;
3640
}
3741

38-
let disable_return_count_check = return_types.iter().any(|ty| ty.is_variadic());
42+
let disable_return_count_check = min_return_types.iter().any(|ty| ty.is_variadic());
3943

4044
let expr_return_len = return_stat.get_expr_list().collect::<Vec<_>>().len();
41-
let return_types_len = return_types.len();
45+
let return_types_len = min_return_types.len();
4246
if !disable_return_count_check && expr_return_len < return_types_len {
4347
context.add_diagnostic(
4448
DiagnosticCode::MissingReturnValue,

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@ mod redundant_parameter;
1313
mod redundant_return_value;
1414
mod return_type_mismatch;
1515
mod syntax_error;
16+
mod undefined_doc_param;
1617
mod undefined_global;
1718
mod unused;
1819

20+
use emmylua_parser::{LuaAstNode, LuaClosureExpr, LuaComment, LuaStat, LuaSyntaxKind};
1921
use lsp_types::{Diagnostic, DiagnosticSeverity, DiagnosticTag, NumberOrString};
2022
use rowan::TextRange;
2123
use std::sync::Arc;
@@ -57,6 +59,7 @@ pub fn check_file(context: &mut DiagnosticContext, semantic_model: &SemanticMode
5759
check!(redundant_return_value);
5860
check!(missing_return_value);
5961
check!(return_type_mismatch);
62+
check!(undefined_doc_param);
6063

6164
Some(())
6265
}
@@ -203,3 +206,18 @@ impl<'a> DiagnosticContext<'a> {
203206
is_code_default_enable(&code)
204207
}
205208
}
209+
210+
pub fn get_closure_expr_comment(closure_expr: &LuaClosureExpr) -> Option<LuaComment> {
211+
let comment = closure_expr
212+
.ancestors::<LuaStat>()
213+
.next()?
214+
.syntax()
215+
.prev_sibling()?;
216+
match comment.kind().into() {
217+
LuaSyntaxKind::Comment => {
218+
let comment = LuaComment::cast(comment)?;
219+
Some(comment)
220+
}
221+
_ => None,
222+
}
223+
}

crates/emmylua_code_analysis/src/diagnostic/checker/redundant_return_value.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@ fn check_return_stat(
2828
let signature = context.db.get_signature_index().get(&signature_id)?;
2929
let return_types = signature.get_return_types();
3030

31-
// 如果没有返回值注解, 则不检查
3231
if signature.resolve_return != SignatureReturnStatus::DocResolve {
33-
return Some(());
32+
return None;
3433
}
34+
3535
let disable_return_count_check = return_types.iter().any(|ty| ty.is_variadic());
3636
let expr_return_len = return_stat.get_expr_list().collect::<Vec<_>>().len();
3737
let return_types_len = return_types.len();

crates/emmylua_code_analysis/src/diagnostic/checker/return_type_mismatch.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,8 @@ fn check_return_stat(
3333
let signature_id = LuaSignatureId::from_closure(semantic_model.get_file_id(), &closure_expr);
3434
let signature = context.db.get_signature_index().get(&signature_id)?;
3535
let return_types = signature.get_return_types();
36-
// 如果没有返回值注解, 则不检查
3736
if signature.resolve_return != SignatureReturnStatus::DocResolve {
38-
return Some(());
37+
return None;
3938
}
4039

4140
for (index, expr) in return_stat.get_expr_list().enumerate() {

crates/emmylua_code_analysis/src/diagnostic/checker/syntax_error.rs

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
use emmylua_parser::{
2-
float_token_value, int_token_value, LuaAstNode, LuaSyntaxToken, LuaTokenKind,
2+
float_token_value, int_token_value, LuaAstNode, LuaClosureExpr, LuaLiteralExpr, LuaSyntaxKind,
3+
LuaSyntaxToken, LuaTokenKind,
34
};
45

5-
use crate::{DiagnosticCode, SemanticModel};
6+
use crate::{DiagnosticCode, LuaSignatureId, SemanticModel};
67

78
use super::DiagnosticContext;
89

9-
pub const CODES: &[DiagnosticCode] = &[DiagnosticCode::SyntaxError];
10+
pub const CODES: &[DiagnosticCode] = &[DiagnosticCode::SyntaxError, DiagnosticCode::LuaSyntaxError];
1011

1112
pub fn check(context: &mut DiagnosticContext, semantic_model: &SemanticModel) -> Option<()> {
1213
if let Some(parse_errors) = semantic_model.get_file_parse_error() {
@@ -54,6 +55,9 @@ pub fn check(context: &mut DiagnosticContext, semantic_model: &SemanticModel) ->
5455
);
5556
}
5657
}
58+
LuaTokenKind::TkDots => {
59+
check_dots_literal_error(context, semantic_model, &token);
60+
}
5761
_ => {}
5862
}
5963
}
@@ -149,3 +153,32 @@ fn check_normal_string_error(string_token: &LuaSyntaxToken) -> Result<(), String
149153
}
150154
Ok(())
151155
}
156+
157+
fn check_dots_literal_error(
158+
context: &mut DiagnosticContext,
159+
semantic_model: &SemanticModel,
160+
dots_token: &LuaSyntaxToken,
161+
) -> Option<()> {
162+
if let Some(literal_expr) = dots_token.parent() {
163+
match literal_expr.kind().into() {
164+
LuaSyntaxKind::LiteralExpr => {
165+
let literal_expr = LuaLiteralExpr::cast(literal_expr)?;
166+
let closure_expr = literal_expr.ancestors::<LuaClosureExpr>().next()?;
167+
let signature_id =
168+
LuaSignatureId::from_closure(semantic_model.get_file_id(), &closure_expr);
169+
let signature = context.db.get_signature_index().get(&signature_id)?;
170+
if !signature.params.iter().any(|param| param == "...") {
171+
context.add_diagnostic(
172+
DiagnosticCode::LuaSyntaxError,
173+
literal_expr.get_range(),
174+
t!("Cannot use `...` outside a vararg function.").to_string(),
175+
None,
176+
);
177+
}
178+
}
179+
_ => {}
180+
}
181+
}
182+
183+
Some(())
184+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
use emmylua_parser::{LuaAstNode, LuaAstToken, LuaClosureExpr, LuaDocTagParam};
2+
3+
use crate::{DiagnosticCode, LuaSignatureId, SemanticModel};
4+
5+
use super::{get_closure_expr_comment, DiagnosticContext};
6+
7+
pub const CODES: &[DiagnosticCode] = &[DiagnosticCode::UndefinedDocParam];
8+
9+
pub fn check(context: &mut DiagnosticContext, semantic_model: &SemanticModel) -> Option<()> {
10+
let root = semantic_model.get_root().clone();
11+
for return_stat in root.descendants::<LuaClosureExpr>() {
12+
check_doc_param(context, semantic_model, &return_stat);
13+
}
14+
Some(())
15+
}
16+
17+
fn check_doc_param(
18+
context: &mut DiagnosticContext,
19+
semantic_model: &SemanticModel,
20+
closure_expr: &LuaClosureExpr,
21+
) -> Option<()> {
22+
let signature_id = LuaSignatureId::from_closure(semantic_model.get_file_id(), &closure_expr);
23+
let signature = context.db.get_signature_index().get(&signature_id)?;
24+
25+
get_closure_expr_comment(closure_expr)?
26+
.children::<LuaDocTagParam>()
27+
.for_each(|tag| {
28+
if let Some(name_token) = tag.get_name_token() {
29+
let info = signature.get_param_info_by_name(&name_token.get_name_text());
30+
if info.is_none() {
31+
context.add_diagnostic(
32+
DiagnosticCode::UndefinedDocParam,
33+
name_token.get_range(),
34+
t!(
35+
"Undefined doc param: `%{name}`",
36+
name = name_token.get_name_text()
37+
)
38+
.to_string(),
39+
None,
40+
);
41+
}
42+
}
43+
});
44+
Some(())
45+
}

crates/emmylua_code_analysis/src/diagnostic/lua_diagnostic_code.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ use serde::{Deserialize, Serialize};
1010
pub enum DiagnosticCode {
1111
/// Syntax error
1212
SyntaxError,
13+
/// Lua syntax error
14+
LuaSyntaxError,
1315
/// Type not found
1416
TypeNotFound,
1517
/// Missing return statement
@@ -64,6 +66,10 @@ pub enum DiagnosticCode {
6466
MissingReturnValue,
6567
/// Redundant return value
6668
RedundantReturnValue,
69+
/// Undefined Doc Param
70+
UndefinedDocParam,
71+
/// Duplicate doc field
72+
DuplicateDocField,
6773

6874
#[serde(other)]
6975
None,
@@ -97,7 +103,7 @@ pub fn is_code_default_enable(code: &DiagnosticCode) -> bool {
97103
match code {
98104
DiagnosticCode::InjectFieldFail => false,
99105
DiagnosticCode::DisableGlobalDefine => false,
100-
DiagnosticCode::UndefinedField => false,
106+
// DiagnosticCode::UndefinedField => false,
101107
DiagnosticCode::IterVariableReassign => false,
102108
DiagnosticCode::CodeStyleCheck => false,
103109
// ... handle other variants
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#[cfg(test)]
2+
mod test {
3+
// use crate::{DiagnosticCode, VirtualWorkspace};
4+
5+
// #[test]
6+
// fn test() {
7+
// let mut ws = VirtualWorkspace::new();
8+
9+
// assert!(!ws.check_code_for(
10+
// DiagnosticCode::DuplicateDocField,
11+
// r#"
12+
// ---@class (partial) Test
13+
// ---@field name string
14+
// ---@field name string
15+
// local Test = {}
16+
// "#
17+
// ));
18+
// }
19+
}

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,20 @@ mod tests {
2727
"#
2828
));
2929
}
30+
31+
#[test]
32+
fn test_missing_return_value_variadic() {
33+
let mut ws = VirtualWorkspace::new();
34+
35+
assert!(ws.check_code_for(
36+
DiagnosticCode::MissingReturnValue,
37+
r#"
38+
--- @return integer?
39+
--- @return integer?
40+
function bar()
41+
return
42+
end
43+
"#
44+
));
45+
}
3046
}

0 commit comments

Comments
 (0)