Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion crates/emmylua_code_analysis/resources/std/builtin.lua
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@
--- @alias std.RawGet<T, K> unknown

---
--- built-in type for generic template, for match integer const and true/false
--- built-in type for generic template, used for const generic
--- @alias std.ConstTpl<T> unknown

--- compact luals
Expand Down Expand Up @@ -167,6 +167,13 @@
--- Extract from T those types that are assignable to U
--- @alias Extract<T, U> T extends U and T or never

---
--- From T, pick a set of properties whose keys are in the union K
--- @alias Pick<T, K extends keyof T> {[P in K]: T[P]; }

---
--- Construct a type with the properties of T except for those in type K.
--- @alias Omit<T, K extends keyof any> Pick<T, Exclude<keyof T, K>>

--- attribute

Expand Down
2 changes: 1 addition & 1 deletion crates/emmylua_code_analysis/resources/std/global.lua
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ function xpcall(f, msgh, ...) end
--- @generic T, Start: integer, End: integer
--- @param i? std.ConstTpl<Start>
--- @param j? std.ConstTpl<End>
--- @param list T
--- @param list std.ConstTpl<T>
--- @return std.Unpack<T, Start, End>
function unpack(list, i, j) end

Expand Down
2 changes: 1 addition & 1 deletion crates/emmylua_code_analysis/resources/std/table.lua
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ function table.sort(list, comp) end
--- @generic T, Start: integer, End: integer
--- @param i? std.ConstTpl<Start>
--- @param j? std.ConstTpl<End>
--- @param list T
--- @param list std.ConstTpl<T>
--- @return std.Unpack<T, Start, End>
function table.unpack(list, i, j) end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,24 @@ use crate::{GenericParam, GenericTpl, GenericTplId, LuaType};
pub trait GenericIndex: std::fmt::Debug {
fn add_generic_scope(&mut self, ranges: Vec<TextRange>, is_func: bool) -> GenericScopeId;

fn append_generic_param(&mut self, scope_id: GenericScopeId, param: GenericParam);

fn append_generic_params(&mut self, scope_id: GenericScopeId, params: Vec<GenericParam>) {
fn append_generic_param(
&mut self,
scope_id: GenericScopeId,
param: GenericParam,
) -> Option<GenericTplId>;

fn append_generic_params(
&mut self,
scope_id: GenericScopeId,
params: Vec<GenericParam>,
) -> Vec<GenericParam> {
let mut appended = Vec::new();
for param in params {
self.append_generic_param(scope_id, param);
if let Some(tpl_id) = self.append_generic_param(scope_id, param.clone()) {
appended.push(param.with_tpl_id(Some(tpl_id)));
}
}
appended
}

fn find_generic(
Expand Down Expand Up @@ -63,16 +75,15 @@ impl GenericIndex for FileGenericIndex {
scope_id
}

fn append_generic_param(&mut self, scope_id: GenericScopeId, param: GenericParam) {
fn append_generic_param(
&mut self,
scope_id: GenericScopeId,
param: GenericParam,
) -> Option<GenericTplId> {
if let Some(scope) = self.scopes.get_mut(scope_id.id) {
scope.insert_param(param);
}
}

fn append_generic_params(&mut self, scope_id: GenericScopeId, params: Vec<GenericParam>) {
for param in params {
self.append_generic_param(scope_id, param);
return Some(scope.insert_param(param));
}
None
}

/// Find generic parameter by position and name.
Expand Down Expand Up @@ -131,10 +142,12 @@ impl FileGenericScope {
self.next_tpl_id.is_func()
}

fn insert_param(&mut self, param: GenericParam) {
let tpl_id = self.next_tpl_id;
self.next_tpl_id = self.next_tpl_id.with_idx((tpl_id.get_idx() + 1) as u32);
fn insert_param(&mut self, param: GenericParam) -> GenericTplId {
let tpl_id = param.tpl_id.unwrap_or(self.next_tpl_id);
let next_idx = self.next_tpl_id.get_idx().max(tpl_id.get_idx() + 1) as u32;
self.next_tpl_id = self.next_tpl_id.with_idx(next_idx);
self.params.insert(param.name.to_string(), (tpl_id, param));
tpl_id
}

fn contains(&self, position: TextSize) -> bool {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,10 @@ pub fn analyze_alias(analyzer: &mut DocAnalyzer, tag: LuaDocTagAlias) -> Option<
alias_decl.get_id()
};

let type_node = tag.get_type()?;
if tag.get_generic_decl_list().is_some() {
let generic_params = get_type_generic_params(analyzer, &alias_decl_id);
let range = analyzer.comment.get_range();
let range = type_node.get_range();
let scope_id = analyzer
.type_context
.generic_index
Expand All @@ -155,7 +156,7 @@ pub fn analyze_alias(analyzer: &mut DocAnalyzer, tag: LuaDocTagAlias) -> Option<
.append_generic_params(scope_id, generic_params);
}

let mut origin_type = infer_type(&mut analyzer.type_context, tag.get_type()?);
let mut origin_type = infer_type(&mut analyzer.type_context, type_node);
if alias_origin_reaches(analyzer.get_db(), &origin_type, &alias_decl_id) {
origin_type = LuaType::Any;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ fn normalize_generic_params(db: &DbIndex, params: &[GenericParam]) -> Vec<Generi
.map(|ty| complete_type_generic_args_in_type(db, ty)),
param.attributes.clone(),
)
.with_tpl_id(param.tpl_id)
})
.collect()
}
Expand Down Expand Up @@ -97,8 +98,9 @@ fn resolve_generic_params(
});

let param = GenericParam::new(name, constraint, default_type, None);
generic_index.append_generic_param(scope_id, param.clone());
params.push(param);
if let Some(tpl_id) = generic_index.append_generic_param(scope_id, param.clone()) {
params.push(param.with_tpl_id(Some(tpl_id)));
}
}

