Skip to content

Commit f193a60

Browse files
committed
update attribute constructor
1 parent e642ebf commit f193a60

File tree

5 files changed

+102
-62
lines changed

5 files changed

+102
-62
lines changed

crates/emmylua_code_analysis/resources/std/builtin.lua

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,15 +159,16 @@
159159
--- used to specify the default constructor of a class.
160160
---
161161
--- Parameters:
162-
--- - `name` - The name of the constructor
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.
163164
--- - `strip_self` - Whether the `self` parameter can be omitted when calling the constructor, defaults to `true`
164165
--- - `return_self` - Whether the constructor is forced to return `self`, defaults to `true`
165-
---@attribute constructor(name: string, strip_self: boolean?, return_self: boolean?)
166+
---@attribute constructor(name: string, root_class: string?, strip_self: boolean?, return_self: boolean?)
166167

167168
--- Associates `getter` and `setter` methods with a field. Currently provides only definition navigation functionality,
168169
--- and the target methods must reside within the same class.
169170
---
170-
--- params:
171+
--- Parameters:
171172
--- - convention: Naming convention, defaults to `camelCase`. Implicitly adds `get` and `set` prefixes. eg: `_age` -> `getAge`, `setAge`.
172173
--- - getter: Getter method name. Takes precedence over `convention`.
173174
--- - setter: Setter method name. Takes precedence over `convention`.

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

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,17 @@ pub fn analyze_call(analyzer: &mut LuaAnalyzer, call_expr: LuaCallExpr) -> Optio
1313
};
1414
let signature = analyzer.db.get_signature_index().get(&signature_id)?;
1515
for (idx, param_info) in signature.param_docs.iter() {
16-
if let Some(ref attrs) = param_info.attributes {
17-
for attr in attrs.iter() {
18-
if attr.id.get_name() == "constructor" {
19-
let unresolve = UnResolveConstructor {
20-
file_id: analyzer.file_id,
21-
call_expr: call_expr.clone(),
22-
signature_id,
23-
param_idx: *idx,
24-
};
25-
analyzer
26-
.context
27-
.add_unresolve(unresolve.into(), InferFailReason::None);
28-
return Some(());
29-
}
30-
}
16+
if param_info.get_attribute_by_name("constructor").is_some() {
17+
let unresolve = UnResolveConstructor {
18+
file_id: analyzer.file_id,
19+
call_expr: call_expr.clone(),
20+
signature_id,
21+
param_idx: *idx,
22+
};
23+
analyzer
24+
.context
25+
.add_unresolve(unresolve.into(), InferFailReason::None);
26+
return Some(());
3127
}
3228
}
3329
}

crates/emmylua_code_analysis/src/compilation/analyzer/unresolve/resolve.rs

