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
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ pub fn analyze_field(analyzer: &mut DocAnalyzer, tag: LuaDocTagField) -> Option<
),
("key".to_string(), Some(key_type_ref.clone())),
],
field_type.clone(),
vec![field_type.clone()],
))),
);
analyzer
Expand Down Expand Up @@ -207,7 +207,7 @@ pub fn analyze_operator(analyzer: &mut DocAnalyzer, tag: LuaDocTagOperator) -> O
false,
false,
operands,
return_type,
vec![return_type],
))),
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -696,21 +696,13 @@ fn infer_func_type(analyzer: &mut DocTypeAnalyzeContext<'_>, func: &LuaDocFuncTy
is_colon = false
}

let return_type = if return_types.len() == 1 {
return_types[0].clone()
} else if return_types.len() > 1 {
LuaType::Variadic(VariadicType::Multi(return_types).into())
} else {
LuaType::Nil
};

LuaType::DocFunction(
LuaFunctionType::new(
async_state,
is_colon,
is_variadic,
params_result,
return_type,
return_types,
)
.into(),
)
Expand Down
153 changes: 43 additions & 110 deletions crates/emmylua_code_analysis/src/compilation/analyzer/lua/closure.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
use std::ops::Deref;

use emmylua_parser::{
LuaAst, LuaAstNode, LuaCallArgList, LuaCallExpr, LuaClosureExpr, LuaFuncStat, LuaVarExpr,
LuaAst, LuaAstNode, LuaCallArgList, LuaCallExpr, LuaClosureExpr, LuaExpr, LuaFuncStat,
LuaVarExpr,
};

use crate::{
DbIndex, InferFailReason, LuaInferCache, LuaType, SignatureReturnStatus, TypeOps, VariadicType,
DbIndex, InferFailReason, LuaInferCache, LuaType, SignatureReturnStatus, TypeOps,
compilation::analyzer::unresolve::{
UnResolveCallClosureParams, UnResolveClosureReturn, UnResolveParentAst,
UnResolveParentClosureParams, UnResolveReturn,
},
db_index::{LuaDocReturnInfo, LuaSignatureId},
db_index::{LuaDocReturnInfo, LuaSignatureId, return_row::merge_return_rows_with},
infer_expr,
semantic::infer_return_expr_list_types,
};

