Skip to content

Commit e423d30

Browse files
committed
fix #534
1 parent 80cb3d4 commit e423d30

File tree

7 files changed

+199
-4
lines changed

7 files changed

+199
-4
lines changed

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ use emmylua_parser::{
55
LuaIndexKey, LuaRepeatStat, LuaSyntaxKind, LuaVarExpr, LuaWhileStat,
66
};
77

8-
use crate::{DiagnosticCode, InferFailReason, LuaMemberKey, LuaType, SemanticModel};
8+
use crate::{
9+
enum_variable_is_param, DiagnosticCode, InferFailReason, LuaMemberKey, LuaType, SemanticModel,
10+
};
911

1012
use super::{humanize_lint_type, Checker, DiagnosticContext};
1113

@@ -144,6 +146,18 @@ fn is_valid_member(
144146
index_key: &LuaIndexKey,
145147
code: DiagnosticCode,
146148
) -> Option<()> {
149+
// 如果类型是 Ref 的 enum, 那么需要检查变量是否为参数, 因为作为参数的 enum 本质上是 value 而不是 enum
150+
if enum_variable_is_param(
151+
semantic_model.get_db(),
152+
&mut semantic_model.get_config().borrow_mut(),
153+
index_expr,
154+
prefix_typ,
155+
)
156+
.is_some()
157+
{
158+
return None;
159+
}
160+
147161
// 检查 member_info
148162
let need_add_diagnostic =
149163
match semantic_model.get_semantic_info(index_expr.syntax().clone().into()) {

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

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,4 +612,49 @@ mod test {
612612
"#
613613
));
614614
}
615+
616+
#[test]
617+
fn test_enum_field_1() {
618+
let mut ws = VirtualWorkspace::new();
619+
ws.def(
620+
r#"
621+
---@enum Enum
622+
local Enum = {
623+
a = 1,
624+
}
625+
"#,
626+
);
627+
assert!(!ws.check_code_for(
628+
DiagnosticCode::UndefinedField,
629+
r#"
630+
---@param p Enum
631+
function func(p)
632+
local x1 = p.a
633+
end
634+
"#
635+
));
636+
637+
assert!(!ws.check_code_for(
638+
DiagnosticCode::UndefinedField,
639+
r#"
640+
---@param p Enum
641+
function func(p)
642+
local x1 = p
643+
local x2 = x1.a
644+
end
645+
"#
646+
));
647+
648+
assert!(!ws.check_code_for(
649+
DiagnosticCode::UndefinedField,
650+
r#"
651+
---@param p Enum
652+
function func(p)
653+
local x1 = p
654+
local x2 = x1
655+
local x3 = x2.a
656+
end
657+
"#
658+
));
659+
}
615660
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
use emmylua_parser::{LuaAstNode, LuaIndexExpr};
2+
use rowan::NodeOrToken;
3+
4+
use crate::{
5+
infer_node_semantic_decl, semantic::semantic_info::infer_token_semantic_decl, DbIndex,
6+
LuaDeclId, LuaInferCache, LuaSemanticDeclId, LuaType, SemanticDeclLevel,
7+
};
8+
9+
pub fn enum_variable_is_param(
10+
db: &DbIndex,
11+
cache: &mut LuaInferCache,
12+
index_expr: &LuaIndexExpr,
13+
prefix_typ: &LuaType,
14+
) -> Option<()> {
15+
fn find_enum_origin(
16+
db: &DbIndex,
17+
cache: &mut LuaInferCache,
18+
decl_id: LuaDeclId,
19+
) -> Option<LuaDeclId> {
20+
let syntax_tree = db.get_vfs().get_syntax_tree(&decl_id.file_id)?;
21+
let root = syntax_tree.get_red_root();
22+
23+
let node = db
24+
.get_decl_index()
25+
.get_decl(&decl_id)?
26+
.get_value_syntax_id()?
27+
.to_node_from_root(&root)?;
28+
29+
let semantic_decl = match node.into() {
30+
NodeOrToken::Node(node) => {
31+
infer_node_semantic_decl(db, cache, node, SemanticDeclLevel::NoTrace)
32+
}
33+
NodeOrToken::Token(token) => {
34+
infer_token_semantic_decl(db, cache, token, SemanticDeclLevel::NoTrace)
35+
}
36+
};
37+
38+
match semantic_decl {
39+
Some(LuaSemanticDeclId::Member(_)) => None,
40+
Some(LuaSemanticDeclId::LuaDecl(new_decl_id)) => {
41+
let decl = db.get_decl_index().get_decl(&new_decl_id)?;
42+
if decl.get_value_syntax_id().is_some() {
43+
Some(find_enum_origin(db, cache, new_decl_id).unwrap_or(new_decl_id))
44+
} else {
45+
Some(new_decl_id)
46+
}
47+
}
48+
_ => None,
49+
}
50+
}
51+
52+
let LuaType::Ref(id) = prefix_typ else {
53+
return None;
54+
};
55+
56+
let type_decl = db.get_type_index().get_type_decl(id)?;
57+
if !type_decl.is_enum() {
58+
return None;
59+
}
60+
61+
let prefix_expr = index_expr.get_prefix_expr()?;
62+
let prefix_decl = infer_node_semantic_decl(
63+
db,
64+
cache,
65+
prefix_expr.syntax().clone(),
66+
SemanticDeclLevel::default(),
67+
)?;
68+
69+
let LuaSemanticDeclId::LuaDecl(decl_id) = prefix_decl else {
70+
return None;
71+
};
72+
73+
let origin_decl_id = find_enum_origin(db, cache, decl_id).unwrap_or(decl_id);
74+
let decl = db.get_decl_index().get_decl(&origin_decl_id)?;
75+
76+
if decl.is_param() {
77+
Some(())
78+
} else {
79+
None
80+
}
81+
}

