Skip to content
Open

update #1113

Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
895e85f
refactor(generic): Unified Generic Structure
xuhuanzy May 31, 2026
f465a77
refactor(generic): rename `instantiate_func_generic` to `infer_call_g…
xuhuanzy May 31, 2026
114d707
chore: clean up generic code
xuhuanzy May 31, 2026
53cc7d6
feat: add generic `const` modifier
xuhuanzy May 31, 2026
e8a53b6
refactor(generic): remove `LuaType::ConstTplRef`
xuhuanzy Jun 1, 2026
08576b6
feat(diagnostic): The deprecation checker now inspects type references.
xuhuanzy Jun 1, 2026
c3edd46
update generic
xuhuanzy Jun 1, 2026
9dcb5be
refactor(attribute): switch to a solution similar to C#.
xuhuanzy Jun 8, 2026
25e0c7a
perf: Improve subtype checking performance
xuhuanzy Jun 8, 2026
ffb172b
perf: hover
xuhuanzy Jun 9, 2026
6901ada
perf: find members
xuhuanzy Jun 10, 2026
35be13b
update generic: support for capturing generic parameters of table
xuhuanzy Jun 10, 2026
68296d6
update infer_call_generic
xuhuanzy Jun 10, 2026
3fd1b50
refactor(hover): optimize functions overload
xuhuanzy Jun 11, 2026
a5c2496
refactor(hover): optimize `table field` type is a signature
xuhuanzy Jun 12, 2026
1405446
perf: hover
xuhuanzy Jun 12, 2026
4621da4
fix(hover): call function
xuhuanzy Jun 13, 2026
313da4f
fix(hover): generic function call
xuhuanzy Jun 13, 2026
d3a1563
refactor(hover): no longer special rendering for `@return_overload`
xuhuanzy Jun 13, 2026
91bc734
refactor(diagnostic): param type check for all overloads
xuhuanzy Jun 14, 2026
238246a
refactor(diagnostic): optimize param check
xuhuanzy Jun 14, 2026
b0d6666
clean code
xuhuanzy Jun 14, 2026
b0a0f00
fix(diagnostic): param type check
xuhuanzy Jun 14, 2026
098fb12
perf: filter callable overloads
xuhuanzy Jun 15, 2026
1b588b7
perf: call completion
xuhuanzy Jun 15, 2026
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
18 changes: 13 additions & 5 deletions crates/emmylua_code_analysis/resources/std/builtin.lua
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@
--- built-in type for Rawget
--- @alias std.RawGet<T, K> unknown

--- @deprecated use `const T` as a replacement, for example `---@generic const T`.
---
--- built-in type for generic template, for match integer const and true/false
--- @alias std.ConstTpl<T> unknown
Expand Down Expand Up @@ -170,9 +171,12 @@

--- attribute

--- @class Attribute

---
--- Deprecated. Receives an optional message parameter.
--- @attribute deprecated(message: string?)
--- @class deprecated: Attribute
--- @overload fun(message?: string)

---
--- Language Server Optimization Items.
Expand All @@ -181,13 +185,15 @@
--- - `check_table_field`: Skip the assign check for table fields. It is recommended to use this option for all large configuration tables.
--- - `delayed_definition`: Indicates that the type of the variable is determined by the first assignment.
--- Only valid for `local` declarations with no initial value.
--- @attribute lsp_optimization(code: "check_table_field"|"delayed_definition")
--- @class lsp_optimization: Attribute
--- @overload fun(code: "check_table_field"|"delayed_definition")

---
--- Index field alias, will be displayed in `hint` and `completion`.
---
--- Receives a string parameter for the alias name.
--- @attribute index_alias(name: string)
--- @class index_alias: Attribute
--- @overload fun(name: string)

---
--- This attribute must be applied to function parameters, and the function parameter's type must be a string template generic,
Expand All @@ -200,7 +206,8 @@
--- - `return_mode`: Constructor return strategy. `"self"` forces `self`, `"doc"` uses the documented return type,
--- and `"default"` prefers the documented return type and falls back to `self`.
--- Defaults to `"default"`
--- @attribute constructor(name: string, root_class: string?, strip_self: boolean?, return_mode: "self"|"doc"|"default"?)
--- @class constructor: Attribute
--- @overload fun(name: string, root_class?: string, strip_self?: boolean, return_mode?: "self"|"doc"|"default")

---
--- Associates `getter` and `setter` methods with a field. Currently provides only definition navigation functionality,
Expand All @@ -210,4 +217,5 @@
--- - `convention`: Naming convention, defaults to `camelCase`. Implicitly adds `get` and `set` prefixes. eg: `_age` -> `getAge`, `setAge`.
--- - `getter`: Getter method name. Takes precedence over `convention`.
--- - `setter`: Setter method name. Takes precedence over `convention`.
--- @attribute field_accessor(convention: "camelCase"|"PascalCase"|"snake_case"|nil, getter: string?, setter: string?)
--- @class field_accessor: Attribute
--- @overload fun(convention?: "camelCase"|"PascalCase"|"snake_case", getter?: string, setter?: string)
14 changes: 7 additions & 7 deletions crates/emmylua_code_analysis/resources/std/global.lua
Original file line number Diff line number Diff line change
Expand Up @@ -277,9 +277,9 @@ function rawequal(v1, v2) end
---
--- Gets the real value of `table[index]`, the `__index` metamethod. `table`
--- must be a table; `index` may be any value.
--- @generic T, K
--- @generic const T, const K
--- @param table T
--- @param index std.ConstTpl<K>
--- @param index K
--- @return std.RawGet<T, K>
function rawget(table, index) end

Expand Down Expand Up @@ -340,8 +340,8 @@ function require(modname) end
--- `index`. a negative number indexes from the end (-1 is the last argument).
--- Otherwise, `index` must be the string "#", and `select` returns
--- the total number of extra arguments it received.
--- @generic T, Num: integer | '#'
--- @param index std.ConstTpl<Num>
--- @generic T, const Num: integer | '#'
--- @param index Num
--- @param ... T...
--- @return std.Select<T..., Num>
function select(index, ...) end
Expand Down Expand Up @@ -460,9 +460,9 @@ function xpcall(f, msgh, ...) end

--- @version 5.1, JIT
---
--- @generic T, Start: integer, End: integer
--- @param i? std.ConstTpl<Start>
--- @param j? std.ConstTpl<End>
--- @generic const T, const Start: integer, const End: integer
--- @param i? Start
--- @param j? End
--- @param list T
--- @return std.Unpack<T, Start, End>
function unpack(list, i, j) end
Expand Down
6 changes: 3 additions & 3 deletions crates/emmylua_code_analysis/resources/std/table.lua
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,9 @@ function table.sort(list, comp) end
--- Returns the elements from the given list. This function is equivalent to
--- return `list[i]`, `list[i+1]`, `···`, `list[j]`
--- By default, i is 1 and j is #list.
--- @generic T, Start: integer, End: integer
--- @param i? std.ConstTpl<Start>
--- @param j? std.ConstTpl<End>
--- @generic const T, const Start: integer, const End: integer
--- @param i? Start
--- @param j? End
--- @param list T
--- @return std.Unpack<T, Start, End>
function table.unpack(list, i, j) end
Expand Down
23 changes: 2 additions & 21 deletions crates/emmylua_code_analysis/src/compilation/analyzer/decl/docs.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use emmylua_parser::{
LuaAstNode, LuaAstToken, LuaComment, LuaDocTag, LuaDocTagAlias, LuaDocTagAttribute,
LuaDocTagClass, LuaDocTagEnum, LuaDocTagMeta, LuaDocTagNamespace, LuaDocTagUsing,
LuaDocTypeFlag,
LuaAstNode, LuaAstToken, LuaComment, LuaDocTag, LuaDocTagAlias, LuaDocTagClass, LuaDocTagEnum,
LuaDocTagMeta, LuaDocTagNamespace, LuaDocTagUsing, LuaDocTypeFlag,
};
use flagset::FlagSet;
use rowan::TextRange;
Expand Down Expand Up @@ -100,24 +99,6 @@ pub fn analyze_doc_tag_alias(analyzer: &mut DeclAnalyzer, alias: LuaDocTagAlias)
Some(())
}

pub fn analyze_doc_tag_attribute(
analyzer: &mut DeclAnalyzer,
attribute: LuaDocTagAttribute,
) -> Option<()> {
let name_token = attribute.get_name_token()?;
let name = name_token.get_name_text().to_string();
let range = name_token.syntax().text_range();

add_type_decl(
analyzer,
&name,
range,
LuaDeclTypeKind::Attribute,
FlagSet::default(),
);
Some(())
}

pub fn analyze_doc_tag_namespace(
analyzer: &mut DeclAnalyzer,
namespace: LuaDocTagNamespace,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,6 @@ fn walk_node_enter(analyzer: &mut DeclAnalyzer, node: LuaAst) {
LuaAst::LuaDocTagAlias(doc_tag) => {
docs::analyze_doc_tag_alias(analyzer, doc_tag);
}
LuaAst::LuaDocTagAttribute(doc_tag) => {
docs::analyze_doc_tag_attribute(analyzer, doc_tag);
}
LuaAst::LuaDocTagNamespace(doc_tag) => {
docs::analyze_doc_tag_namespace(analyzer, doc_tag);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use crate::{
infer_type::infer_type,
tags::{get_owner_id, report_orphan_tag},
},
get_attribute_constructor_params, is_attribute_class,
};

pub fn analyze_tag_attribute_use(
Expand Down Expand Up @@ -64,27 +65,19 @@ pub fn infer_attribute_uses(
LuaDocType::Name(attribute_use.get_type()?),
);
if let LuaType::Ref(type_id) = attribute_type {
if !is_attribute_class(analyzer.type_context.db, &type_id) {
continue;
}

let arg_types: Vec<LuaType> = attribute_use
.get_arg_list()
.map(|arg_list| arg_list.get_args().map(infer_attribute_arg_type).collect())
.unwrap_or_default();
let param_names = analyzer
.type_context
.db
.get_type_index()
.get_type_decl(&type_id)
.and_then(|decl| decl.get_attribute_type())
.and_then(|typ| match typ {
LuaType::DocAttribute(attr_type) => Some(
attr_type
.get_params()
.iter()
.map(|(name, _)| name.clone())
.collect::<Vec<_>>(),
),
_ => None,
})
.unwrap_or_default();
let param_names: Vec<String> =
get_attribute_constructor_params(analyzer.type_context.db, &type_id, &arg_types)
.into_iter()
.map(|(name, _)| name)
.collect();

let mut params = Vec::new();
for (idx, arg_type) in arg_types.into_iter().enumerate() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,32 @@ use rowan::{TextRange, TextSize};
use smol_str::SmolStr;
use std::sync::Arc;

use crate::{GenericParam, GenericTpl, GenericTplId, LuaType};
use crate::{GenericParam, GenericTpl, GenericTplId};

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_param(
&mut self,
scope_id: GenericScopeId,
param: GenericParam,
) -> Option<GenericTplId>;

fn append_generic_params(&mut self, scope_id: GenericScopeId, params: Vec<GenericParam>) {
for param in params {
self.append_generic_param(scope_id, param);
let _ = self.append_generic_param(scope_id, param);
}
}

fn find_generic(
&self,
position: TextSize,
name: &str,
) -> Option<(GenericTplId, Option<LuaType>, Option<LuaType>)>;
fn find_generic(&self, position: TextSize, name: &str) -> Option<(GenericTplId, GenericParam)>;

fn generic_param_mut(&mut self, tpl_id: GenericTplId) -> Option<&mut GenericParam>;

fn mark_generic_const(&mut self, tpl_id: GenericTplId) -> Option<GenericParam> {
let param = self.generic_param_mut(tpl_id)?;
param.is_const = true;
Some(param.clone())
}
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -63,36 +71,38 @@ 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.
/// return (GenericTplId, constraint, default)
fn find_generic(
&self,
position: TextSize,
name: &str,
) -> Option<(GenericTplId, Option<LuaType>, Option<LuaType>)> {
fn find_generic(&self, position: TextSize, name: &str) -> Option<(GenericTplId, GenericParam)> {
for scope in self.scopes.iter().rev() {
if !scope.contains(position) {
continue;
}

if let Some((id, param)) = scope.params.get(name) {
return Some((
*id,
param.type_constraint.clone(),
param.default_type.clone(),
));
return Some((*id, param.clone()));
}
}

None
}

fn generic_param_mut(&mut self, tpl_id: GenericTplId) -> Option<&mut GenericParam> {
for scope in self.scopes.iter_mut().rev() {
for (id, param) in scope.params.values_mut() {
if *id == tpl_id {
return Some(param);
}
}
}

Expand Down Expand Up @@ -131,10 +141,11 @@ impl FileGenericScope {
self.next_tpl_id.is_func()
}

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

fn contains(&self, position: TextSize) -> bool {
Expand Down Expand Up @@ -175,18 +186,19 @@ impl ConditionalInferIndex {

let tpl_id = GenericTplId::ConditionalInfer(self.next_infer_id);
self.next_infer_id += 1;
let param = GenericParam::new(SmolStr::new(name), None, None, false, None);
let tpl = Arc::new(GenericTpl::new(
tpl_id,
SmolStr::new(name).into(),
None,
None,
param.name.clone(),
param.constraint.clone(),
param.default.clone(),
param.is_const,
param.attributes.clone(),
));

let scope = &mut self.scopes[scope_idx];
scope.bindings.insert(name.to_string(), tpl.clone());
scope
.params
.push(GenericParam::new(SmolStr::new(name), None, None, None));
scope.params.push(param);
Some(tpl)
}

Expand Down
Loading
Loading