Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 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
8 changes: 3 additions & 5 deletions crates/emmylua_code_analysis/resources/std/global.lua
Original file line number Diff line number Diff line change
Expand Up @@ -390,13 +390,11 @@ function setmetatable(table, metatable) end
--- represents 10, 'B' represents 11, and so forth, with 'Z' representing 35. If
--- the string `e` is not a valid numeral in the given base, the function
--- returns **nil**.
---@overload fun(e:number, base?: int):number
---@overload fun(e:string, base?: int):number?
---@overload fun(e:any, base?:int):nil
---@overload fun(e: string, base: integer):integer?
---@param e any
---@param base? int
---@return number?
function tonumber(e, base) end
---@nodiscard
function tonumber(e) end

---
--- Receives a value of any type and converts it to a string in a human-readable
Expand Down
2 changes: 1 addition & 1 deletion crates/emmylua_code_analysis/resources/std/math.lua
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ function math.tan(x) return 0 end
---
--- If the value `x` is convertible to an integer, returns that integer.
--- Otherwise, returns `nil`.
---@param x number
---@param x any
---@return integer?
function math.tointeger(x) end

Expand Down
12 changes: 5 additions & 7 deletions crates/emmylua_code_analysis/resources/std/table.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,12 @@ table = {}
--- `list[i]..sep..list[i+1] ... sep..list[j]`. The default value for
--- `sep` is the empty string, the default for `i` is 1, and the default for
--- `j` is #list. If `i` is greater than `j`, returns the empty string.
---@overload fun(list:string[]):string
---@overload fun(list:string[], sep:string):string
---@overload fun(list:string[], sep:string, i:integer):string
---@param list string[]
---@param sep string
---@param i integer
---@param j integer
---@param list table
---@param sep? string
---@param i? integer
---@param j? integer
---@return string
---@nodiscard
function table.concat(list, sep, i, j) end

---
Expand Down
35 changes: 23 additions & 12 deletions crates/emmylua_code_analysis/src/compilation/analyzer/decl/exprs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ use emmylua_parser::{
};

use crate::{
compilation::analyzer::unresolve::UnResolveTableField,
db_index::{LuaDecl, LuaMember, LuaMemberKey, LuaMemberOwner},
FileId, InFiled, LuaDeclExtra, LuaDeclId, LuaMemberFeature, LuaMemberId, LuaSignatureId,
FileId, InFiled, InferFailReason, LuaDeclExtra, LuaDeclId, LuaMemberFeature, LuaMemberId,
LuaSignatureId,
};

use super::DeclAnalyzer;
Expand Down Expand Up @@ -180,18 +182,33 @@ fn analyze_closure_params(
Some(())
}

