Skip to content

Commit 0216bbf

Browse files
committed
update attribute: rename lsp_perf_optim to lsp_optimization, add delayed_definition
1 parent 0bd02ef commit 0216bbf

File tree

8 files changed

+110
-36
lines changed

8 files changed

+110
-36
lines changed

CHANGELOG.md

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,17 @@
1212
### ✨ Added
1313
- **Attribute**: 实现了新的特性`---@attribute`,用于定义附加元数据,内置多个特性:
1414
```lua
15+
1516
--- Deprecated. Receives an optional message parameter.
1617
---@attribute deprecated(message: string?)
1718

18-
--- Language Server Performance Optimization Items.
19+
--- Language Server Optimization Items.
1920
---
20-
--- Receives a parameter, the options are:
21-
--- - `check_table_field` - Skip the assign check for table fields. It is recommended to use this option for all large configuration tables.
22-
---@attribute lsp_perf_optim(code: "check_table_field"|string)
21+
--- Parameters:
22+
--- - `check_table_field`: Skip the assign check for table fields. It is recommended to use this option for all large configuration tables.
23+
--- - `delayed_definition`: Indicates that the type of the variable is determined by the first assignment.
24+
--- Only valid for `local` declarations with no initial value.
25+
---@attribute lsp_optimization(code: "check_table_field"|"delayed_definition")
2326

2427
--- Index field alias, will be displayed in `hint` and `completion`.
2528
---
@@ -30,19 +33,19 @@
3033
--- used to specify the default constructor of a class.
3134
---
3235
--- Parameters:
33-
--- - `name` - The name of the method as a constructor.
34-
--- - `root_class` - Used to mark the root class, will implicitly inherit this class, such as `System.Object` in c#. Defaults to empty.
35-
--- - `strip_self` - Whether the `self` parameter can be omitted when calling the constructor, defaults to `true`
36-
--- - `return_self` - Whether the constructor is forced to return `self`, defaults to `true`
36+
--- - `name`: The name of the method as a constructor.
37+
--- - `root_class`: Used to mark the root class, will implicitly inherit this class, such as `System.Object` in c#. Defaults to empty.
38+
--- - `strip_self`: Whether the `self` parameter can be omitted when calling the constructor, defaults to `true`
39+
--- - `return_self`: Whether the constructor is forced to return `self`, defaults to `true`
3740
---@attribute constructor(name: string, root_class: string?, strip_self: boolean?, return_self: boolean?)
3841

3942
--- Associates `getter` and `setter` methods with a field. Currently provides only definition navigation functionality,
4043
--- and the target methods must reside within the same class.
4144
---
4245
--- Parameters:
43-
--- - convention: Naming convention, defaults to `camelCase`. Implicitly adds `get` and `set` prefixes. eg: `_age` -> `getAge`, `setAge`.
44-
--- - getter: Getter method name. Takes precedence over `convention`.
45-
--- - setter: Setter method name. Takes precedence over `convention`.
46+
--- - `convention`: Naming convention, defaults to `camelCase`. Implicitly adds `get` and `set` prefixes. eg: `_age` -> `getAge`, `setAge`.
47+
--- - `getter`: Getter method name. Takes precedence over `convention`.
48+
--- - `setter`: Setter method name. Takes precedence over `convention`.
4649
---@attribute field_accessor(convention: "camelCase"|"PascalCase"|"snake_case"|nil, getter: string?, setter: string?)
4750
```
4851

crates/emmylua_code_analysis/resources/std/builtin.lua

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -144,11 +144,13 @@
144144
--- Deprecated. Receives an optional message parameter.
145145
---@attribute deprecated(message: string?)
146146

147-
--- Language Server Performance Optimization Items.
147+
--- Language Server Optimization Items.
148148
---
149-
--- Receives a parameter, the options are:
150-
--- - `check_table_field` - Skip the assign check for table fields. It is recommended to use this option for all large configuration tables.
151-
---@attribute lsp_perf_optim(code: "check_table_field"|string)
149+
--- Parameters:
150+
--- - `check_table_field`: Skip the assign check for table fields. It is recommended to use this option for all large configuration tables.
151+
--- - `delayed_definition`: Indicates that the type of the variable is determined by the first assignment.
152+
--- Only valid for `local` declarations with no initial value.
153+
---@attribute lsp_optimization(code: "check_table_field"|"delayed_definition")
152154