crates/emmylua_code_analysis/src/semantic/infer/infer_index.rs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use crate::{
1111
DbIndex, LuaGenericType, LuaIntersectionType, LuaMemberKey, LuaObjectType,
1212
LuaOperatorMetaMethod, LuaTupleType, LuaType, LuaTypeDeclId, LuaUnionType,
1313
},
14+
enum_variable_is_param,
1415
semantic::{
1516
generic::{instantiate_type_generic, TypeSubstitutor},
1617
member::get_buildin_type_map_type_id,
@@ -211,15 +212,32 @@ fn infer_custom_type_member(
211212
.ok_or(InferFailReason::None)?;
212213
if type_decl.is_alias() {
213214
if let Some(origin_type) = type_decl.get_alias_origin(db, None) {
214-
return infer_member_by_member_key(db, cache, &origin_type, index_expr, infer_guard);
215+
return infer_member_by_member_key(
216+
db,
217+
cache,
218+
&origin_type,
219+
index_expr.clone(),
220+
infer_guard,
221+
);
215222
} else {
216-
return Err(InferFailReason::None);
223+
return Err(InferFailReason::FieldNotFound);
217224
}
218225
}
226+
match &index_expr {
227+
LuaIndexMemberExpr::IndexExpr(index_expr) => {
228+
if enum_variable_is_param(db, cache, index_expr, &LuaType::Ref(prefix_type_id.clone()))
229+
.is_some()
230+
{
231+
return Err(InferFailReason::None);
232+
}
233+
}
234+
_ => {}
235+
}
219236

220237
let owner = LuaMemberOwner::Type(prefix_type_id.clone());
221238
let index_key = index_expr.get_index_key().ok_or(InferFailReason::None)?;
222239
let key = LuaMemberKey::from_index_key(db, cache, &index_key)?;
240+
223241
if let Some(member_item) = db.get_member_index().get_member_item(&owner, &key) {
224242
return member_item.resolve_type(db);
225243
}

crates/emmylua_code_analysis/src/semantic/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
mod cache;
2+
mod decl;
23
mod generic;
34
mod infer;
45
mod member;
@@ -33,6 +34,7 @@ use semantic_info::{
3334
pub(crate) use type_check::check_type_compact;
3435
use type_check::is_sub_type_of;
3536
use visibility::check_visibility;
37+
pub use decl::enum_variable_is_param;
3638

3739
use crate::{db_index::LuaTypeDeclId, Emmyrc, LuaDocument, LuaSemanticDeclId};
3840
use crate::{

crates/emmylua_ls/src/handlers/completion/providers/member_provider.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use emmylua_code_analysis::{DbIndex, LuaMemberInfo, LuaSemanticDeclId, LuaType, LuaTypeDeclId};
1+
use emmylua_code_analysis::{
2+
enum_variable_is_param, DbIndex, LuaMemberInfo, LuaSemanticDeclId, LuaType, LuaTypeDeclId,
3+
};
24
use emmylua_parser::{LuaAstNode, LuaAstToken, LuaIndexExpr, LuaStringToken};
35

46
use crate::handlers::completion::{
@@ -25,6 +27,16 @@ pub fn add_completion(builder: &mut CompletionBuilder) -> Option<()> {
2527

2628
let prefix_expr = index_expr.get_prefix_expr()?;
2729
let prefix_type = builder.semantic_model.infer_expr(prefix_expr.into()).ok()?;
30+
// 如果是枚举类型且为函数参数, 则不进行补全
31+
if enum_variable_is_param(
32+
builder.semantic_model.get_db(),
33+
&mut builder.semantic_model.get_config().borrow_mut(),
34+
&index_expr,
35+
&prefix_type,
36+
)
37+
.is_some() {
38+
return None;
39+
}
2840
let member_info_map = builder.semantic_model.get_member_info_map(&prefix_type)?;
2941
for (_, member_infos) in member_info_map.iter() {
3042
add_resolve_member_infos(builder, &member_infos, completion_status);

crates/emmylua_ls/src/handlers/test/completion_test.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -729,4 +729,27 @@ mod tests {
729729
CompletionTriggerKind::INVOKED,
730730
));
731731
}
732+
733+
#[test]
734+
fn test_enum_field_1() {
735+
let mut ws = ProviderVirtualWorkspace::new();
736+
ws.def(
737+
r#"
738+
---@enum Enum
739+
local Enum = {
740+
a = 1,
741+
}
742+
"#,
743+
);
744+
assert!(ws.check_completion_with_kind(
745+
r#"
746+
---@param p Enum
747+
function func(p)
748+
local x1 = p.<??>
749+
end
750+
"#,
751+
vec![],
752+
CompletionTriggerKind::TRIGGER_CHARACTER,
753+
));
754+
}
732755
}

0 commit comments

Comments
 (0)