Lines changed: 77 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -257,58 +257,92 @@ pub fn try_resolve_constructor(
257257
cache: &mut LuaInferCache,
258258
unresolve_constructor: &mut UnResolveConstructor,
259259
) -> ResolveResult {
260-
let signature = db
261-
.get_signature_index()
262-
.get(&unresolve_constructor.signature_id)
263-
.ok_or(InferFailReason::None)?;
264-
let param_info = signature
265-
.get_param_info_by_id(unresolve_constructor.param_idx)
266-
.ok_or(InferFailReason::None)?;
267-
let constructor_use = param_info
268-
.attributes
269-
.iter()
270-
.flatten()
271-
.find(|attr| attr.id.get_name() == "constructor")
272-
.ok_or(InferFailReason::None)?;
273-
let target_signature_type = constructor_use
274-
.args
275-
.first()
276-
.and_then(|(_, typ)| typ.as_ref())
277-
.ok_or(InferFailReason::None)?;
278-
let LuaType::DocStringConst(target_signature_name) = target_signature_type else {
279-
return Err(InferFailReason::None);
280-
};
281-
let target_type_decl_id = get_constructor_target_type(
260+
let (param_type, target_signature_name, root_class, strip_self, return_self) = {
261+
let signature = db
262+
.get_signature_index()
263+
.get(&unresolve_constructor.signature_id)
264+
.ok_or(InferFailReason::None)?;
265+
let param_info = signature
266+
.get_param_info_by_id(unresolve_constructor.param_idx)
267+
.ok_or(InferFailReason::None)?;
268+
let constructor_use = param_info
269+
.get_attribute_by_name("constructor")
270+
.ok_or(InferFailReason::None)?;
271+
272+
// 作为构造函数的方法名
273+
let target_signature_name = constructor_use
274+
.get_param_by_name("name")
275+
.and_then(|typ| match typ {
276+
LuaType::DocStringConst(value) => Some(value.deref().clone()),
277+
_ => None,
278+
})
279+
.ok_or(InferFailReason::None)?;
280+
// 作为构造函数的根类
281+
let root_class =
282+
constructor_use
283+
.get_param_by_name("root_class")
284+
.and_then(|typ| match typ {
285+
LuaType::DocStringConst(value) => Some(value.deref().clone()),
286+
_ => None,
287+
});
288+
// 是否可以省略self参数
289+
let strip_self = constructor_use
290+
.get_param_by_name("strip_self")
291+
.and_then(|typ| match typ {
292+
LuaType::DocBooleanConst(value) => Some(*value),
293+
_ => None,
294+
})
295+
.unwrap_or(true);
296+
// 是否返回self
297+
let return_self = constructor_use
298+
.get_param_by_name("return_self")
299+
.and_then(|typ| match typ {
300+
LuaType::DocBooleanConst(value) => Some(*value),
301+
_ => None,
302+
})
303+
.unwrap_or(true);
304+
305+
Ok::<_, InferFailReason>((
306+
param_info.type_ref.clone(),
307+
target_signature_name,
308+
root_class,
309+
strip_self,
310+
return_self,
311+
))
312+
}?;
313+
314+
// 需要添加构造函数的目标类型
315+
let target_id = get_constructor_target_type(
282316
db,
283317
cache,
284-
&param_info.type_ref,
318+
&param_type,
285319
unresolve_constructor.call_expr.clone(),
286320
unresolve_constructor.param_idx,
287321
)
288322
.ok_or(InferFailReason::None)?;
289-
let target_type = LuaType::Ref(target_type_decl_id);
290-
let member_key = LuaMemberKey::Name(target_signature_name.deref().clone());
323+
324+
// 添加根类
325+
if let Some(root_class) = root_class {
326+
let root_type_id = LuaTypeDeclId::new(&root_class);
327+
if let Some(type_decl) = db.get_type_index().get_type_decl(&root_type_id) {
328+
if type_decl.is_class() {
329+
let root_type = LuaType::Ref(root_type_id.clone());
330+
db.get_type_index_mut().add_super_type(
331+
target_id.clone(),
332+
unresolve_constructor.file_id,
333+
root_type,
334+
);
335+
}
336+
}
337+
}
338+
339+
// 添加构造函数
340+
let target_type = LuaType::Ref(target_id);
341+
let member_key = LuaMemberKey::Name(target_signature_name);
291342
let members =
292343
find_members_with_key(db, &target_type, member_key, false).ok_or(InferFailReason::None)?;
293344
let ctor_signature_member = members.first().ok_or(InferFailReason::None)?;
294-
let strip_self = constructor_use
295-
.args
296-
.get(1)
297-
.and_then(|(_, typ)| typ.as_ref())
298-
.and_then(|typ| match typ {
299-
LuaType::DocBooleanConst(value) => Some(*value),
300-
_ => None,
301-
})
302-
.unwrap_or(true);
303-
let return_self = constructor_use
304-
.args
305-
.get(2)
306-
.and_then(|(_, typ)| typ.as_ref())
307-
.and_then(|typ| match typ {
308-
LuaType::DocBooleanConst(value) => Some(*value),
309-
_ => None,
310-
})
311-
.unwrap_or(true);
345+
312346
set_signature_to_default_call(db, cache, ctor_signature_member, strip_self, return_self)
313347
.ok_or(InferFailReason::None)?;
314348

crates/emmylua_code_analysis/src/db_index/signature/signature.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,15 @@ pub struct LuaDocParamInfo {
185185
pub attributes: Option<Vec<LuaAttributeUse>>,
186186
}
187187

188+
impl LuaDocParamInfo {
189+
pub fn get_attribute_by_name(&self, name: &str) -> Option<&LuaAttributeUse> {
190+
self.attributes
191+
.iter()
192+
.flatten()
193+
.find(|attr| attr.id.get_name() == name)
194+
}
195+
}
196+
188197
#[derive(Debug)]
189198
pub struct LuaDocReturnInfo {
190199
pub name: Option<String>,

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ mod tests {
136136

137137
#[gtest]
138138
fn test_class_call_hint() -> Result<()> {
139-
let mut ws = ProviderVirtualWorkspace::new();
139+
let mut ws = ProviderVirtualWorkspace::new_with_init_std_lib();
140140
ws.def(
141141
r#"
142142
---@generic T

0 commit comments

Comments
 (0)