153155
--- Index field alias, will be displayed in `hint` and `completion`.
154156
---
@@ -159,17 +161,17 @@
159161
--- used to specify the default constructor of a class.
160162
---
161163
--- Parameters:
162-
--- - `name` - The name of the method as a constructor.
163-
--- - `root_class` - Used to mark the root class, will implicitly inherit this class, such as `System.Object` in c#. Defaults to empty.
164-
--- - `strip_self` - Whether the `self` parameter can be omitted when calling the constructor, defaults to `true`
165-
--- - `return_self` - Whether the constructor is forced to return `self`, defaults to `true`
164+
--- - `name`: The name of the method as a constructor.
165+
--- - `root_class`: Used to mark the root class, will implicitly inherit this class, such as `System.Object` in c#. Defaults to empty.
166+
--- - `strip_self`: Whether the `self` parameter can be omitted when calling the constructor, defaults to `true`
167+
--- - `return_self`: Whether the constructor is forced to return `self`, defaults to `true`
166168
---@attribute constructor(name: string, root_class: string?, strip_self: boolean?, return_self: boolean?)
167169

168170
--- Associates `getter` and `setter` methods with a field. Currently provides only definition navigation functionality,
169171
--- and the target methods must reside within the same class.
170172
---
171173
--- Parameters:
172-
--- - convention: Naming convention, defaults to `camelCase`. Implicitly adds `get` and `set` prefixes. eg: `_age` -> `getAge`, `setAge`.
173-
--- - getter: Getter method name. Takes precedence over `convention`.
174-
--- - setter: Setter method name. Takes precedence over `convention`.
174+
--- - `convention`: Naming convention, defaults to `camelCase`. Implicitly adds `get` and `set` prefixes. eg: `_age` -> `getAge`, `setAge`.
175+
--- - `getter`: Getter method name. Takes precedence over `convention`.
176+
--- - `setter`: Setter method name. Takes precedence over `convention`.
175177
---@attribute field_accessor(convention: "camelCase"|"PascalCase"|"snake_case"|nil, getter: string?, setter: string?)

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

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
use emmylua_parser::{
22
BinaryOperator, LuaAssignStat, LuaAstNode, LuaExpr, LuaFuncStat, LuaIndexExpr,
3-
LuaLocalFuncStat, LuaLocalStat, LuaTableField, LuaVarExpr, PathTrait,
3+
LuaLocalFuncStat, LuaLocalStat, LuaNameExpr, LuaTableField, LuaVarExpr, PathTrait,
44
};
55

66
use crate::{
7-
InFiled, InferFailReason, LuaTypeCache, LuaTypeOwner,
7+
InFiled, InferFailReason, LuaSemanticDeclId, LuaTypeCache, LuaTypeOwner,
88
compilation::analyzer::{
99
common::{add_member, bind_type},
1010
unresolve::{UnResolveDecl, UnResolveMember},
@@ -23,6 +23,10 @@ pub fn analyze_local_stat(analyzer: &mut LuaAnalyzer, local_stat: LuaLocalStat)
2323
for local_name in name_list {
2424
let position = local_name.get_position();
2525
let decl_id = LuaDeclId::new(analyzer.file_id, position);
26+
// 标记了延迟定义属性, 此时将跳过绑定类型, 等待第一次赋值时再绑定类型
27+
if has_delayed_definition_attribute(analyzer, decl_id) {
28+
return Some(());
29+
}
2630
analyzer
2731
.db
2832
.get_type_index_mut()
@@ -304,6 +308,17 @@ pub fn analyze_assign_stat(analyzer: &mut LuaAnalyzer, assign_stat: LuaAssignSta
304308
continue;
305309
}
306310
};
311+
312+
// 如果具有延迟定义属性, 则先绑定最初的定义
313+
if let LuaVarExpr::NameExpr(name_expr) = var {
314+
if let Some(decl_id) = get_delayed_definition_decl_id(analyzer, name_expr) {
315+
bind_type(
316+
analyzer.db,
317+
decl_id.into(),
318+
LuaTypeCache::InferType(expr_type.clone()),
319+
);
320+
}
321+
}
307322
assign_merge_type_owner_and_expr_type(analyzer, type_owner, &expr_type, 0);
308323
}
309324

@@ -500,3 +515,37 @@ fn special_assign_pattern(
500515

501516
Some(())
502517
}
518+
519+
fn has_delayed_definition_attribute(analyzer: &LuaAnalyzer, decl_id: LuaDeclId) -> bool {
520+
if let Some(property) = analyzer
521+
.db
522+
.get_property_index()
523+
.get_property(&LuaSemanticDeclId::LuaDecl(decl_id))
524+
{
525+
if let Some(lsp_optimization) = property.find_attribute_use("lsp_optimization") {
526+
if let Some(LuaType::DocStringConst(code)) = lsp_optimization.get_param_by_name("code")
527+
{
528+
if code.as_ref() == "delayed_definition" {
529+
return true;
530+
}
531+
};
532+
}
533+
}
534+
false
535+
}
536+
537+
// 获取延迟定义的声明id
538+
fn get_delayed_definition_decl_id(
539+
analyzer: &LuaAnalyzer,
540+
name_expr: &LuaNameExpr,
541+
) -> Option<LuaDeclId> {
542+
let file_id = analyzer.file_id;
543+
let references_index = analyzer.db.get_reference_index();
544+
let range = name_expr.get_range();
545+
let file_ref = references_index.get_local_reference(&file_id)?;
546+
let decl_id = file_ref.get_decl_id(&range)?;
547+
if !has_delayed_definition_attribute(analyzer, decl_id) {
548+
return None;
549+
}
550+
Some(decl_id)
551+
}

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

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,31 @@ mod test {
3636
ws.check_code_for(
3737
DiagnosticCode::AssignTypeMismatch,
3838
r#"
39-
---@[lsp_perf_optim("check_table_field")]
39+
---@[lsp_optimization("check_table_field")]
4040
local config = {}
4141
"#,
4242
);
4343
}
44+
45+
#[test]
46+
fn test_delayed_definition() {
47+
let mut ws = VirtualWorkspace::new_with_init_std_lib();
48+
49+
ws.def(
50+
r#"
51+
---@[lsp_optimization("delayed_definition")]
52+
local config
53+
54+
function func()
55+
A = config
56+
end
57+
58+
config = 1
59+
"#,
60+
);
61+
62+
let ty = ws.expr_ty("A");
63+
let ty_desc = ws.humanize_type(ty);
64+
assert_eq!(ty_desc, "integer");
65+
}
4466
}

