Skip to content

Commit 2e7b259

Browse files
committed
support variadic generic template param
1 parent efe1607 commit 2e7b259

File tree

15 files changed

+214
-70
lines changed

15 files changed

+214
-70
lines changed

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

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ use std::collections::HashMap;
22

33
use rowan::{TextRange, TextSize};
44

5-
use crate::GenericTplId;
5+
use crate::{GenericParam, GenericTplId};
66

77
#[derive(Debug, Clone)]
88
pub struct FileGenericIndex {
9-
generic_params: Vec<GenericParams>,
9+
generic_params: Vec<TagGenericParams>,
1010
root_node_ids: Vec<GenericEffectId>,
1111
effect_nodes: Vec<GenericEffectRangeNode>,
1212
}
@@ -23,12 +23,12 @@ impl FileGenericIndex {
2323
pub fn add_generic_scope(
2424
&mut self,
2525
ranges: Vec<TextRange>,
26-
params: HashMap<String, usize>,
26+
params: Vec<GenericParam>,
2727
is_func: bool,
2828
) {
2929
let params_id = self.generic_params.len();
3030
self.generic_params
31-
.push(GenericParams::new(params, is_func));
31+
.push(TagGenericParams::new(params, is_func));
3232
let params_id = GenericParamId::new(params_id);
3333
let root_node_ids: Vec<_> = self.root_node_ids.clone();
3434
for range in ranges {
@@ -93,16 +93,18 @@ impl FileGenericIndex {
9393
false
9494
}
9595

96-
pub fn find_generic(&self, position: TextSize, name: &str) -> Option<GenericTplId> {
96+
/// Find generic parameter by position and name.
97+
/// return (GenericTplId, is_variadic)
98+
pub fn find_generic(&self, position: TextSize, name: &str) -> Option<(GenericTplId, bool)> {
9799
let params_ids = self.find_generic_params(position)?;
98100

99101
for params_id in params_ids.iter().rev() {
100102
if let Some(params) = self.generic_params.get(*params_id) {
101-
if let Some(id) = params.params.get(name) {
103+
if let Some((id, is_variadic)) = params.params.get(name) {
102104
if params.is_func {
103-
return Some(GenericTplId::Func(*id as u32));
105+
return Some((GenericTplId::Func(*id as u32), *is_variadic));
104106
} else {
105-
return Some(GenericTplId::Type(*id as u32));
107+
return Some((GenericTplId::Type(*id as u32), *is_variadic));
106108
}
107109
}
108110
}
@@ -177,13 +179,17 @@ impl GenericEffectId {
177179
}
178180

179181
#[derive(Debug, Clone, PartialEq, Eq)]
180-
pub struct GenericParams {
181-
params: HashMap<String, usize>,
182+
pub struct TagGenericParams {
183+
params: HashMap<String, (usize, bool)>, // bool: is_variadic
182184
is_func: bool,
183185
}
184186

185-
impl GenericParams {
186-
pub fn new(params: HashMap<String, usize>, is_func: bool) -> Self {
187+
impl TagGenericParams {
188+
pub fn new(generic_params: Vec<GenericParam>, is_func: bool) -> Self {
189+
let mut params = HashMap::new();
190+
for (i, param) in generic_params.into_iter().enumerate() {
191+
params.insert(param.name.to_string(), (i, param.is_variadic));
192+
}
187193
Self { params, is_func }
188194
}
189195
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,10 +142,12 @@ fn infer_buildin_or_ref_type(
142142
LuaType::Table
143143
}
144144
_ => {
145-
if let Some(tpl_id) = analyzer.generic_index.find_generic(position, name) {
145+
if let Some((tpl_id, is_variadic)) = analyzer.generic_index.find_generic(position, name)
146+
{
146147
return LuaType::TplRef(Arc::new(GenericTpl::new(
147148
tpl_id,
148149
SmolStr::new(name).into(),
150+
is_variadic,
149151
)));
150152
}
151153

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

Lines changed: 20 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
use std::collections::HashMap;
2-
31
use emmylua_parser::{
42
LuaAssignStat, LuaAst, LuaAstNode, LuaAstToken, LuaCommentOwner, LuaDocDescription,
53
LuaDocDescriptionOwner, LuaDocGenericDeclList, LuaDocTagAlias, LuaDocTagClass, LuaDocTagEnum,
64
LuaDocTagGeneric, LuaFuncStat, LuaLocalName, LuaLocalStat, LuaNameExpr, LuaSyntaxId,
75
LuaSyntaxKind, LuaTokenKind, LuaVarExpr,
86
};
97
use rowan::TextRange;
8+
use smol_str::SmolStr;
109

1110
use super::{
1211
DocAnalyzer, infer_type::infer_type, preprocess_description, tags::find_owner_closure,
1312
};
13+
use crate::GenericParam;
1414
use crate::compilation::analyzer::doc::tags::report_orphan_tag;
1515
use crate::{
1616
LuaTypeCache, LuaTypeDeclId,
@@ -30,20 +30,14 @@ pub fn analyze_class(analyzer: &mut DocAnalyzer, tag: LuaDocTagClass) -> Option<
3030
let class_decl_id = class_decl.get_id();
3131
analyzer.current_type_id = Some(class_decl_id.clone());
3232
if let Some(generic_params) = tag.get_generic_decl() {
33-
let params = get_generic_params(analyzer, generic_params);
34-
let mut params_index = HashMap::new();
35-
let mut count = 0;
36-
for (name, _) in params.iter() {
37-
params_index.insert(name.clone(), count);
38-
count += 1;
39-
}
33+
let generic_params = get_generic_params(analyzer, generic_params);
4034

4135
analyzer
4236
.db
4337
.get_type_index_mut()
44-
.add_generic_params(class_decl_id.clone(), params);
38+
.add_generic_params(class_decl_id.clone(), generic_params.clone());
4539

46-
add_generic_index(analyzer, params_index);
40+
add_generic_index(analyzer, generic_params);
4741
}
4842

4943
if let Some(supers) = tag.get_supers() {
@@ -154,22 +148,16 @@ pub fn analyze_alias(analyzer: &mut DocAnalyzer, tag: LuaDocTagAlias) -> Option<
154148
};
155149

156150
if let Some(generic_params) = tag.get_generic_decl_list() {
157-
let params = get_generic_params(analyzer, generic_params);
158-
let mut params_index = HashMap::new();
159-
let mut count = 0;
160-
for (name, _) in params.iter() {
161-
params_index.insert(name.clone(), count);
162-
count += 1;
163-
}
151+
let generic_params = get_generic_params(analyzer, generic_params);
164152

165153
analyzer
166154
.db
167155
.get_type_index_mut()
168-
.add_generic_params(alias_decl_id.clone(), params);
156+
.add_generic_params(alias_decl_id.clone(), generic_params.clone());
169157
let range = analyzer.comment.get_range();
170158
analyzer
171159
.generic_index
172-
.add_generic_scope(vec![range], params_index, false);
160+
.add_generic_scope(vec![range], generic_params, false);
173161
}
174162

175163
let origin_type = infer_type(analyzer, tag.get_type()?);
@@ -189,11 +177,11 @@ pub fn analyze_alias(analyzer: &mut DocAnalyzer, tag: LuaDocTagAlias) -> Option<
189177
fn get_generic_params(
190178
analyzer: &mut DocAnalyzer,
191179
params: LuaDocGenericDeclList,
192-
) -> Vec<(String, Option<LuaType>)> {
180+
) -> Vec<GenericParam> {
193181
let mut params_result = Vec::new();
194182
for param in params.get_generic_decl() {
195183
let name = if let Some(param) = param.get_name_token() {
196-
param.get_name_text().to_string()
184+
SmolStr::new(param.get_name_text())
197185
} else {
198186
continue;
199187
};
@@ -204,13 +192,14 @@ fn get_generic_params(
204192
None
205193
};
206194

207-
params_result.push((name, type_ref));
195+
let is_variadic = param.is_variadic();
196+
params_result.push(GenericParam::new(name, type_ref, is_variadic));
208197
}
209198

210199
params_result
211200
}
212201

213-
fn add_generic_index(analyzer: &mut DocAnalyzer, params_index: HashMap<String, usize>) {
202+
fn add_generic_index(analyzer: &mut DocAnalyzer, generic_params: Vec<GenericParam>) {
214203
let mut ranges = Vec::new();
215204
let range = analyzer.comment.get_range();
216205
ranges.push(range);
@@ -234,7 +223,7 @@ fn add_generic_index(analyzer: &mut DocAnalyzer, params_index: HashMap<String, u
234223

235224
analyzer
236225
.generic_index
237-
.add_generic_scope(ranges, params_index, false);
226+
.add_generic_scope(ranges, generic_params, false);
238227
}
239228

240229
fn get_local_stat_reference_ranges(
@@ -328,10 +317,9 @@ pub fn analyze_func_generic(analyzer: &mut DocAnalyzer, tag: LuaDocTagGeneric) -
328317
report_orphan_tag(analyzer, &tag);
329318
return None;
330319
};
331-
let mut params_result = HashMap::new();
320+
let mut params_result = vec![];
332321
let mut param_info = Vec::new();
333322
if let Some(params_list) = tag.get_generic_decl_list() {
334-
let mut count = 0;
335323
for param in params_list.get_generic_decl() {
336324
let name = if let Some(param) = param.get_name_token() {
337325
param.get_name_text().to_string()
@@ -345,9 +333,12 @@ pub fn analyze_func_generic(analyzer: &mut DocAnalyzer, tag: LuaDocTagGeneric) -
345333
None
346334
};
347335

348-
params_result.insert(name.clone(), count);
336+
params_result.push(GenericParam::new(
337+
SmolStr::new(name.as_str()),
338+
type_ref.clone(),
339+
false,
340+
));
349341
param_info.push((name, type_ref));
350-
count += 1;
351342
}
352343
}
353344

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
use smol_str::SmolStr;
2+
3+
use crate::LuaType;
4+
5+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
6+
pub struct GenericParam {
7+
pub name: SmolStr,
8+
pub type_constraint: Option<LuaType>,
9+
pub is_variadic: bool,
10+
}
11+
12+
impl GenericParam {
13+
pub fn new(name: SmolStr, type_constraint: Option<LuaType>, is_variadic: bool) -> Self {
14+
Self {
15+
name,
16+
type_constraint,
17+
is_variadic,
18+
}
19+
}
20+
}

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

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::{
66
AsyncState, DbIndex, GenericTpl, LuaAliasCallType, LuaFunctionType, LuaGenericType,
77
LuaInstanceType, LuaIntersectionType, LuaMemberKey, LuaMemberOwner, LuaObjectType,
88
LuaSignatureId, LuaStringTplType, LuaTupleType, LuaType, LuaTypeDeclId, LuaUnionType,
9-
VariadicType,
9+
TypeSubstitutor, VariadicType,
1010
};
1111

1212
use super::{LuaAliasCallKind, LuaMultiLineUnion};
@@ -125,7 +125,13 @@ fn humanize_def_type(db: &DbIndex, id: &LuaTypeDeclId, level: RenderLevel) -> St
125125

126126
let generic_names = generic
127127
.iter()
128-
.map(|it| it.0.clone())
128+
.map(|it| {
129+
if it.is_variadic {
130+
format!("{}...", it.name)
131+
} else {
132+
it.name.to_string()
133+
}
134+
})
129135
.collect::<Vec<_>>()
130136
.join(", ");
131137
format!("{}<{}>", full_name, generic_names)
@@ -488,14 +494,28 @@ fn humanize_generic_type(db: &DbIndex, generic: &LuaGenericType, level: RenderLe
488494

489495
let full_name = type_decl.get_full_name();
490496

491-
let generic_params = generic
497+
let generic_inst_params = generic
492498
.get_params()
493499
.iter()
494500
.map(|ty| humanize_type(db, ty, level.next_level()))
495501
.collect::<Vec<_>>()
496502
.join(",");
497503

498-
format!("{}<{}>", full_name, generic_params)
504+
let generic_base = format!("{}<{}>", full_name, generic_inst_params);
505+
if (level == RenderLevel::Detailed || level == RenderLevel::Documentation)
506+
&& type_decl.is_alias()
507+
{
508+
let substituor = TypeSubstitutor::from_type_array(generic.get_params().clone());
509+
if let Some(origin_type) = type_decl.get_alias_origin(db, Some(&substituor)) {
510+
// prevent infinite recursion
511+
if origin_type.is_function() {
512+
let origin_type_str = humanize_type(db, &origin_type, level);
513+
return format!("{} = {}", generic_base, origin_type_str);
514+
}
515+
}
516+
}
517+
518+
generic_base
499519
}
500520

501521
fn humanize_table_const_type_detail_and_simple(

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

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
mod generic_param;
12
mod humanize_type;
23
mod test;
34
mod type_decl;
@@ -8,6 +9,7 @@ mod types;
89

910
use super::traits::LuaIndex;
1011
use crate::{DbIndex, FileId, InFiled};
12+
pub use generic_param::GenericParam;
1113
pub use humanize_type::{RenderLevel, format_union_type, humanize_type};
1214
use std::collections::{HashMap, HashSet};
1315
pub use type_decl::{
@@ -24,7 +26,7 @@ pub struct LuaTypeIndex {
2426
file_using_namespace: HashMap<FileId, Vec<String>>,
2527
file_types: HashMap<FileId, Vec<LuaTypeDeclId>>,
2628
full_name_type_map: HashMap<LuaTypeDeclId, LuaTypeDecl>,
27-
generic_params: HashMap<LuaTypeDeclId, Vec<(String, Option<LuaType>)>>,
29+
generic_params: HashMap<LuaTypeDeclId, Vec<GenericParam>>,
2830
supers: HashMap<LuaTypeDeclId, Vec<InFiled<LuaType>>>,
2931
types: HashMap<LuaTypeOwner, LuaTypeCache>,
3032
in_filed_type_owner: HashMap<FileId, HashSet<LuaTypeOwner>>,
@@ -161,18 +163,11 @@ impl LuaTypeIndex {
161163
result
162164
}
163165

164-
pub fn add_generic_params(
165-
&mut self,
166-
decl_id: LuaTypeDeclId,
167-
params: Vec<(String, Option<LuaType>)>,
168-
) {
166+
pub fn add_generic_params(&mut self, decl_id: LuaTypeDeclId, params: Vec<GenericParam>) {
169167
self.generic_params.insert(decl_id, params);
170168
}
171169

172-
pub fn get_generic_params(
173-
&self,
174-
decl_id: &LuaTypeDeclId,
175-
) -> Option<&Vec<(String, Option<LuaType>)>> {
170+
pub fn get_generic_params(&self, decl_id: &LuaTypeDeclId) -> Option<&Vec<GenericParam>> {
176171
self.generic_params.get(decl_id)
177172
}
178173

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

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1291,17 +1291,29 @@ impl GenericTplId {
12911291
pub fn is_type(&self) -> bool {
12921292
matches!(self, GenericTplId::Type(_))
12931293
}
1294+
1295+
pub fn with_idx(&self, idx: u32) -> Self {
1296+
match self {
1297+
GenericTplId::Type(_) => GenericTplId::Type(idx),
1298+
GenericTplId::Func(_) => GenericTplId::Func(idx),
1299+
}
1300+
}
12941301
}
12951302

12961303
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
12971304
pub struct GenericTpl {
12981305
tpl_id: GenericTplId,
12991306
name: ArcIntern<SmolStr>,
1307+
is_variadic: bool,
13001308
}
13011309

13021310
impl GenericTpl {
1303-
pub fn new(tpl_id: GenericTplId, name: ArcIntern<SmolStr>) -> Self {
1304-
Self { tpl_id, name }
1311+
pub fn new(tpl_id: GenericTplId, name: ArcIntern<SmolStr>, is_variadic: bool) -> Self {
1312+
Self {
1313+
tpl_id,
1314+
name,
1315+
is_variadic,
1316+
}
13051317
}
13061318

13071319
pub fn get_tpl_id(&self) -> GenericTplId {
@@ -1311,6 +1323,10 @@ impl GenericTpl {
13111323
pub fn get_name(&self) -> &str {
13121324
&self.name
13131325
}
1326+
1327+
pub fn is_variadic(&self) -> bool {
1328+
self.is_variadic
1329+
}
13141330
}
13151331

13161332
#[derive(Debug, Clone, Hash, PartialEq, Eq)]

0 commit comments

Comments
 (0)