Skip to content

Commit b8b76e3

Browse files
committed
impl instantiate LuaMappedType
1 parent fdedb3f commit b8b76e3

File tree

9 files changed

+217
-32
lines changed

9 files changed

+217
-32
lines changed

.vscode/settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33
"files.insertFinalNewline": true,
44
"files.trimFinalNewlines": true,
55
"files.trimTrailingWhitespace": true,
6-
"files.trimTrailingWhitespaceInRegexAndStrings": true
6+
"files.trimTrailingWhitespaceInRegexAndStrings": true,
7+
"rust-analyzer.check.command": "clippy",
78
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ impl FileGenericIndex {
5555
}
5656
}
5757

58-
fn get_start(&self, ranges: &Vec<TextRange>) -> Option<usize> {
58+
fn get_start(&self, ranges: &[TextRange]) -> Option<usize> {
5959
let params_ids = self.find_generic_params(ranges.first()?.start())?;
6060
let mut start = 0;
6161
for params_id in params_ids.iter() {
@@ -163,8 +163,8 @@ impl FileGenericIndex {
163163
}
164164

165165
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
166-
struct GenericParamId {
167-
id: usize,
166+
pub struct GenericParamId {
167+
pub id: usize,
168168
}
169169

170170
impl GenericParamId {

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ fn infer_buildin_or_ref_type(
138138
let position = range.start();
139139
match name {
140140
"unknown" => LuaType::Unknown,
141+
"never" => LuaType::Never,
141142
"nil" | "void" => LuaType::Nil,
142143
"any" => LuaType::Any,
143144
"userdata" => LuaType::Userdata,
@@ -704,19 +705,21 @@ fn infer_mapped_type(
704705
let constraint = generic_decl
705706
.get_type()
706707
.map(|constraint| infer_type(analyzer, constraint));
707-
let param = GenericParam::new(SmolStr::new(&name), constraint, generic_decl.is_variadic());
708+
let param = GenericParam::new(SmolStr::new(name), constraint, generic_decl.is_variadic());
708709

709710
analyzer.generic_index.add_generic_scope(
710711
vec![mapped_type.get_range()],
711712
vec![param.clone()],
712713
false,
713714
);
715+
let position = mapped_type.get_range().start();
716+
let id = analyzer.generic_index.find_generic(position, name)?.0;
714717

715718
let index_access = mapped_type.get_index_access()?;
716719
let value_type = infer_index_access_type(analyzer, &index_access);
717720

718721
Some(LuaType::Mapped(
719-
LuaMappedType::new(param, value_type).into(),
722+
LuaMappedType::new((id, param), value_type).into(),
720723
))
721724
}
722725

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ use crate::{
1717
use emmylua_parser::{LuaAstNode, LuaComment, LuaSyntaxNode};
1818
use file_generic_index::FileGenericIndex;
1919
use tags::get_owner_id;
20-
2120
pub struct DocAnalysisPipeline;
2221

2322
impl AnalysisPipeline for DocAnalysisPipeline {

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,5 +370,13 @@ mod test {
370370
end
371371
"#,
372372
);
373+
assert!(ws.check_code_for(
374+
DiagnosticCode::ParamTypeNotMatch,
375+
r#"
376+
---@type Pick1<{name: string, age: number, email: string}, "name" | "age">
377+
local m
378+
accept(m)
379+
"#,
380+
));
373381
}
374382
}

crates/emmylua_code_analysis/src/db_index/type/types.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1533,12 +1533,12 @@ impl From<LuaConditionalType> for LuaType {
15331533

15341534
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
15351535
pub struct LuaMappedType {
1536-
pub param: GenericParam,
1536+
pub param: (GenericTplId, GenericParam),
15371537
pub value: LuaType,
15381538
}
15391539

15401540
impl LuaMappedType {
1541-
pub fn new(param: GenericParam, value_type: LuaType) -> Self {
1541+
pub fn new(param: (GenericTplId, GenericParam), value_type: LuaType) -> Self {
15421542
Self {
15431543
param,
15441544
value: value_type,

crates/emmylua_code_analysis/src/semantic/generic/instantiate_special_generic.rs

Lines changed: 88 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::{
44
DbIndex, LuaAliasCallKind, LuaAliasCallType, LuaMemberKey, LuaType, TypeOps, VariadicType,
55
get_member_map,
66
semantic::{
7+
generic::instantiate_type_generic::key_type_to_member_key,
78
member::{find_members, infer_raw_member_type},
89
type_check,
910
},
@@ -28,14 +29,14 @@ pub fn instantiate_alias_call(
2829
return LuaType::Unknown;
2930
}
3031
// 如果类型为`Union`且只有一个类型, 则会解开`Union`包装
31-
return TypeOps::Remove.apply(db, &operands[0], &operands[1]);
32+
TypeOps::Remove.apply(db, &operands[0], &operands[1])
3233
}
3334
LuaAliasCallKind::Add => {
3435
if operands.len() != 2 {
3536
return LuaType::Unknown;
3637
}
3738

38-
return TypeOps::Union.apply(db, &operands[0], &operands[1]);
39+
TypeOps::Union.apply(db, &operands[0], &operands[1])
3940
}
4041
LuaAliasCallKind::KeyOf => {
4142
if operands.len() != 1 {
@@ -52,37 +53,40 @@ pub fn instantiate_alias_call(
5253
})
5354
.collect::<Vec<_>>();
5455

55-
return LuaType::from_vec(member_key_types);
56+
LuaType::from_vec(member_key_types)
5657
}
58+
// 条件类型不在此处理
5759
LuaAliasCallKind::Extends => {
5860
if operands.len() != 2 {
5961
return LuaType::Unknown;
6062
}
6163

6264
let compact = type_check::check_type_compact(db, &operands[0], &operands[1]).is_ok();
63-
return LuaType::BooleanConst(compact);
65+
LuaType::BooleanConst(compact)
6466
}
6567
LuaAliasCallKind::Select => {
6668
if operands.len() != 2 {
6769
return LuaType::Unknown;
6870
}
6971

70-
return instantiate_select_call(&operands[0], &operands[1]);
71-
}
72-
LuaAliasCallKind::Unpack => {
73-
return instantiate_unpack_call(db, &operands);
72+
instantiate_select_call(&operands[0], &operands[1])
7473
}
74+
LuaAliasCallKind::Unpack => instantiate_unpack_call(db, &operands),
7575
LuaAliasCallKind::RawGet => {
7676
if operands.len() != 2 {
7777
return LuaType::Unknown;
7878
}
7979

80-
return instantiate_rawget_call(db, &operands[0], &operands[1]);
80+
instantiate_rawget_call(db, &operands[0], &operands[1])
8181
}
82-
_ => {}
83-
}
82+
LuaAliasCallKind::Index => {
83+
if operands.len() != 2 {
84+
return LuaType::Unknown;
85+
}
8486

85-
LuaType::Unknown
87+
instantiate_index_call(db, &operands[0], &operands[1])
88+
}
89+
}
8690
}
8791

8892
enum NumOrLen {
@@ -250,3 +254,75 @@ fn instantiate_rawget_call(db: &DbIndex, owner: &LuaType, key: &LuaType) -> LuaT
250254

251255
infer_raw_member_type(db, owner, &member_key).unwrap_or(LuaType::Unknown)
252256
}
257+
258+
fn instantiate_index_call(db: &DbIndex, owner: &LuaType, key: &LuaType) -> LuaType {
259+
match owner {
260+
LuaType::Union(union) => {
261+
let results = union
262+
.into_vec()
263+
.iter()
264+
.map(|member| instantiate_index_call(db, member, key))
265+
.collect::<Vec<_>>();
266+
return LuaType::from_vec(results);
267+
}
268+
LuaType::Intersection(intersection) => {
269+
let results = intersection
270+
.get_types()
271+
.iter()
272+
.map(|member| instantiate_index_call(db, member, key))
273+
.collect::<Vec<_>>();
274+
return LuaType::from_vec(results);
275+
}
276+
LuaType::Variadic(variadic) => match variadic.deref() {
277+
VariadicType::Base(base) => {
278+
return instantiate_index_call(db, base, key);
279+
}
280+
VariadicType::Multi(types) => {
281+
let results = types
282+
.iter()
283+
.map(|member| instantiate_index_call(db, member, key))
284+
.collect::<Vec<_>>();
285+
return LuaType::from_vec(results);
286+
}
287+
},
288+
_ => {}
289+
}
290+
291+
match key {
292+
LuaType::Union(union) => {
293+
let results = union
294+
.into_vec()
295+
.iter()
296+
.map(|member| instantiate_index_call(db, owner, member))
297+
.collect::<Vec<_>>();
298+
return LuaType::from_vec(results);
299+
}
300+
LuaType::MultiLineUnion(multi) => {
301+
let results = multi
302+
.get_unions()
303+
.iter()
304+
.map(|(member, _)| instantiate_index_call(db, owner, member))
305+
.collect::<Vec<_>>();
306+
return LuaType::from_vec(results);
307+
}
308+
LuaType::Variadic(variadic) => match variadic.deref() {
309+
VariadicType::Base(base) => {
310+
return instantiate_index_call(db, owner, base);
311+
}
312+
VariadicType::Multi(types) => {
313+
let results = types
314+
.iter()
315+
.map(|member| instantiate_index_call(db, owner, member))
316+
.collect::<Vec<_>>();
317+
return LuaType::from_vec(results);
318+
}
319+
},
320+
_ => {}
321+
}
322+
323+
if let Some(member_key) = key_type_to_member_key(key) {
324+
infer_raw_member_type(db, owner, &member_key).unwrap_or(LuaType::Unknown)
325+
} else {
326+
LuaType::Unknown
327+
}
328+
}

crates/emmylua_code_analysis/src/semantic/generic/instantiate_type_generic.rs

Lines changed: 103 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
use std::{
2-
collections::{HashMap, HashSet},
2+
collections::{HashMap, HashSet, hash_map::Entry},
33
ops::Deref,
44
};
55

66
use crate::{
7-
DbIndex, GenericTpl, GenericTplId, LuaArrayType, LuaSignatureId, LuaTupleStatus,
7+
DbIndex, GenericTpl, GenericTplId, LuaArrayType, LuaMemberKey, LuaSignatureId, LuaTupleStatus,
88
check_type_compact,
99
db_index::{
1010
LuaAliasCallKind, LuaConditionalType, LuaFunctionType, LuaGenericType, LuaIntersectionType,
11-
LuaObjectType, LuaTupleType, LuaType, LuaUnionType, VariadicType,
11+
LuaMappedType, LuaObjectType, LuaTupleType, LuaType, LuaUnionType, VariadicType,
1212
},
1313
};
1414

@@ -42,6 +42,7 @@ pub fn instantiate_type_generic(
4242
LuaType::Call(alias_call) => instantiate_alias_call(db, alias_call, substitutor),
4343
LuaType::Variadic(variadic) => instantiate_variadic_type(db, variadic, substitutor),
4444
LuaType::Conditional(conditional) => instantiate_conditional(db, conditional, substitutor),
45+
LuaType::Mapped(mapped) => instantiate_mapped_type(db, mapped.deref(), substitutor),
4546
LuaType::SelfInfer => {
4647
if let Some(typ) = substitutor.get_self_type() {
4748
typ.clone()
@@ -710,3 +711,102 @@ fn resolve_infer_tpl_ids(
710711

711712
map
712713
}
714+
715+
fn instantiate_mapped_type(
716+
db: &DbIndex,
717+
mapped: &LuaMappedType,
718+
substitutor: &TypeSubstitutor,
719+
) -> LuaType {
720+
let constraint = mapped
721+
.param
722+
.1
723+
.type_constraint
724+
.as_ref()
725+
.map(|ty| instantiate_type_generic(db, ty, substitutor));
726+
727+
if let Some(constraint_ty) = constraint {
728+
let mut key_types = Vec::new();
729+
collect_mapped_key_atoms(&constraint_ty, &mut key_types);
730+
731+
let mut visited = HashSet::new();
732+
let mut fields: HashMap<LuaMemberKey, LuaType> = HashMap::new();
733+
let mut index_access: Vec<(LuaType, LuaType)> = Vec::new();
734+
735+
for key_ty in key_types {
736+
if !visited.insert(key_ty.clone()) {
737+
continue;
738+
}
739+
740+
let value_ty =
741+
instantiate_mapped_value(db, substitutor, &mapped.value, mapped.param.0, &key_ty);
742+
743+
if let Some(member_key) = key_type_to_member_key(&key_ty) {
744+
match fields.entry(member_key) {
745+
Entry::Occupied(mut entry) => {
746+
let merged = LuaType::from_vec(vec![entry.get().clone(), value_ty]);
747+
entry.insert(merged);
748+
}
749+
Entry::Vacant(entry) => {
750+
entry.insert(value_ty);
751+
}
752+
}
753+
} else {
754+
index_access.push((key_ty, value_ty));
755+
}
756+
}
757+
758+
if !fields.is_empty() || !index_access.is_empty() {
759+
return LuaType::Object(LuaObjectType::new_with_fields(fields, index_access).into());
760+
}
761+
}
762+
763+
instantiate_type_generic(db, &mapped.value, substitutor)
764+
}
765+
766+
fn instantiate_mapped_value(
767+
db: &DbIndex,
768+
substitutor: &TypeSubstitutor,
769+
value: &LuaType,
770+
tpl_id: GenericTplId,
771+
replacement: &LuaType,
772+
) -> LuaType {
773+
let mut local_substitutor = substitutor.clone();
774+
local_substitutor.insert_type(tpl_id, replacement.clone());
775+
776+
instantiate_type_generic(db, value, &local_substitutor)
777+
}
778+
779+
pub(super) fn key_type_to_member_key(key_ty: &LuaType) -> Option<LuaMemberKey> {
780+
match key_ty {
781+
LuaType::DocStringConst(s) => Some(LuaMemberKey::Name(s.deref().clone())),
782+
LuaType::StringConst(s) => Some(LuaMemberKey::Name(s.deref().clone())),
783+
LuaType::DocIntegerConst(i) => Some(LuaMemberKey::Integer(*i)),
784+
LuaType::IntegerConst(i) => Some(LuaMemberKey::Integer(*i)),
785+
_ => None,
786+
}
787+
}
788+
789+
fn collect_mapped_key_atoms(key_ty: &LuaType, acc: &mut Vec<LuaType>) {
790+
match key_ty {
791+
LuaType::Union(union) => {
792+
for member in union.into_vec() {
793+
collect_mapped_key_atoms(&member, acc);
794+
}
795+
}
796+
LuaType::MultiLineUnion(multi) => {
797+
for (member, _) in multi.get_unions() {
798+
collect_mapped_key_atoms(member, acc);
799+
}
800+
}
801+
LuaType::Variadic(variadic) => match variadic.deref() {
802+
VariadicType::Base(base) => collect_mapped_key_atoms(base, acc),
803+
VariadicType::Multi(types) => {
804+
for member in types {
805+
collect_mapped_key_atoms(member, acc);
806+
}
807+
}
808+
},
809+
LuaType::Unknown => {}
810+
_ => acc.push(key_ty.clone()),
811+
}
812+
}

crates/emmylua_parser/src/grammar/doc/types.rs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -528,14 +528,12 @@ fn parse_suffixed_type(p: &mut LuaDocParser, cm: CompleteMarker) -> DocParseResu
528528
m.set_kind(p, LuaSyntaxKind::TypeIndexAccess);
529529
parse_type(p)?;
530530
}
531-
} else {
532-
if matches!(
533-
p.current_token(),
534-
LuaTokenKind::TkString | LuaTokenKind::TkInt | LuaTokenKind::TkName
535-
) {
536-
m.set_kind(p, LuaSyntaxKind::IndexExpr);
537-
p.bump();
538-
}
531+
} else if matches!(
532+
p.current_token(),
533+
LuaTokenKind::TkString | LuaTokenKind::TkInt | LuaTokenKind::TkName
534+
) {
535+
m.set_kind(p, LuaSyntaxKind::IndexExpr);
536+
p.bump();
539537
}
540538

541539
expect_token(p, LuaTokenKind::TkRightBracket)?;

0 commit comments

Comments
 (0)