crates/emmylua_code_analysis/src/db_index/property/property.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,11 +110,11 @@ impl LuaCommonProperty {
110110
self.attribute_uses.as_ref()
111111
}
112112

113-
pub fn find_attribute_use(&self, id: LuaTypeDeclId) -> Option<&LuaAttributeUse> {
113+
pub fn find_attribute_use(&self, id: &str) -> Option<&LuaAttributeUse> {
114114
self.attribute_uses.as_ref().and_then(|attribute_uses| {
115115
attribute_uses
116116
.iter()
117-
.find(|attribute_use| attribute_use.id == id)
117+
.find(|attribute_use| attribute_use.id.get_name() == id)
118118
})
119119
}
120120
}

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

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ use rowan::{NodeOrToken, TextRange};
88

99
use crate::{
1010
DiagnosticCode, LuaDeclExtra, LuaDeclId, LuaMemberKey, LuaSemanticDeclId, LuaType,
11-
LuaTypeDeclId, SemanticDeclLevel, SemanticModel, TypeCheckFailReason, TypeCheckResult,
12-
VariadicType, infer_index_expr,
11+
SemanticDeclLevel, SemanticModel, TypeCheckFailReason, TypeCheckResult, VariadicType,
12+
infer_index_expr,
1313
};
1414

1515
use super::{Checker, DiagnosticContext, humanize_lint_type};
@@ -219,11 +219,9 @@ pub fn check_table_expr(
219219
.get_property_index()
220220
.get_property(&semantic_decl)
221221
{
222-
if let Some(lsp_perf_optim) =
223-
property.find_attribute_use(LuaTypeDeclId::new("lsp_perf_optim"))
224-
{
222+
if let Some(lsp_optimization) = property.find_attribute_use("lsp_optimization") {
225223
if let Some(LuaType::DocStringConst(code)) =
226-
lsp_perf_optim.get_param_by_name("code")
224+
lsp_optimization.get_param_by_name("code")
227225
{
228226
if code.as_ref() == "check_table_field" {
229227
return Some(false);

crates/emmylua_ls/src/handlers/completion/add_completions/add_member_completion.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use emmylua_code_analysis::{
2-
DbIndex, LuaMemberInfo, LuaMemberKey, LuaSemanticDeclId, LuaType, LuaTypeDeclId, SemanticModel,
2+
DbIndex, LuaMemberInfo, LuaMemberKey, LuaSemanticDeclId, LuaType, SemanticModel,
33
try_extract_signature_id_from_field,
44
};
55
use emmylua_parser::{
@@ -360,7 +360,7 @@ pub fn get_index_alias_name(
360360
};
361361

362362
let alias_label = common_property
363-
.find_attribute_use(LuaTypeDeclId::new("index_alias"))?
363+
.find_attribute_use("index_alias")?
364364
.args
365365
.first()
366366
.and_then(|(_, typ)| typ.as_ref())

crates/emmylua_ls/src/handlers/definition/goto_def_definition.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@ fn try_set_accessor_locations(
473473
.get_property_index()
474474
.get_property(&semantic_decl_id)?;
475475

476-
let attribute_use = property.find_attribute_use(LuaTypeDeclId::new("field_accessor"))?;
476+
let attribute_use = property.find_attribute_use("field_accessor")?;
477477
let has_getter =
478478
if let Some(LuaType::DocStringConst(getter)) = attribute_use.get_param_by_name("getter") {
479479
try_add_accessor_location(

0 commit comments

Comments
 (0)