Skip to content

Commit b88f4a5

Browse files
committed
update @export. 支持return {}
1 parent 3ff7636 commit b88f4a5

File tree

11 files changed

+135
-51
lines changed

11 files changed

+135
-51
lines changed

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

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
use crate::{LuaExport, LuaExportScope, LuaNoDiscard, LuaSignatureId};
1+
use crate::{
2+
LuaDeclId, LuaExport, LuaExportScope, LuaNoDiscard, LuaSemanticDeclId, LuaSignatureId,
3+
};
24

35
use super::{
46
tags::{find_owner_closure, get_owner_id},
57
DocAnalyzer,
68
};
79
use emmylua_parser::{
8-
LuaDocDescriptionOwner, LuaDocTagDeprecated, LuaDocTagExport, LuaDocTagNodiscard,
9-
LuaDocTagSource, LuaDocTagVersion, LuaDocTagVisibility,
10+
LuaAst, LuaAstNode, LuaDocDescriptionOwner, LuaDocTagDeprecated, LuaDocTagExport,
11+
LuaDocTagNodiscard, LuaDocTagSource, LuaDocTagVersion, LuaDocTagVisibility, LuaTableExpr,
1012
};
1113

1214
pub fn analyze_visibility(
@@ -112,7 +114,17 @@ pub fn analyze_async(analyzer: &mut DocAnalyzer) -> Option<()> {
112114
}
113115

114116
pub fn analyze_export(analyzer: &mut DocAnalyzer, tag: LuaDocTagExport) -> Option<()> {
115-
let owner_id = get_owner_id(analyzer)?;
117+
let owner = analyzer.comment.get_owner()?;
118+
let owner_id = match owner {
119+
LuaAst::LuaReturnStat(return_stat) => {
120+
let return_table_expr = return_stat.child::<LuaTableExpr>()?;
121+
LuaSemanticDeclId::LuaDecl(LuaDeclId::new(
122+
analyzer.file_id,
123+
return_table_expr.get_position(),
124+
))
125+
}
126+
_ => get_owner_id(analyzer)?,
127+
};
116128

117129
let export_scope = if let Some(scope_text) = tag.get_export_scope() {
118130
match scope_text.as_str() {

crates/emmylua_code_analysis/src/compilation/analyzer/lua/module.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use emmylua_parser::{LuaAstNode, LuaChunk, LuaExpr};
22

33
use crate::{
44
compilation::analyzer::unresolve::UnResolveModule, db_index::LuaType, InferFailReason,
5-
LuaSemanticDeclId, LuaSignatureId,
5+
LuaDeclId, LuaSemanticDeclId, LuaSignatureId,
66
};
77

88
use super::{func_body::analyze_func_body_returns, LuaAnalyzer, LuaReturnPoint};
@@ -67,6 +67,11 @@ fn get_property_owner_id(analyzer: &LuaAnalyzer, expr: LuaExpr) -> Option<LuaSem
6767
LuaExpr::ClosureExpr(closure) => Some(LuaSemanticDeclId::Signature(
6868
LuaSignatureId::from_closure(analyzer.file_id, &closure),
6969
)),
70+
// `return {}`
71+
LuaExpr::TableExpr(table_expr) => Some(LuaSemanticDeclId::LuaDecl(LuaDeclId::new(
72+
analyzer.file_id,
73+
table_expr.get_position(),
74+
))),
7075
_ => None,
7176
}
7277
}

crates/emmylua_code_analysis/src/compilation/test/export_test.rs

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,19 @@ mod test {
88
ws.def_file(
99
"A.lua",
1010
r#"
11-
---@export
12-
local A = {}
1311
14-
return A
12+
---@export
13+
return {
14+
newField = 1
15+
}
1516
"#,
1617
);
1718

1819
ws.def(
1920
r#"
2021
local A = require("A")
21-
A.newField = 1
22-
"#,
23-
);
24-
25-
ws.def(
26-
r#"
27-
E = require("A").newField
28-
22+
A.newField = 2
2923
"#,
3024
);
31-
let res = ws.expr_ty("E");
32-
dbg!(&res);
3325
}
3426
}

crates/emmylua_code_analysis/src/db_index/module/module_info.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use emmylua_parser::{LuaVersionCondition, LuaVersionNumber};
22

3-
use crate::{db_index::LuaType, FileId, LuaSemanticDeclId};
3+
use crate::{db_index::LuaType, DbIndex, FileId, LuaExport, LuaSemanticDeclId};
44

55
use super::{module_node::ModuleNodeId, workspace::WorkspaceId};
66

@@ -36,4 +36,26 @@ impl ModuleInfo {
3636

3737
true
3838
}
39+
40+
pub fn is_export(&self, db: &DbIndex) -> bool {
41+
let Some(property_owner_id) = &self.property_owner_id else {
42+
return false;
43+
};
44+
45+
db.get_property_index()
46+
.get_property(property_owner_id)
47+
.and_then(|property| property.export.as_ref())
48+
.is_some()
49+
}
50+
51+
pub fn get_export<'a>(&self, db: &'a DbIndex) -> Option<&'a LuaExport> {
52+
let property_owner_id = self.property_owner_id.as_ref()?;
53+
let export = db
54+
.get_property_index()
55+
.get_property(property_owner_id)?
56+
.export
57+
.as_ref()?;
58+
59+
Some(export)
60+
}
3961
}

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ fn check_index_expr(
6868
if is_invalid_prefix_type(&prefix_typ) {
6969
if matches!(prefix_typ, LuaType::TableConst(_)) {
7070
// 如果导入了被 @export 标记的表常量, 那么不应该跳过检查
71-
module_info = check_import_table_const(semantic_model, index_expr);
71+
module_info = check_require_table_const_with_export(semantic_model, index_expr);
7272
if module_info.is_none() {
7373
return Some(());
7474
}
@@ -488,7 +488,7 @@ fn check_enum_is_param(
488488
}
489489

490490
/// 检查导入的表常量
491-
fn check_import_table_const<'a>(
491+
fn check_require_table_const_with_export<'a>(
492492
semantic_model: &'a SemanticModel,
493493
index_expr: &LuaIndexExpr,
494494
) -> Option<&'a ModuleInfo> {
@@ -508,5 +508,9 @@ fn check_import_table_const<'a>(
508508
.get_decl_index()
509509
.get_decl(&decl_id)?;
510510

511-
parse_require_module_info(semantic_model, &decl)
511+
let module_info = parse_require_module_info(semantic_model, &decl)?;
512+
if module_info.is_export(semantic_model.get_db()) {
513+
return Some(module_info);
514+
}
515+
None
512516
}

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,4 +180,32 @@ mod test {
180180
"#,
181181
));
182182
}
183+
184+
#[test]
185+
fn test_export_2() {
186+
let mut ws = VirtualWorkspace::new();
187+
ws.def_file(
188+
"a.lua",
189+
r#"
190+
---@export
191+
return {
192+
a = 1
193+
}
194+
"#,
195+
);
196+
assert!(!ws.check_code_for(
197+
DiagnosticCode::InjectField,
198+
r#"
199+
local a = require("a")
200+
a.newField = 1
201+
"#,
202+
));
203+
assert!(ws.check_code_for(
204+
DiagnosticCode::InjectField,
205+
r#"
206+
local a = require("a")
207+
a.a = 2
208+
"#,
209+
));
210+
}
183211
}