params
Expand Down Expand Up @@ -156,13 +158,16 @@ impl GenericIndex for HeaderGenericIndex {
id
}

fn append_generic_param(&mut self, scope_id: GenericScopeId, param: GenericParam) {
let Some(scope) = self.scopes.get_mut(scope_id.id) else {
return;
};
fn append_generic_param(
&mut self,
scope_id: GenericScopeId,
param: GenericParam,
) -> Option<GenericTplId> {
let scope = self.scopes.get_mut(scope_id.id)?;
let tpl_id = scope.next_tpl_id;
scope.next_tpl_id = scope.next_tpl_id.with_idx((tpl_id.get_idx() + 1) as u32);
scope.params.push((tpl_id, param));
Some(tpl_id)
}

fn find_generic(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::collections::HashSet;

use emmylua_parser::{
LuaAst, LuaAstNode, LuaAstToken, LuaBlock, LuaDocDescriptionOwner, LuaDocTagAs, LuaDocTagCast,
LuaDocTagModule, LuaDocTagOther, LuaDocTagOverload, LuaDocTagParam, LuaDocTagReturn,
Expand All @@ -12,13 +14,14 @@ use super::{
tags::{find_owner_closure, get_owner_id_or_report},
};
use crate::{
InFiled, JsonSchemaFile, LuaOperatorMetaMethod, LuaTypeCache, LuaTypeOwner, OperatorFunction,
SignatureReturnStatus, TypeOps,
DbIndex, InFiled, JsonSchemaFile, LuaOperatorMetaMethod, LuaTypeCache, LuaTypeDeclId,
LuaTypeOwner, OperatorFunction, SignatureReturnStatus, TplResolvePolicy, TypeMapper, TypeOps,
compilation::analyzer::common::bind_type,
db_index::{
LuaDeclId, LuaDocParamInfo, LuaDocReturnInfo, LuaDocReturnOverloadInfo, LuaMemberId,
LuaOperator, LuaSemanticDeclId, LuaSignatureId, LuaType,
LuaDeclId, LuaDocParamInfo, LuaDocReturnInfo, LuaDocReturnOverloadInfo, LuaGenericType,
LuaMemberId, LuaOperator, LuaSemanticDeclId, LuaSignatureId, LuaType,
},
instantiate_type_generic, instantiate_type_generic_full,
};
use crate::{
LuaAttributeUse,
Expand All @@ -36,6 +39,7 @@ pub fn analyze_type(analyzer: &mut DocAnalyzer, tag: LuaDocTagType) -> Option<()
let mut type_list = Vec::new();
for lua_doc_type in tag.get_type_list() {
let type_ref = infer_type(&mut analyzer.type_context, lua_doc_type);
let type_ref = maybe_instantiate_doc_type(analyzer.get_db(), type_ref);
type_list.push(type_ref);
}

Expand All @@ -44,6 +48,147 @@ pub fn analyze_type(analyzer: &mut DocAnalyzer, tag: LuaDocTagType) -> Option<()
Some(())
}

fn maybe_instantiate_doc_type(db: &DbIndex, type_ref: LuaType) -> LuaType {
let type_decl_id = match &type_ref {
LuaType::Ref(type_id) => Some(type_id.clone()),
LuaType::Generic(generic) => Some(generic.get_base_type_id()),
_ => None,
};
let has_alias_chain = type_decl_id
.as_ref()
.and_then(|type_decl_id| db.get_type_index().get_type_decl(type_decl_id))
.is_some_and(|type_decl| {
type_decl.is_alias()
&& matches!(
type_decl.get_alias_ref(),
Some(LuaType::Ref(_) | LuaType::Generic(_))
)
});
let contain_tpl = type_ref.contain_tpl();

if !contain_tpl && !has_alias_chain {
return type_ref;
}

let mapper = TypeMapper::empty();

if contain_tpl {
if has_alias_chain {
let (current_type, current_id) = match &type_ref {
LuaType::Generic(generic) => {
let params = generic
.get_params()
.iter()
.map(|param| {
instantiate_type_generic_full(
db,
param,
&mapper,
None,
TplResolvePolicy::PreserveTplRef,
)
})
.collect::<Vec<_>>();
(
LuaType::Generic(
LuaGenericType::new(generic.get_base_type_id(), params).into(),
),
generic.get_base_type_id(),
)
}
LuaType::Ref(type_id) => (LuaType::Ref(type_id.clone()), type_id.clone()),
_ => {
return instantiate_type_generic_full(
db,
&type_ref,
&mapper,
None,
TplResolvePolicy::PreserveTplRef,
);
}
};

return instantiate_doc_alias_chain(db, current_type, current_id, &mapper);
}
return instantiate_type_generic_full(
db,
&type_ref,
&mapper,
None,
TplResolvePolicy::PreserveTplRef,
);
}

if has_alias_chain {
let instantiated = instantiate_type_generic(db, &type_ref, &mapper);
if !matches!(instantiated, LuaType::Any | LuaType::Unknown) {
return instantiated;
}
}

type_ref
}

fn instantiate_doc_alias_chain(
db: &DbIndex,
mut current_type: LuaType,
mut current_id: LuaTypeDeclId,
mapper: &TypeMapper,
) -> LuaType {
let mut visited = HashSet::new();
loop {
if !visited.insert(current_id.clone()) {
return current_type;
}

let Some(type_decl) = db.get_type_index().get_type_decl(&current_id) else {
return current_type;
};
if !type_decl.is_alias() {
return current_type;
}

let Some(origin) = type_decl.get_alias_ref() else {
return current_type;
};
let next_id = match origin {
LuaType::Ref(type_id) => type_id.clone(),
LuaType::Generic(generic) => generic.get_base_type_id(),
_ => return current_type,
};

let params = match &current_type {
LuaType::Generic(generic) => generic.get_params().clone(),
LuaType::Ref(_) => Vec::new(),
_ => return current_type,
};
let alias_mapper = TypeMapper::from_alias(db, params, &current_id);
let alias_mapper = TypeMapper::merge(Some(alias_mapper), mapper.clone());

current_type = match origin {
LuaType::Generic(generic) => {
let params = generic
.get_params()
.iter()
.map(|param| {
instantiate_type_generic_full(
db,
param,
&alias_mapper,
None,
TplResolvePolicy::PreserveTplRef,
)
})
.collect::<Vec<_>>();
LuaType::Generic(LuaGenericType::new(next_id.clone(), params).into())
}
LuaType::Ref(type_id) => LuaType::Ref(type_id.clone()),
_ => return current_type,
};
current_id = next_id;
}
}

fn bind_type_to_owner(
analyzer: &mut DocAnalyzer,
tag: &impl LuaAstNode,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ use emmylua_parser::{LuaAstToken, LuaExpr, LuaForRangeStat};

use crate::{
DbIndex, InferFailReason, LuaDeclId, LuaInferCache, LuaOperatorMetaMethod, LuaType,
LuaTypeCache, TplContext, TypeOps, TypeSubstitutor, VariadicType,
compilation::analyzer::unresolve::UnResolveIterVar, infer_expr, instantiate_doc_function,
tpl_pattern_match_args,
LuaTypeCache, TypeOps, VariadicType, compilation::analyzer::unresolve::UnResolveIterVar,
infer_expr, instantiate_doc_function_by_arg_types,
};

use super::LuaAnalyzer;
Expand Down Expand Up @@ -144,28 +143,8 @@ pub fn infer_for_range_iter_expr_func(
let Some(status_param) = status_param else {
return Ok(doc_function.get_variadic_ret());
};
let mut substitutor = TypeSubstitutor::new();
let mut context = TplContext {
db,
cache,
substitutor: &mut substitutor,
call_expr: None,
};
let params = doc_function
.get_params()
.iter()
.map(|(_, opt_ty)| opt_ty.clone().unwrap_or(LuaType::Any))
.collect::<Vec<_>>();

tpl_pattern_match_args(&mut context, &params, &[status_param])?;

let instantiate_func = if let LuaType::DocFunction(f) =
instantiate_doc_function(db, &doc_function, &substitutor)
{
f
} else {
doc_function
};
let instantiate_func =
instantiate_doc_function_by_arg_types(db, cache, &doc_function, &[status_param])?;

Ok(instantiate_func.get_variadic_ret())
}
Loading
Loading