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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@
.idea
.cursor
dhat-heap.json
.claude

# MCP, 用于为AI提供lsp上下文
.serena
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
"files.insertFinalNewline": true,
"files.trimFinalNewlines": true,
"files.trimTrailingWhitespace": true,
"files.trimTrailingWhitespaceInRegexAndStrings": true
"files.trimTrailingWhitespaceInRegexAndStrings": true,
"rust-analyzer.check.command": "clippy",
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ impl FileGenericIndex {
is_func: bool,
) {
let params_id = self.generic_params.len();
// 由于我们允许 infer 推断出一个虚拟泛型, 因此需要计算已声明的泛型数量确定其位置
let start = self.get_start(&ranges).unwrap_or(0);
self.generic_params
.push(TagGenericParams::new(params, is_func));
.push(TagGenericParams::new(params, is_func, start));
let params_id = GenericParamId::new(params_id);
let root_node_ids: Vec<_> = self.root_node_ids.clone();
for range in ranges {
Expand All @@ -53,6 +55,17 @@ impl FileGenericIndex {
}
}

fn get_start(&self, ranges: &[TextRange]) -> Option<usize> {
let params_ids = self.find_generic_params(ranges.first()?.start())?;
let mut start = 0;
for params_id in params_ids.iter() {
if let Some(params) = self.generic_params.get(*params_id) {
start += params.params.len();
}
}
Some(start)
}

fn try_add_range_to_effect_node(
&mut self,
range: TextRange,
Expand Down Expand Up @@ -150,8 +163,8 @@ impl FileGenericIndex {
}

#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
struct GenericParamId {
id: usize,
pub struct GenericParamId {
pub id: usize,
}

impl GenericParamId {
Expand Down Expand Up @@ -185,10 +198,10 @@ pub struct TagGenericParams {
}

impl TagGenericParams {
pub fn new(generic_params: Vec<GenericParam>, is_func: bool) -> Self {
pub fn new(generic_params: Vec<GenericParam>, is_func: bool, start: usize) -> Self {
let mut params = HashMap::new();
for (i, param) in generic_params.into_iter().enumerate() {
params.insert(param.name.to_string(), (i, param.is_variadic));
params.insert(param.name.to_string(), (start + i, param.is_variadic));
}
Self { params, is_func }
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
use std::sync::Arc;

use emmylua_parser::{
LuaAst, LuaAstNode, LuaDocBinaryType, LuaDocDescriptionOwner, LuaDocFuncType,
LuaDocGenericType, LuaDocMultiLineUnionType, LuaDocObjectFieldKey, LuaDocObjectType,
LuaAst, LuaAstNode, LuaDocBinaryType, LuaDocConditionalType, LuaDocDescriptionOwner,
LuaDocFuncType, LuaDocGenericDecl, LuaDocGenericType, LuaDocIndexAccessType, LuaDocInferType,
LuaDocMappedType, LuaDocMultiLineUnionType, LuaDocObjectFieldKey, LuaDocObjectType,
LuaDocStrTplType, LuaDocType, LuaDocUnaryType, LuaDocVariadicType, LuaLiteralToken,
LuaSyntaxKind, LuaTypeBinaryOperator, LuaTypeUnaryOperator, LuaVarExpr,
};
use internment::ArcIntern;
use rowan::TextRange;
use smol_str::SmolStr;

use crate::{
AsyncState, DiagnosticCode, GenericTpl, InFiled, LuaAliasCallKind, LuaArrayLen, LuaArrayType,
LuaMultiLineUnion, LuaTupleStatus, LuaTypeDeclId, TypeOps, VariadicType,
AsyncState, DiagnosticCode, GenericParam, GenericTpl, InFiled, LuaAliasCallKind, LuaArrayLen,
LuaArrayType, LuaMultiLineUnion, LuaTupleStatus, LuaTypeDeclId, TypeOps, VariadicType,
db_index::{
AnalyzeError, LuaAliasCallType, LuaFunctionType, LuaGenericType, LuaIndexAccessKey,
LuaIntersectionType, LuaObjectType, LuaStringTplType, LuaTupleType, LuaType,
AnalyzeError, LuaAliasCallType, LuaConditionalType, LuaFunctionType, LuaGenericType,
LuaIndexAccessKey, LuaIntersectionType, LuaMappedType, LuaObjectType, LuaStringTplType,
LuaTupleType, LuaType,
},
};

Expand Down Expand Up @@ -108,7 +111,20 @@ pub fn infer_type(analyzer: &mut DocAnalyzer, node: LuaDocType) -> LuaType {
LuaDocType::MultiLineUnion(multi_union) => {
return infer_multi_line_union_type(analyzer, multi_union);
}
_ => {} // LuaDocType::Conditional(lua_doc_conditional_type) => todo!(),
LuaDocType::Conditional(cond_type) => {
return infer_conditional_type(analyzer, cond_type);
}
LuaDocType::Infer(infer_type) => {
if let Some(name) = infer_type.get_name_text() {
return LuaType::ConditionalInfer(ArcIntern::new(SmolStr::new(&name)));
}
}
LuaDocType::Mapped(mapped_type) => {
return infer_mapped_type(analyzer, mapped_type).unwrap_or(LuaType::Unknown);
}
LuaDocType::IndexAccess(index_access) => {
return infer_index_access_type(analyzer, index_access);
}
}
LuaType::Unknown
}
Expand All @@ -122,6 +138,7 @@ fn infer_buildin_or_ref_type(
let position = range.start();
match name {
"unknown" => LuaType::Unknown,
"never" => LuaType::Never,
"nil" | "void" => LuaType::Nil,
"any" => LuaType::Any,
"userdata" => LuaType::Userdata,
Expand Down Expand Up @@ -637,3 +654,104 @@ fn infer_multi_line_union_type(

LuaType::MultiLineUnion(LuaMultiLineUnion::new(union_members).into())
}

fn infer_conditional_type(
analyzer: &mut DocAnalyzer,
cond_type: &LuaDocConditionalType,
) -> LuaType {
if let Some((condition, when_true, when_false)) = cond_type.get_types() {
// 收集条件中的所有 infer 声明
let infer_params = collect_cond_infer_params(&condition);
if !infer_params.is_empty() {
// 条件表达式中 infer 声明的类型参数只允许在`true`分支中使用
let true_range = when_true.get_range();
analyzer
.generic_index
.add_generic_scope(vec![true_range], infer_params.clone(), false);
}

// 处理条件和分支类型
let condition_type = infer_type(analyzer, condition);
let true_type = infer_type(analyzer, when_true);
let false_type = infer_type(analyzer, when_false);

return LuaConditionalType::new(
condition_type,
true_type,
false_type,
infer_params,
cond_type.has_new().unwrap_or(false),
)
.into();
}

LuaType::Unknown
}

/// 收集条件类型中的条件表达式中所有 infer 声明
fn collect_cond_infer_params(doc_type: &LuaDocType) -> Vec<GenericParam> {
let mut params = Vec::new();
let doc_infer_types = doc_type.descendants::<LuaDocInferType>();
for infer_type in doc_infer_types {
if let Some(name) = infer_type.get_name_text() {
params.push(GenericParam::new(SmolStr::new(&name), None, false));
}
}
params
}

fn infer_mapped_type(
analyzer: &mut DocAnalyzer,
mapped_type: &LuaDocMappedType,
) -> Option<LuaType> {
// [P in K]
let mapped_key = mapped_type.get_key()?;
let generic_decl = mapped_key.child::<LuaDocGenericDecl>()?;
let name_token = generic_decl.get_name_token()?;
let name = name_token.get_name_text();
let constraint = generic_decl
.get_type()
.map(|constraint| infer_type(analyzer, constraint));
let param = GenericParam::new(SmolStr::new(name), constraint, generic_decl.is_variadic());

analyzer.generic_index.add_generic_scope(
vec![mapped_type.get_range()],
vec![param.clone()],
false,
);
let position = mapped_type.get_range().start();
let id = analyzer.generic_index.find_generic(position, name)?.0;

let doc_type = mapped_type.get_value_type()?;
let value_type = infer_type(analyzer, doc_type);

Some(LuaType::Mapped(
LuaMappedType::new(
(id, param),
value_type,
mapped_type.is_readonly(),
mapped_type.is_optional(),
)
.into(),
))
}

fn infer_index_access_type(
analyzer: &mut DocAnalyzer,
index_access: &LuaDocIndexAccessType,
) -> LuaType {
let mut types_iter = index_access.children::<LuaDocType>();
let Some(source_doc) = types_iter.next() else {
return LuaType::Unknown;
};
let Some(key_doc) = types_iter.next() else {
return LuaType::Unknown;
};

let source_type = infer_type(analyzer, source_doc);
let key_type = infer_type(analyzer, key_doc);

LuaType::Call(
LuaAliasCallType::new(LuaAliasCallKind::Index, vec![source_type, key_type]).into(),
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ use crate::{
use emmylua_parser::{LuaAstNode, LuaComment, LuaSyntaxNode};
use file_generic_index::FileGenericIndex;
use tags::get_owner_id;

pub struct DocAnalysisPipeline;

impl AnalysisPipeline for DocAnalysisPipeline {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ pub fn analyze_class(analyzer: &mut DocAnalyzer, tag: LuaDocTagClass) -> Option<
.get_type_index_mut()
.add_generic_params(class_decl_id.clone(), generic_params.clone());

add_generic_index(analyzer, generic_params);
add_generic_index(analyzer, generic_params, &tag);
}

if let Some(supers) = tag.get_supers() {
Expand Down Expand Up @@ -159,9 +159,12 @@ pub fn analyze_alias(analyzer: &mut DocAnalyzer, tag: LuaDocTagAlias) -> Option<
.generic_index
.add_generic_scope(vec![range], generic_params, false);
}
// dbg!(&tag);

let origin_type = infer_type(analyzer, tag.get_type()?);

// dbg!(&origin_type);

let alias = analyzer
.db
.get_type_index_mut()
Expand Down Expand Up @@ -197,10 +200,13 @@ fn get_generic_params(
params_result
}

fn add_generic_index(analyzer: &mut DocAnalyzer, generic_params: Vec<GenericParam>) {
fn add_generic_index(
analyzer: &mut DocAnalyzer,
generic_params: Vec<GenericParam>,
tag: &LuaDocTagClass,
) {
let mut ranges = Vec::new();
let range = analyzer.comment.get_range();
ranges.push(range);
ranges.push(tag.get_effective_range());
if let Some(comment_owner) = analyzer.comment.get_owner() {
let range = comment_owner.get_range();
ranges.push(range);
Expand Down Expand Up @@ -330,7 +336,7 @@ pub fn analyze_func_generic(analyzer: &mut DocAnalyzer, tag: LuaDocTagGeneric) -
params_result.push(GenericParam::new(
SmolStr::new(name.as_str()),
type_ref.clone(),
false,
param.is_variadic(),
));
param_info.push((name, type_ref));
}
Expand Down
Loading