crates/emmylua_code_analysis/src/semantic/visibility/export.rs

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
use crate::{LuaExport, LuaExportScope, ModuleInfo, SemanticModel};
1+
use crate::{LuaExportScope, ModuleInfo, SemanticModel};
22

3-
/// 检查模块是否可见, 如果没有 export 标记, 视为可见
3+
/// 检查模块是否可见.
4+
///
5+
/// 如果没有 export 标记, 视为可见.
46
pub fn check_export_visibility(
57
semantic_model: &SemanticModel,
68
module_info: &ModuleInfo,
79
) -> Option<bool> {
810
// 检查模块是否有 export 标记
9-
let Some(export) = get_export(semantic_model, module_info) else {
11+
let Some(export) = module_info.get_export(semantic_model.get_db()) else {
1012
return Some(true);
1113
};
1214

@@ -35,19 +37,3 @@ pub fn check_export_visibility(
3537

3638
Some(false)
3739
}
38-
39-
fn get_export<'a>(
40-
semantic_model: &'a SemanticModel,
41-
module_info: &ModuleInfo,
42-
) -> Option<&'a LuaExport> {
43-
// 检查模块是否有 export 标记
44-
let property_owner_id = module_info.property_owner_id.clone()?;
45-
let export = semantic_model
46-
.get_db()
47-
.get_property_index()
48-
.get_property(&property_owner_id)?
49-
.export
50-
.as_ref()?;
51-
52-
Some(export)
53-
}

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,14 @@ fn try_add_member_completion_items(
127127
position: Position,
128128
completions: &mut Vec<CompletionItem>,
129129
) -> Option<()> {
130+
// 模块必须要有 export 标记
131+
if module_info
132+
.get_export(builder.semantic_model.get_db())
133+
.is_none()
134+
{
135+
return None;
136+
};
137+
130138
if let Some(export_type) = &module_info.export_type {
131139
match export_type {
132140
LuaType::TableConst(_) | LuaType::Def(_) => {

crates/emmylua_ls/src/handlers/semantic_token/build_semantic_tokens.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -583,7 +583,7 @@ fn handle_name_node(
583583
.get_decl_index()
584584
.get_decl(&decl_id)?;
585585
let decl_type = semantic_model.get_type(decl_id.into());
586-
if let Some(true) = check_import_decl(semantic_model, &decl) {
586+
if let Some(true) = check_require_decl(semantic_model, &decl) {
587587
builder.push_with_modifier(
588588
name_token.syntax(),
589589
SemanticTokenType::CLASS,
@@ -706,7 +706,7 @@ fn check_ref_is_require_def(
706706
}
707707

708708
/// 检查是否是导入语句
709-
fn check_import_decl(semantic_model: &SemanticModel, decl: &LuaDecl) -> Option<bool> {
709+
fn check_require_decl(semantic_model: &SemanticModel, decl: &LuaDecl) -> Option<bool> {
710710
let module_info = parse_require_module_info(semantic_model, decl)?;
711711
if check_export_visibility(semantic_model, &module_info).unwrap_or(false) {
712712
return Some(true);

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

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
#[cfg(test)]
22
mod tests {
33

4-
use std::{ops::Deref, sync::Arc};
5-
64
use emmylua_code_analysis::EmmyrcFilenameConvention;
75
use lsp_types::{CompletionItemKind, CompletionTriggerKind};
86

@@ -894,9 +892,9 @@ mod tests {
894892
#[test]
895893
fn test_auto_require() {
896894
let mut ws = ProviderVirtualWorkspace::new();
897-
let mut emmyrc = ws.analysis.emmyrc.deref().clone();
895+
let mut emmyrc = ws.get_emmyrc();
898896
emmyrc.completion.auto_require_naming_convention = EmmyrcFilenameConvention::KeepClass;
899-
ws.analysis.update_config(Arc::new(emmyrc));
897+
ws.update_emmyrc(emmyrc);
900898
ws.def_file(
901899
"map.lua",
902900
r#"
@@ -921,9 +919,6 @@ mod tests {
921919
#[test]
922920
fn test_auto_require_table_field() {
923921
let mut ws = ProviderVirtualWorkspace::new();
924-
let mut emmyrc = ws.analysis.emmyrc.deref().clone();
925-
emmyrc.completion.auto_require_naming_convention = EmmyrcFilenameConvention::KeepClass;
926-
ws.analysis.update_config(Arc::new(emmyrc));
927922
ws.def_file(
928923
"aaaa.lua",
929924
r#"
@@ -1049,4 +1044,26 @@ mod tests {
10491044
CompletionTriggerKind::INVOKED,
10501045
));
10511046
}
1047+
1048+
#[test]
1049+
fn test_auto_require_field_1() {
1050+
let mut ws = ProviderVirtualWorkspace::new();
1051+
// 没有 export 标记, 不允许子字段自动导入
1052+
ws.def_file(
1053+
"AAA.lua",
1054+
r#"
1055+
local function map()
1056+
end
1057+
return {
1058+
map = map,
1059+
}
1060+
"#,
1061+
);
1062+
assert!(ws.check_completion(
1063+
r#"
1064+
map<??>
1065+
"#,
1066+
vec![],
1067+
));
1068+
}
10521069
}

crates/emmylua_ls/src/handlers/test_lib/mod.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use emmylua_code_analysis::{EmmyLuaAnalysis, FileId, VirtualUrlGenerator};
1+
use std::{ops::Deref, sync::Arc};
2+
3+
use emmylua_code_analysis::{EmmyLuaAnalysis, Emmyrc, FileId, VirtualUrlGenerator};
24
use lsp_types::{
35
CodeActionResponse, CompletionItemKind, CompletionResponse, CompletionTriggerKind,
46
GotoDefinitionResponse, Hover, HoverContents, InlayHint, MarkupContent, Position,
@@ -104,6 +106,14 @@ impl ProviderVirtualWorkspace {
104106
file_id
105107
}
106108

109+
pub fn get_emmyrc(&self) -> Emmyrc {
110+
self.analysis.emmyrc.deref().clone()
111+
}
112+
113+
pub fn update_emmyrc(&mut self, emmyrc: Emmyrc) {
114+
self.analysis.update_config(Arc::new(emmyrc));
115+
}
116+
107117
/// 处理文件内容
108118
fn handle_file_content(content: &str) -> Option<(String, Position)> {
109119
let content = content.to_string();

0 commit comments

Comments
 (0)