Skip to content

Commit 57a133d

Browse files
committed
Fix setmetatable
1 parent 32bcbd4 commit 57a133d

8 files changed

Lines changed: 152 additions & 27 deletions

File tree

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,20 @@ pub fn analyze_setmetatable(analyzer: &mut LuaAnalyzer, call_expr: LuaCallExpr)
1414
return Some(());
1515
}
1616

17+
let table = args[0].clone();
1718
let metatable = args[1].clone();
18-
let LuaExpr::TableExpr(table_expr) = metatable else {
19+
let LuaExpr::TableExpr(metatable) = metatable else {
1920
return Some(());
2021
};
2122

2223
let file_id = analyzer.file_id;
23-
let operator_owner = LuaOperatorOwner::Table(InFiled::new(file_id, table_expr.get_range()));
24-
for field in table_expr.get_fields() {
24+
analyzer.db.get_metatable_index_mut().add(
25+
InFiled::new(file_id, table.get_range()),
26+
InFiled::new(file_id, metatable.get_range()),
27+
);
28+
29+
let operator_owner = LuaOperatorOwner::Table(InFiled::new(file_id, metatable.get_range()));
30+
for field in metatable.get_fields() {
2531
analyze_metable_field(analyzer, &field, &operator_owner);
2632
}
2733

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
use std::collections::HashMap;
2+
3+
use rowan::TextRange;
4+
5+
use crate::{FileId, InFiled};
6+
7+
use super::LuaIndex;
8+
9+
#[derive(Debug)]
10+
pub struct LuaMetatableIndex {
11+
pub metatables: HashMap<InFiled<TextRange>, InFiled<TextRange>>,
12+
}
13+
14+
impl LuaMetatableIndex {
15+
pub fn new() -> Self {
16+
Self {
17+
metatables: HashMap::new(),
18+
}
19+
}
20+
21+
pub fn add(&mut self, table: InFiled<TextRange>, metatable: InFiled<TextRange>) {
22+
self.metatables.insert(table, metatable);
23+
}
24+
25+
pub fn get(&self, table: &InFiled<TextRange>) -> Option<&InFiled<TextRange>> {
26+
self.metatables.get(table)
27+
}
28+
}
29+
30+
impl LuaIndex for LuaMetatableIndex {
31+
fn remove(&mut self, file_id: FileId) {
32+
self.metatables.retain(|key, _| key.file_id != file_id);
33+
}
34+
35+
fn clear(&mut self) {
36+
self.metatables.clear();
37+
}
38+
}

crates/emmylua_code_analysis/src/db_index/mod.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ mod dependency;
33
mod diagnostic;
44
mod flow;
55
mod member;
6+
mod metatable;
67
mod module;
78
mod operators;
89
mod property;
@@ -23,6 +24,7 @@ pub use member::{
2324
LuaMember, LuaMemberFeature, LuaMemberId, LuaMemberIndex, LuaMemberIndexItem, LuaMemberKey,
2425
LuaMemberOwner,
2526
};
27+
use metatable::LuaMetatableIndex;
2628
use module::LuaModuleIndex;
2729
pub use module::{ModuleInfo, WorkspaceId};
2830
pub use operators::{
@@ -50,6 +52,7 @@ pub struct DbIndex {
5052
flow_index: LuaFlowIndex,
5153
vfs: Vfs,
5254
file_dependencies_index: LuaDenpendencyIndex,
55+
metatable_index: LuaMetatableIndex,
5356
emmyrc: Arc<Emmyrc>,
5457
}
5558

@@ -69,6 +72,7 @@ impl DbIndex {
6972
flow_index: LuaFlowIndex::new(),
7073
vfs: Vfs::new(),
7174
file_dependencies_index: LuaDenpendencyIndex::new(),
75+
metatable_index: LuaMetatableIndex::new(),
7276
emmyrc: Arc::new(Emmyrc::default()),
7377
}
7478
}
@@ -79,6 +83,14 @@ impl DbIndex {
7983
}
8084
}
8185

86+
pub fn get_metatable_index_mut(&mut self) -> &mut LuaMetatableIndex {
87+
&mut self.metatable_index
88+
}
89+
90+
pub fn get_metatable_index(&self) -> &LuaMetatableIndex {
91+
&self.metatable_index
92+
}
93+
8294
pub fn get_decl_index_mut(&mut self) -> &mut LuaDeclIndex {
8395
&mut self.decl_index
8496
}
@@ -199,6 +211,7 @@ impl LuaIndex for DbIndex {
199211
self.operator_index.remove(file_id);
200212
self.flow_index.remove(file_id);
201213
self.file_dependencies_index.remove(file_id);
214+
self.metatable_index.remove(file_id);
202215
}
203216

204217
fn clear(&mut self) {
@@ -213,5 +226,6 @@ impl LuaIndex for DbIndex {
213226
self.operator_index.clear();
214227
self.flow_index.clear();
215228
self.file_dependencies_index.clear();
229+
self.metatable_index.clear();
216230
}
217231
}

crates/emmylua_code_analysis/src/semantic/infer/infer_call/infer_setmetatable.rs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use emmylua_parser::{LuaAstNode, LuaCallExpr, LuaExpr, LuaIndexKey};
22

33
use crate::{
44
infer_expr, semantic::infer::InferResult, DbIndex, InFiled, InferFailReason, LuaInferCache,
5-
LuaInstanceType, LuaType,
5+
LuaType,
66
};
77

88
pub fn infer_setmetatable_call(
@@ -20,19 +20,17 @@ pub fn infer_setmetatable_call(
2020
let basic_table = args[0].clone();
2121
let metatable = args[1].clone();
2222

23-
let meta_type = infer_metatable_type(db, cache, metatable)?;
23+
let (meta_type, is_index) = infer_metatable_index_type(db, cache, metatable)?;
2424
match &basic_table {
2525
LuaExpr::TableExpr(table_expr) => {
26-
if table_expr.is_empty() {
26+
if table_expr.is_empty() && is_index {
2727
return Ok(meta_type);
28-
} else {
29-
let file_id = cache.get_file_id();
30-
let inst = LuaType::Instance(
31-
LuaInstanceType::new(meta_type, InFiled::new(file_id, basic_table.get_range()))
32-
.into(),
33-
);
34-
return Ok(inst);
3528
}
29+
30+
return Ok(LuaType::TableConst(InFiled::new(
31+
cache.get_file_id(),
32+
table_expr.get_range(),
33+
)));
3634
}
3735
_ => {
3836
if meta_type.is_unknown() {
@@ -44,11 +42,11 @@ pub fn infer_setmetatable_call(
4442
}
4543
}
4644

47-
fn infer_metatable_type(
45+
fn infer_metatable_index_type(
4846
db: &DbIndex,
4947
cache: &mut LuaInferCache,
5048
metatable: LuaExpr,
51-
) -> InferResult {
49+
) -> Result<(LuaType, bool /*__index type*/), InferFailReason> {
5250
match &metatable {
5351
LuaExpr::TableExpr(table) => {
5452
let fields = table.get_fields();
@@ -71,13 +69,15 @@ fn infer_metatable_type(
7169
| LuaExpr::IndexExpr(_)
7270
| LuaExpr::NameExpr(_)
7371
) {
74-
return infer_expr(db, cache, field_value);
72+
let meta_type = infer_expr(db, cache, field_value)?;
73+
return Ok((meta_type, true));
7574
}
7675
}
7776
}
7877
}
7978
_ => {}
8079
};
8180

82-
return infer_expr(db, cache, metatable);
81+
let meta_type = infer_expr(db, cache, metatable)?;
82+
Ok((meta_type, false))
8383
}

crates/emmylua_code_analysis/src/semantic/infer/infer_call/mod.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,19 @@ fn collect_function_type(
138138
infer_guard,
139139
funcs,
140140
)?,
141+
LuaType::TableConst(inst) => {
142+
collect_func_by_table(db, cache, &inst, call_expr.clone(), infer_guard, funcs)?;
143+
}
144+
LuaType::Instance(inst) => {
145+
collect_function_type(
146+
db,
147+
cache,
148+
call_expr,
149+
inst.get_base().clone(),
150+
infer_guard,
151+
funcs,
152+
)?;
153+
}
141154
LuaType::Union(union_types) => {
142155
for sub_type in union_types.get_types() {
143156
collect_function_type(
@@ -455,3 +468,48 @@ fn is_last_call_expr(call_expr: &LuaCallExpr) -> bool {
455468

456469
false
457470
}
471+
472+
fn collect_func_by_table(
473+
db: &DbIndex,
474+
_: &mut LuaInferCache,
475+
table: &InFiled<TextRange>,
476+
_: LuaCallExpr,
477+
_: &mut InferGuard,
478+
funcs: &mut Vec<Arc<LuaFunctionType>>,
479+
) -> Result<(), InferFailReason> {
480+
let metatable = db
481+
.get_metatable_index()
482+
.get(table)
483+
.ok_or(InferFailReason::None)?;
484+
485+
let operator_index = db.get_operator_index();
486+
let operator_ids = operator_index
487+
.get_operators(&metatable.clone().into(), LuaOperatorMetaMethod::Call)
488+
.ok_or(InferFailReason::None)?;
489+
490+
for overload_id in operator_ids {
491+
let operator = operator_index
492+
.get_operator(overload_id)
493+
.ok_or(InferFailReason::None)?;
494+
let func = operator.get_operator_func();
495+
match func {
496+
LuaType::DocFunction(f) => {
497+
funcs.push(f.clone());
498+
}
499+
LuaType::Signature(signature_id) => {
500+
let signature = db
501+
.get_signature_index()
502+
.get(&signature_id)
503+
.ok_or(InferFailReason::None)?;
504+
if !signature.is_resolve_return() {
505+
return Err(InferFailReason::UnResolveSignatureReturn(signature_id));
506+
}
507+
508+
funcs.push(signature.to_call_operator_func_type());
509+
}
510+
_ => {}
511+
}
512+
}
513+
514+
Ok(())
515+
}

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

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ pub fn infer_call_expr_func(
7373
LuaType::Instance(inst) => {
7474
infer_instance_type_doc_function(db, cache, &inst, call_expr, infer_guard, args_count)
7575
}
76-
LuaType::TableConst(meta_table) => infer_metatable_type_doc_function(db, meta_table),
76+
LuaType::TableConst(meta_table) => infer_table_type_doc_function(db, meta_table),
7777
LuaType::Union(union) => {
7878
// 此时我们将其视为泛型实例化联合体
7979
if union.get_types().len() > 1
@@ -325,7 +325,7 @@ fn infer_instance_type_doc_function(
325325
args_count: Option<usize>,
326326
) -> InferCallFuncResult {
327327
let base = instance.get_base();
328-
let metatable = match &base {
328+
let base_table = match &base {
329329
LuaType::TableConst(meta_table) => meta_table.clone(),
330330
LuaType::Instance(inst) => {
331331
return infer_instance_type_doc_function(
@@ -340,14 +340,16 @@ fn infer_instance_type_doc_function(
340340
_ => return Err(InferFailReason::None),
341341
};
342342

343-
infer_metatable_type_doc_function(db, metatable)
343+
infer_table_type_doc_function(db, base_table)
344344
}
345345

346-
fn infer_metatable_type_doc_function(
347-
db: &DbIndex,
348-
meta_table: InFiled<TextRange>,
349-
) -> InferCallFuncResult {
350-
let meta_table_owner = LuaOperatorOwner::Table(meta_table);
346+
fn infer_table_type_doc_function(db: &DbIndex, table: InFiled<TextRange>) -> InferCallFuncResult {
347+
let meta_table = db
348+
.get_metatable_index()
349+
.get(&table)
350+
.ok_or(InferFailReason::None)?;
351+
352+
let meta_table_owner = LuaOperatorOwner::Table(meta_table.clone());
351353

352354
let call_operators = db
353355
.get_operator_index()

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,12 @@ fn infer_member_by_index_table(
406406
table_range: &InFiled<TextRange>,
407407
index_expr: LuaIndexMemberExpr,
408408
) -> InferResult {
409-
let meta_owner = LuaOperatorOwner::Table(table_range.clone());
409+
let metatable = db
410+
.get_metatable_index()
411+
.get(table_range)
412+
.ok_or(InferFailReason::FieldDotFound)?;
413+
414+
let meta_owner = LuaOperatorOwner::Table(metatable.clone());
410415
let operator_ids = db
411416
.get_operator_index()
412417
.get_operators(&meta_owner, LuaOperatorMetaMethod::Index)

crates/emmylua_ls/src/handlers/signature_helper/build_signature_helper.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,9 @@ fn build_table_call_signature_help(
329329
colon_call: bool,
330330
current_idx: usize,
331331
) -> Option<SignatureHelp> {
332-
let operator_owner = LuaOperatorOwner::Table(meta);
332+
let metatable = semantic_model.get_db().get_metatable_index().get(&meta)?;
333+
334+
let operator_owner = LuaOperatorOwner::Table(metatable.clone());
333335
let db = semantic_model.get_db();
334336
let operator_ids = db
335337
.get_operator_index()

0 commit comments

Comments
 (0)