Skip to content

Commit a2d4878

Browse files
committed
update infer generic constraint
1 parent 1d50c1a commit a2d4878

File tree

6 files changed

+143
-239
lines changed

6 files changed

+143
-239
lines changed

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -292,19 +292,19 @@ pub enum SignatureReturnStatus {
292292
#[derive(Debug, Clone)]
293293
pub struct LuaGenericParamInfo {
294294
pub name: String,
295-
pub type_constraint: Option<LuaType>,
295+
pub constraint: Option<LuaType>,
296296
pub attributes: Option<Vec<LuaAttributeUse>>,
297297
}
298298

299299
impl LuaGenericParamInfo {
300300
pub fn new(
301301
name: String,
302-
type_constraint: Option<LuaType>,
302+
constraint: Option<LuaType>,
303303
attributes: Option<Vec<LuaAttributeUse>>,
304304
) -> Self {
305305
Self {
306306
name,
307-
type_constraint,
307+
constraint,
308308
attributes,
309309
}
310310
}

crates/emmylua_code_analysis/src/diagnostic/checker/generic/generic_constraint_mismatch.rs

Lines changed: 85 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
1-
use emmylua_parser::{LuaAst, LuaAstNode, LuaCallExpr, LuaDocTagType};
1+
use emmylua_parser::{LuaAst, LuaAstNode, LuaAstToken, LuaCallExpr, LuaDocTagType};
22
use rowan::TextRange;
33

44
use crate::diagnostic::{checker::Checker, lua_diagnostic::DiagnosticContext};
55
use crate::semantic::{
66
CallConstraintContext, build_call_constraint_context, normalize_constraint_type,
77
};
88
use crate::{
9-
DiagnosticCode, DocTypeInferContext, GenericTplId, LuaMemberOwner, LuaSemanticDeclId,
10-
LuaSignature, LuaStringTplType, LuaType, RenderLevel, SemanticDeclLevel, SemanticModel,
11-
TypeCheckFailReason, TypeCheckResult, TypeSubstitutor, humanize_type, infer_doc_type,
12-
instantiate_type_generic,
9+
DiagnosticCode, DocTypeInferContext, LuaStringTplType, LuaType, RenderLevel, SemanticModel,
10+
TypeCheckFailReason, TypeCheckResult, TypeSubstitutor, VariadicType, humanize_type,
11+
infer_doc_type, instantiate_type_generic,
1312
};
1413

1514
pub struct GenericConstraintMismatchChecker;
@@ -38,47 +37,72 @@ fn check_call_expr(
3837
semantic_model: &SemanticModel,
3938
call_expr: LuaCallExpr,
4039
) -> Option<()> {
41-
let function = semantic_model
42-
.infer_expr(call_expr.get_prefix_expr()?.clone())
43-
.ok()?;
44-
if let LuaType::Signature(signature_id) = function {
45-
let signature = semantic_model
46-
.get_db()
47-
.get_signature_index()
48-
.get(&signature_id)?;
49-
let Some(CallConstraintContext {
40+
let Some((
41+
CallConstraintContext {
5042
params,
5143
arg_infos,
5244
substitutor,
53-
}) = build_call_constraint_context(semantic_model, &call_expr, signature)
54-
else {
55-
return Some(());
56-
};
45+
},
46+
doc_func,
47+
)) = build_call_constraint_context(semantic_model, &call_expr)
48+
else {
49+
return Some(());
50+
};
5751

58-
for (i, (_, param_type)) in params.iter().enumerate() {
59-
let param_type = if let Some(param_type) = param_type {
60-
param_type
61-
} else {
62-
continue;
63-
};
52+
let mut arg_ranges = collect_arg_ranges(semantic_model, &call_expr);
53+
if call_expr.is_colon_call() && !doc_func.is_colon_define() {
54+
let colon_range = call_expr.get_colon_token()?.get_range();
55+
arg_ranges.insert(0, colon_range);
56+
}
6457

65-
check_param(
66-
context,
67-
semantic_model,
68-
&call_expr,
69-
i,
70-
param_type,
71-
signature,
72-
&arg_infos,
73-
false,
74-
&substitutor,
75-
);
76-
}
58+
for (i, (_, param_type)) in params.iter().enumerate() {
59+
let param_type = if let Some(param_type) = param_type {
60+
param_type
61+
} else {
62+
continue;
63+
};
64+
65+
check_param(
66+
context,
67+
semantic_model,
68+
&call_expr,
69+
i,
70+
param_type,
71+
&arg_infos,
72+
&arg_ranges,
73+
false,
74+
&substitutor,
75+
);
7776
}
7877

7978
Some(())
8079
}
8180

81+
fn collect_arg_ranges(semantic_model: &SemanticModel, call_expr: &LuaCallExpr) -> Vec<TextRange> {
82+
let Some(arg_list) = call_expr.get_args_list() else {
83+
return Vec::new();
84+
};
85+
let arg_exprs = arg_list.get_args().collect::<Vec<_>>();
86+
let mut ranges = Vec::new();
87+
for expr in arg_exprs {
88+
let expr_type = semantic_model
89+
.infer_expr(expr.clone())
90+
.unwrap_or(LuaType::Unknown);
91+
match expr_type {
92+
LuaType::Variadic(variadic) => match variadic.as_ref() {
93+
VariadicType::Base(_) => ranges.push(expr.get_range()),
94+
VariadicType::Multi(values) => {
95+
for _ in values {
96+
ranges.push(expr.get_range());
97+
}
98+
}
99+
},
100+
_ => ranges.push(expr.get_range()),
101+
}
102+
}
103+
ranges
104+
}
105+
82106
fn check_doc_tag_type(
83107
context: &mut DiagnosticContext,
84108
semantic_model: &SemanticModel,
@@ -122,31 +146,20 @@ fn check_param(
122146
call_expr: &LuaCallExpr,
123147
param_index: usize,
124148
param_type: &LuaType,
125-
signature: &LuaSignature,
126-
arg_infos: &[(LuaType, TextRange)],
149+
arg_infos: &[LuaType],
150+
arg_ranges: &[TextRange],
127151
from_union: bool,
128152
substitutor: &TypeSubstitutor,
129153
) -> Option<()> {
130154
// 应该先通过泛型体操约束到唯一类型再进行检查
131155
match param_type {
132156
LuaType::StrTplRef(str_tpl_ref) => {
133-
let extend_type = str_tpl_ref
134-
.get_constraint()
135-
.cloned()
136-
.or_else(|| {
137-
get_extend_type(
138-
semantic_model,
139-
call_expr,
140-
str_tpl_ref.get_tpl_id(),
141-
signature,
142-
)
143-
})
144-
.map(|ty| {
145-
normalize_constraint_type(
146-
semantic_model.get_db(),
147-
instantiate_type_generic(semantic_model.get_db(), &ty, substitutor),
148-
)
149-
});
157+
let extend_type = str_tpl_ref.get_constraint().cloned().map(|ty| {
158+
normalize_constraint_type(
159+
semantic_model.get_db(),
160+
instantiate_type_generic(semantic_model.get_db(), &ty, substitutor),
161+
)
162+
});
150163
let arg_expr = call_expr.get_args_list()?.get_args().nth(param_index)?;
151164
let arg_type = semantic_model.infer_expr(arg_expr.clone()).ok()?;
152165

@@ -164,24 +177,15 @@ fn check_param(
164177
);
165178
}
166179
LuaType::TplRef(tpl_ref) | LuaType::ConstTplRef(tpl_ref) => {
167-
let extend_type = tpl_ref
168-
.get_constraint()
169-
.cloned()
170-
.or_else(|| {
171-
get_extend_type(semantic_model, call_expr, tpl_ref.get_tpl_id(), signature)
172-
})
173-
.map(|ty| {
174-
normalize_constraint_type(
175-
semantic_model.get_db(),
176-
instantiate_type_generic(semantic_model.get_db(), &ty, substitutor),
177-
)
178-
});
179-
validate_tpl_ref(
180-
context,
181-
semantic_model,
182-
&extend_type,
183-
arg_infos.get(param_index),
184-
);
180+
let extend_type = tpl_ref.get_constraint().cloned().map(|ty| {
181+
normalize_constraint_type(
182+
semantic_model.get_db(),
183+
instantiate_type_generic(semantic_model.get_db(), &ty, substitutor),
184+
)
185+
});
186+
let arg_type = arg_infos.get(param_index);
187+
let arg_range = arg_ranges.get(param_index).copied();
188+
validate_tpl_ref(context, semantic_model, &extend_type, arg_type, arg_range);
185189
}
186190
LuaType::Union(union_type) => {
187191
// 如果不是来自 union, 才展开 union 中的每个类型进行检查
@@ -193,8 +197,8 @@ fn check_param(
193197
call_expr,
194198
param_index,
195199
union_member_type,
196-
signature,
197200
arg_infos,
201+
arg_ranges,
198202
true,
199203
substitutor,
200204
);
@@ -206,45 +210,6 @@ fn check_param(
206210
Some(())
207211
}
208212

209-
fn get_extend_type(
210-
semantic_model: &SemanticModel,
211-
call_expr: &LuaCallExpr,
212-
tpl_id: GenericTplId,
213-
signature: &LuaSignature,
214-
) -> Option<LuaType> {
215-
match tpl_id {
216-
GenericTplId::Func(tpl_id) => signature
217-
.generic_params
218-
.get(tpl_id as usize)?
219-
.type_constraint
220-
.clone(),
221-
GenericTplId::Type(tpl_id) => {
222-
let prefix_expr = call_expr.get_prefix_expr()?;
223-
let semantic_decl = semantic_model.find_decl(
224-
prefix_expr.syntax().clone().into(),
225-
SemanticDeclLevel::default(),
226-
)?;
227-
let member_index = semantic_model.get_db().get_member_index();
228-
match semantic_decl {
229-
LuaSemanticDeclId::Member(member_id) => {
230-
let owner = member_index.get_current_owner(&member_id)?;
231-
match owner {
232-
LuaMemberOwner::Type(type_id) => {
233-
let generic_params = semantic_model
234-
.get_db()
235-
.get_type_index()
236-
.get_generic_params(type_id)?;
237-
generic_params.get(tpl_id as usize)?.type_constraint.clone()
238-
}
239-
_ => None,
240-
}
241-
}
242-
_ => None,
243-
}
244-
}
245-
}
246-
}
247-
248213
fn validate_str_tpl_ref(
249214
context: &mut DiagnosticContext,
250215
semantic_model: &SemanticModel,
@@ -309,18 +274,20 @@ fn validate_tpl_ref(
309274
context: &mut DiagnosticContext,
310275
semantic_model: &SemanticModel,
311276
extend_type: &Option<LuaType>,
312-
arg_info: Option<&(LuaType, TextRange)>,
277+
arg_type: Option<&LuaType>,
278+
range: Option<TextRange>,
313279
) -> Option<()> {
314280
let extend_type = extend_type.clone()?;
315-
let arg_info = arg_info?;
316-
let result = semantic_model.type_check_detail(&extend_type, &arg_info.0);
281+
let arg_type = arg_type?;
282+
let range = range?;
283+
let result = semantic_model.type_check_detail(&extend_type, arg_type);
317284
if result.is_err() {
318285
add_type_check_diagnostic(
319286
context,
320287
semantic_model,
321-
arg_info.1,
288+
range,
322289
&extend_type,
323-
&arg_info.0,
290+
arg_type,
324291
result,
325292
);
326293
}

0 commit comments

Comments
 (0)