use super::{LuaAnalyzer, LuaReturnPoint, analyze_func_body_returns_with};
Expand Down Expand Up @@ -220,120 +220,53 @@ pub fn analyze_return_point(
cache: &mut LuaInferCache,
return_points: &[LuaReturnPoint],
) -> Result<Vec<LuaDocReturnInfo>, InferFailReason> {
let mut return_type = None;
let mut return_row: Option<Vec<LuaType>> = None;
for point in return_points {
let point_type = match point {
LuaReturnPoint::Expr(expr) => Some(infer_expr(db, cache, expr.clone())?),
LuaReturnPoint::MuliExpr(exprs) => {
let mut multi_return = Vec::with_capacity(exprs.len());
for expr in exprs {
multi_return.push(infer_expr(db, cache, expr.clone())?);
}
Some(LuaType::Variadic(VariadicType::Multi(multi_return).into()))
let point_row = match point {
LuaReturnPoint::Expr(expr) => {
Some(infer_return_row(db, cache, std::slice::from_ref(expr))?)
}
LuaReturnPoint::Nil => Some(LuaType::Nil),
LuaReturnPoint::MuliExpr(exprs) => Some(infer_return_row(db, cache, exprs)?),
LuaReturnPoint::Empty => Some(Vec::new()),
_ => None,
};

if let Some(point_type) = point_type {
return_type = Some(match return_type {
Some(return_type) => union_return_expr(db, return_type, point_type),
None => point_type,
if let Some(point_row) = point_row {
return_row = Some(match return_row {
Some(return_row) => {
let rows = [return_row.as_slice(), point_row.as_slice()];
merge_return_rows_with(&rows, |types| {
types
.into_iter()
.reduce(|left, right| TypeOps::Union.apply(db, &left, &right))
.unwrap_or(LuaType::Never)
})
}
None => point_row,
});
}
}

let return_type = return_type.unwrap_or(LuaType::Unknown);

Ok(vec![LuaDocReturnInfo {
type_ref: return_type,
description: None,
name: None,
attributes: None,
}])
let return_row = return_row.unwrap_or_else(|| vec![LuaType::Unknown]);

Ok(return_row
.into_iter()
.map(|type_ref| LuaDocReturnInfo {
type_ref,
description: None,
name: None,
attributes: None,
})
.collect())
}

fn union_return_expr(db: &DbIndex, left: LuaType, right: LuaType) -> LuaType {
match (&left, &right) {
(LuaType::Variadic(left_variadic), LuaType::Variadic(right_variadic)) => {
match (&left_variadic.deref(), &right_variadic.deref()) {
(VariadicType::Base(left_base), VariadicType::Base(right_base)) => {
let union_base = TypeOps::Union.apply(db, left_base, right_base);
LuaType::Variadic(VariadicType::Base(union_base).into())
}
(VariadicType::Multi(left_multi), VariadicType::Multi(right_multi)) => {
let mut new_multi = vec![];
let max_len = left_multi.len().max(right_multi.len());
for i in 0..max_len {
let left_type = left_multi.get(i).cloned().unwrap_or(LuaType::Nil);
let right_type = right_multi.get(i).cloned().unwrap_or(LuaType::Nil);
new_multi.push(TypeOps::Union.apply(db, &left_type, &right_type));
}
LuaType::Variadic(VariadicType::Multi(new_multi).into())
}
// difficult to merge the type, use let
_ => left.clone(),
}
}
(LuaType::Variadic(variadic), _) => {
let first_type = variadic.get_type(0).cloned().unwrap_or(LuaType::Unknown);
let first_union_type = TypeOps::Union.apply(db, &first_type, &right);

match variadic.deref() {
VariadicType::Base(base) => {
let union_base = TypeOps::Union.apply(db, base, &LuaType::Nil);
LuaType::Variadic(
VariadicType::Multi(vec![
first_union_type,
LuaType::Variadic(VariadicType::Base(union_base).into()),
])
.into(),
)
}
VariadicType::Multi(multi) => {
let mut new_multi = multi.clone();
if !new_multi.is_empty() {
new_multi[0] = first_union_type;
for mult in new_multi.iter_mut().skip(1) {
*mult = TypeOps::Union.apply(db, mult, &LuaType::Nil);
}
} else {
new_multi.push(first_union_type);
}

LuaType::Variadic(VariadicType::Multi(new_multi).into())
}
}
}
(_, LuaType::Variadic(variadic)) => {
let first_type = variadic.get_type(0).cloned().unwrap_or(LuaType::Unknown);
let first_union_type = TypeOps::Union.apply(db, &left, &first_type);
match variadic.deref() {
VariadicType::Base(base) => {
let union_base = TypeOps::Union.apply(db, base, &LuaType::Nil);
LuaType::Variadic(
VariadicType::Multi(vec![
first_union_type,
LuaType::Variadic(VariadicType::Base(union_base).into()),
])
.into(),
)
}
VariadicType::Multi(multi) => {
let mut new_multi = multi.clone();
if !new_multi.is_empty() {
new_multi[0] = first_union_type;
for mult in new_multi.iter_mut().skip(1) {
*mult = TypeOps::Union.apply(db, mult, &LuaType::Nil);
}
} else {
new_multi.push(first_union_type);
}

LuaType::Variadic(VariadicType::Multi(new_multi).into())
}
}
}
_ => TypeOps::Union.apply(db, &left, &right),
}
fn infer_return_row(
db: &DbIndex,
cache: &mut LuaInferCache,
exprs: &[LuaExpr],
) -> Result<Vec<LuaType>, InferFailReason> {
Ok(infer_return_expr_list_types(db, cache, exprs, infer_expr)?
.into_iter()
.map(|(ty, _)| ty)
.collect())
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::{InferFailReason, LuaType};
pub enum LuaReturnPoint {
Expr(LuaExpr),
MuliExpr(Vec<LuaExpr>),
Nil,
Empty,
Error,
}

Expand Down Expand Up @@ -62,7 +62,7 @@ where
{
let mut flow = analyze_block_returns(body, infer_expr_type)?;
if flow.can_fall_through || flow.can_break {
flow.return_points.push(LuaReturnPoint::Nil);
flow.return_points.push(LuaReturnPoint::Empty);
}

Ok(flow.return_points)
Expand Down Expand Up @@ -356,7 +356,7 @@ fn analyze_call_expr_stat_returns(
fn analyze_return_stat_returns(return_stat: LuaReturnStat) -> ReturnFlow {
let exprs: Vec<LuaExpr> = return_stat.get_expr_list().collect();
let return_point = match exprs.len() {
0 => LuaReturnPoint::Nil,
0 => LuaReturnPoint::Empty,
1 => LuaReturnPoint::Expr(exprs[0].clone()),
_ => LuaReturnPoint::MuliExpr(exprs),
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use emmylua_parser::{LuaAstNode, LuaChunk, LuaExpr};
use crate::{
InferFailReason, LuaDeclId, LuaSemanticDeclId, LuaSignatureId,
compilation::analyzer::unresolve::UnResolveModule, db_index::LuaType, infer_expr,
semantic::adjusted_result_slot_type,
};

use super::{LuaAnalyzer, LuaReturnPoint, analyze_func_body_returns_with};
Expand Down Expand Up @@ -46,7 +47,7 @@ pub fn analyze_chunk_return(analyzer: &mut LuaAnalyzer, chunk: LuaChunk) -> Opti
.db
.get_module_index_mut()
.get_module_mut(analyzer.file_id)?;
module_info.export_type = Some(expr_type.get_result_slot_type(0).unwrap_or(expr_type));
module_info.export_type = Some(adjusted_result_slot_type(&expr_type, 0));
module_info.semantic_id = semantic_id;
if let Some(visibility) = visibility {
module_info.merge_visibility(visibility);
Expand Down
67 changes: 30 additions & 37 deletions crates/emmylua_code_analysis/src/compilation/analyzer/lua/stats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use crate::{
unresolve::{UnResolveDecl, UnResolveMember},
},
db_index::{LuaDeclId, LuaMember, LuaMemberFeature, LuaMemberId, LuaMemberOwner, LuaType},
semantic::{adjusted_result_slot_type, assignment_rhs_source},
};

use super::LuaAnalyzer;
Expand Down Expand Up @@ -49,7 +50,7 @@ pub fn analyze_local_stat(analyzer: &mut LuaAnalyzer, local_stat: LuaLocalStat)

match analyzer.infer_expr(&expr) {
Ok(expr_type) => {
let expr_type = expr_type.get_result_slot_type(0).unwrap_or(expr_type);
let expr_type = adjusted_result_slot_type(&expr_type, 0);
let decl_id = LuaDeclId::new(analyzer.file_id, position);
// 当`call`参数包含表时, 表可能未被分析, 需要延迟
if let LuaType::Instance(instance) = &expr_type
Expand Down Expand Up @@ -105,38 +106,30 @@ pub fn analyze_local_stat(analyzer: &mut LuaAnalyzer, local_stat: LuaLocalStat)
if let Some(last_expr) = last_expr {
match analyzer.infer_expr(last_expr) {
Ok(last_expr_type) => {
if last_expr_type.contain_multi_return() {
for i in expr_count..name_count {
let name = name_list.get(i)?;
let position = name.get_position();
let decl_id = LuaDeclId::new(analyzer.file_id, position);
let ret_type = last_expr_type.get_result_slot_type(i - expr_count + 1);
if let Some(ret_type) = ret_type {
bind_type(
analyzer.db,
decl_id.into(),
LuaTypeCache::InferType(ret_type.clone()),
);
} else {
analyzer.db.get_type_index_mut().bind_type(
decl_id.into(),
LuaTypeCache::InferType(LuaType::Unknown),
);
}
}
return Some(());
for i in expr_count..name_count {
let name = name_list.get(i)?;
let position = name.get_position();
let decl_id = LuaDeclId::new(analyzer.file_id, position);
let (_, slot) = assignment_rhs_source(expr_count, i)?;
let ret_type = adjusted_result_slot_type(&last_expr_type, slot);
bind_type(
analyzer.db,
decl_id.into(),
LuaTypeCache::InferType(ret_type),
);
}
}
Err(reason) => {
for i in expr_count..name_count {
let name = name_list.get(i)?;
let position = name.get_position();
let decl_id = LuaDeclId::new(analyzer.file_id, position);
let (_, slot) = assignment_rhs_source(expr_count, i)?;
let unresolve = UnResolveDecl {
file_id: analyzer.file_id,
decl_id,
expr: last_expr.clone(),
ret_idx: i - expr_count + 1,
ret_idx: slot,
};

analyzer
Expand Down Expand Up @@ -310,7 +303,7 @@ pub fn analyze_assign_stat(analyzer: &mut LuaAnalyzer, assign_stat: LuaAssignSta
}

let expr_type = match analyzer.infer_expr(expr) {
Ok(expr_type) => expr_type.get_result_slot_type(0).unwrap_or(expr_type),
Ok(expr_type) => adjusted_result_slot_type(&expr_type, 0),
Err(InferFailReason::None) => LuaType::Unknown,
Err(reason) => {
match type_owner {
Expand Down Expand Up @@ -363,30 +356,30 @@ pub fn analyze_assign_stat(analyzer: &mut LuaAnalyzer, assign_stat: LuaAssignSta
{
match analyzer.infer_expr(last_expr) {
Ok(last_expr_type) => {
if last_expr_type.contain_multi_return() {
for i in expr_count..var_count {
let var = var_list.get(i)?;
let type_owner = get_var_owner(analyzer, var.clone());
set_index_expr_owner(analyzer, var.clone());
assign_merge_type_owner_and_expr_type(
analyzer,
type_owner,
&last_expr_type,
i - expr_count + 1,
);
}
for i in expr_count..var_count {
let var = var_list.get(i)?;
let type_owner = get_var_owner(analyzer, var.clone());
set_index_expr_owner(analyzer, var.clone());
let (_, slot) = assignment_rhs_source(expr_count, i)?;
assign_merge_type_owner_and_expr_type(
analyzer,
type_owner,
&last_expr_type,
slot,
);
}
}
Err(_) => {
for i in expr_count..var_count {
let var = var_list.get(i)?;
let type_owner = get_var_owner(analyzer, var.clone());
set_index_expr_owner(analyzer, var.clone());
let (_, slot) = assignment_rhs_source(expr_count, i)?;
merge_type_owner_and_unresolve_expr(
analyzer,
type_owner,
last_expr.clone(),
i - expr_count + 1,
slot,
);
}
}
Expand All @@ -404,7 +397,7 @@ fn assign_merge_type_owner_and_expr_type(
expr_type: &LuaType,
idx: usize,
) -> Option<()> {
let expr_type = expr_type.get_result_slot_type(idx).unwrap_or(LuaType::Nil);
let expr_type = adjusted_result_slot_type(expr_type, idx);

bind_type(analyzer.db, type_owner, LuaTypeCache::InferType(expr_type));

Expand Down
Loading
Loading