pub fn analyze_table_expr(analyzer: &mut DeclAnalyzer, expr: LuaTableExpr) -> Option<()> {
if expr.is_object() {
pub fn analyze_table_expr(analyzer: &mut DeclAnalyzer, table_expr: LuaTableExpr) -> Option<()> {
if table_expr.is_object() {
let file_id = analyzer.get_file_id();
let owner_id = LuaMemberOwner::Element(InFiled {
file_id,
value: expr.get_range(),
value: table_expr.get_range(),
});
let decl_feature = if analyzer.is_meta {
LuaMemberFeature::MetaDefine
} else {
LuaMemberFeature::FileDefine
};

for field in expr.get_fields() {
for field in table_expr.get_fields() {
if let Some(field_key) = field.get_field_key() {
let key: LuaMemberKey = field_key.into();
let key: LuaMemberKey = field_key.clone().into();
if key.is_none() {
if let Some(field_expr) = field_key.get_expr() {
let unresolve_member = UnResolveTableField {
file_id: analyzer.get_file_id(),
table_expr: table_expr.clone(),
field: field.clone(),
decl_feature,
reason: InferFailReason::UnResolveExpr(field_expr.clone()),
};
analyzer.add_unresolved(unresolve_member.into());
}
continue;
}

Expand All @@ -201,12 +218,6 @@ pub fn analyze_table_expr(analyzer: &mut DeclAnalyzer, expr: LuaTableExpr) -> Op
field.get_syntax_id(),
);

let decl_feature = if analyzer.is_meta {
LuaMemberFeature::MetaDefine
} else {
LuaMemberFeature::FileDefine
};

let member_id = LuaMemberId::new(field.get_syntax_id(), file_id);
let member = LuaMember::new(owner_id.clone(), member_id, key, decl_feature, None);
analyzer.db.get_member_index_mut().add_member(member);
Expand Down
28 changes: 19 additions & 9 deletions crates/emmylua_code_analysis/src/compilation/analyzer/decl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::{
profile::Profile,
};

use super::AnalyzeContext;
use super::{unresolve::UnResolve, AnalyzeContext};
use emmylua_parser::{LuaAst, LuaAstNode, LuaChunk, LuaFuncStat, LuaSyntaxKind, LuaVarExpr};
use rowan::{TextRange, TextSize, WalkEvent};

Expand All @@ -18,14 +18,18 @@ use crate::{

pub(crate) fn analyze(db: &mut DbIndex, context: &mut AnalyzeContext) {
let _p = Profile::cond_new("decl analyze", context.tree_list.len() > 1);
for in_filed_tree in context.tree_list.iter() {
let tree_list = context.tree_list.clone();
for in_filed_tree in tree_list.iter() {
db.get_reference_index_mut()
.create_local_reference(in_filed_tree.file_id);
let mut analyzer =
DeclAnalyzer::new(db, in_filed_tree.file_id, in_filed_tree.value.clone());
analyzer.analyze();
let decl_tree = analyzer.get_decl_tree();
let (decl_tree, unresolved) = analyzer.get_decl_tree();
db.get_decl_index_mut().add_decl_tree(decl_tree);
for unresolve in unresolved {
context.add_unresolve(unresolve);
}
}
}

Expand Down Expand Up @@ -138,6 +142,7 @@ pub struct DeclAnalyzer<'a> {
decl: LuaDeclarationTree,
scopes: Vec<LuaScopeId>,
is_meta: bool,
pub unresolved: Vec<UnResolve>,
}

impl<'a> DeclAnalyzer<'a> {
Expand All @@ -148,6 +153,7 @@ impl<'a> DeclAnalyzer<'a> {
decl: LuaDeclarationTree::new(file_id),
scopes: Vec::new(),
is_meta: false,
unresolved: Vec::new(),
}
}

Expand All @@ -161,8 +167,16 @@ impl<'a> DeclAnalyzer<'a> {
}
}

pub fn get_decl_tree(self) -> LuaDeclarationTree {
self.decl
pub fn get_file_id(&self) -> FileId {
self.decl.file_id()
}

pub fn add_unresolved(&mut self, unresolved: UnResolve) {
self.unresolved.push(unresolved);
}

pub fn get_decl_tree(self) -> (LuaDeclarationTree, Vec<UnResolve>) {
(self.decl, self.unresolved)
}

pub fn create_scope(&mut self, range: TextRange, kind: LuaScopeKind) {
Expand Down Expand Up @@ -206,10 +220,6 @@ impl<'a> DeclAnalyzer<'a> {
pub fn find_decl(&self, name: &str, position: TextSize) -> Option<&LuaDecl> {
self.decl.find_local_decl(name, position)
}

pub fn get_file_id(&self) -> FileId {
self.decl.file_id()
}
}

fn is_method_func_stat(stat: &LuaFuncStat) -> Option<bool> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,20 +61,20 @@ pub fn analyze_ref_assign(
}

let (var_exprs, value_exprs) = assign_stat.get_var_and_expr_list();
let index = var_exprs
let var_index = var_exprs
.iter()
.position(|it| it.get_position() == var_expr.get_position())?;

if value_exprs.len() == 0 {
return None;
}

let (value_expr, idx) = if let Some(expr) = value_exprs.get(index) {
let (value_expr, idx) = if let Some(expr) = value_exprs.get(var_index) {
(expr.clone(), 0)
} else {
(
value_exprs.last()?.clone(),
(index - value_exprs.len()) as i32,
(var_index - (value_exprs.len() - 1)) as i32,
Copy link

Copilot AI Mar 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change in the arithmetic for computing the index offset in the else branch may be error prone; please double-check that the new formula correctly determines the intended offset compared to the previous implementation.

Suggested change
(var_index - (value_exprs.len() - 1)) as i32,
(var_index - value_exprs.len() + 1) as i32,

Copilot uses AI. Check for mistakes.
)
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,24 @@ pub fn analyze_local_stat(analyzer: &mut LuaAnalyzer, local_stat: LuaLocalStat)
if let LuaType::MuliReturn(multi) = expr_type {
expr_type = multi.get_type(0)?.clone();
}

let decl_id = LuaDeclId::new(analyzer.file_id, position);
// 当`call`参数包含表时, 表可能未被分析, 需要延迟
if let LuaType::Instance(instance) = &expr_type {
if instance.get_base().is_unknown() {
if call_expr_has_effect_table_arg(expr).is_some() {
let unresolve = UnResolveDecl {
file_id: analyzer.file_id,
decl_id,
expr: expr.clone(),
ret_idx: 0,
reason: InferFailReason::UnResolveExpr(expr.clone()),
};
analyzer.add_unresolved(unresolve.into());
continue;
}
}
}

merge_decl_expr_type(analyzer.db, &mut analyzer.infer_cache, decl_id, expr_type);
}
Err(InferFailReason::None) => {
Expand Down Expand Up @@ -134,6 +150,20 @@ pub fn analyze_local_stat(analyzer: &mut LuaAnalyzer, local_stat: LuaLocalStat)
Some(())
}

fn call_expr_has_effect_table_arg(expr: &LuaExpr) -> Option<()> {
if let LuaExpr::CallExpr(call_expr) = expr {
let args_list = call_expr.get_args_list()?;
for arg in args_list.get_args() {
if let LuaExpr::TableExpr(table_expr) = arg {
if !table_expr.is_empty() {
return Some(());
}
}
}
}
None
}

#[derive(Debug, Clone)]
enum TypeOwner {
Decl(LuaDeclId),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@ mod resolve_closure;
use crate::{
db_index::{DbIndex, LuaDeclId, LuaMemberId, LuaSignatureId},
profile::Profile,
FileId, InferFailReason, LuaSemanticDeclId,
FileId, InferFailReason, LuaMemberFeature, LuaSemanticDeclId,
};
use check_reason::{resolve_all_reason, resolve_as_any};
use emmylua_parser::{LuaAssignStat, LuaCallExpr, LuaExpr, LuaFuncStat, LuaTableField};
use emmylua_parser::{
LuaAssignStat, LuaCallExpr, LuaExpr, LuaFuncStat, LuaTableExpr, LuaTableField,
};
use infer_manager::InferCacheManager;
pub use merge_type::{merge_decl_expr_type, merge_member_type};
use resolve::{
try_resolve_decl, try_resolve_iter_var, try_resolve_member, try_resolve_module,
try_resolve_module_ref, try_resolve_return_point,
try_resolve_module_ref, try_resolve_return_point, try_resolve_table_field,
};
use resolve_closure::{
try_resolve_closure_params, try_resolve_closure_parent_params, try_resolve_closure_return,
Expand Down Expand Up @@ -92,6 +94,9 @@ fn try_resolve(
try_resolve_closure_parent_params(db, config, un_resolve_closure_params)
.unwrap_or(false)
}
UnResolve::TableField(un_resolve_table_field) => {
try_resolve_table_field(db, config, un_resolve_table_field).unwrap_or(false)
}
UnResolve::None => continue,
};

Expand All @@ -116,6 +121,7 @@ pub enum UnResolve {
ClosureReturn(Box<UnResolveClosureReturn>),
ClosureParentParams(Box<UnResolveParentClosureParams>),
ModuleRef(Box<UnResolveModuleRef>),
TableField(Box<UnResolveTableField>),
}

#[allow(dead_code)]
Expand All @@ -140,6 +146,7 @@ impl UnResolve {
UnResolve::ClosureParentParams(un_resolve_closure_params) => {
Some(un_resolve_closure_params.file_id)
}
UnResolve::TableField(un_resolve_table_field) => Some(un_resolve_table_field.file_id),
UnResolve::ModuleRef(_) => None,
UnResolve::None => None,
}
Expand Down Expand Up @@ -279,3 +286,18 @@ impl From<UnResolveParentClosureParams> for UnResolve {
UnResolve::ClosureParentParams(Box::new(un_resolve_closure_params))
}
}

#[derive(Debug)]
pub struct UnResolveTableField {
pub file_id: FileId,
pub table_expr: LuaTableExpr,
pub field: LuaTableField,
pub decl_feature: LuaMemberFeature,
pub reason: InferFailReason,
}

impl From<UnResolveTableField> for UnResolve {
fn from(un_resolve_table_field: UnResolveTableField) -> Self {
UnResolve::TableField(Box::new(un_resolve_table_field))
}
}
Loading