Skip to content

Commit c35588f

Browse files
committed
fix(overload): prioritize explicitly documented signatures
fix #1162
1 parent 4c50012 commit c35588f

4 files changed

Lines changed: 90 additions & 8 deletions

File tree

crates/emmylua_code_analysis/src/compilation/analyzer/doc/type_ref_tags.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ pub fn analyze_param(analyzer: &mut DocAnalyzer, tag: LuaDocTagParam) -> Option<
213213
.get_db()
214214
.get_signature_index_mut()
215215
.get_or_create(id);
216+
signature.has_explicit_docs = true;
216217
let param_info = LuaDocParamInfo {
217218
name: name.clone(),
218219
type_ref: type_ref.clone(),
@@ -261,6 +262,7 @@ pub fn analyze_return(analyzer: &mut DocAnalyzer, tag: LuaDocTagReturn) -> Optio
261262
.collect::<Vec<_>>();
262263

263264
bind_signature_return_docs(analyzer, &tag, |signature| {
265+
signature.has_explicit_docs = true;
264266
signature.return_docs.extend(return_infos);
265267
})
266268
}

crates/emmylua_code_analysis/src/db_index/signature/signature.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use crate::{
2121
pub struct LuaSignature {
2222
pub generic_params: Vec<GenericParam>,
2323
pub overloads: Vec<Arc<LuaFunctionType>>,
24+
pub has_explicit_docs: bool,
2425
pub param_docs: HashMap<usize, LuaDocParamInfo>,
2526
pub params: Vec<String>,
2627
pub return_docs: Vec<LuaDocReturnInfo>,
@@ -49,6 +50,7 @@ impl LuaSignature {
4950
Self {
5051
generic_params: Vec::new(),
5152
overloads: Vec::new(),
53+
has_explicit_docs: false,
5254
param_docs: HashMap::new(),
5355
params: Vec::new(),
5456
return_docs: Vec::new(),

crates/emmylua_code_analysis/src/diagnostic/test/assign_type_mismatch_test.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -891,6 +891,70 @@ return t
891891
));
892892
}
893893

894+
#[test]
895+
fn test_literal_argument_selects_overload_return_type() {
896+
let mut ws = VirtualWorkspace::new();
897+
assert!(ws.has_no_diagnostic(
898+
DiagnosticCode::AssignTypeMismatch,
899+
r#"
900+
---@class A
901+
---@class B
902+
local C
903+
904+
---@overload fun(): A
905+
---@overload fun(type: "fluid"): B
906+
function C.name(type) end
907+
908+
local tmp = C.name("fluid")
909+
"#
910+
));
911+
}
912+
913+
#[test]
914+
fn test_explicit_signature_param_keeps_main_return_priority() {
915+
let mut ws = VirtualWorkspace::new();
916+
assert!(ws.has_no_diagnostic(
917+
DiagnosticCode::AssignTypeMismatch,
918+
r#"
919+
---@class A
920+
---@class B
921+
local C
922+
local a ---@type A
923+
924+
---@overload fun(type: "fluid"): B
925+
---@param type "fluid"
926+
function C.name(type)
927+
return a
928+
end
929+
930+
local tmp = C.name("fluid")
931+
"#
932+
));
933+
}
934+
935+
#[test]
936+
fn test_table_field_explicit_signature_param_keeps_main_return_priority() {
937+
let mut ws = VirtualWorkspace::new();
938+
assert!(ws.has_no_diagnostic(
939+
DiagnosticCode::AssignTypeMismatch,
940+
r#"
941+
---@class A
942+
---@class B
943+
local a ---@type A
944+
945+
local C = {
946+
---@overload fun(type: "fluid"): B
947+
---@param type "fluid"
948+
name = function(type)
949+
return a
950+
end
951+
}
952+
953+
local tmp = C.name("fluid")
954+
"#
955+
));
956+
}
957+
894958
#[test]
895959
fn test_table_pack_in_function() {
896960
let mut ws = VirtualWorkspace::new_with_init_std_lib();

crates/emmylua_code_analysis/src/semantic/overload_resolve/collect_overloads.rs

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,14 @@ fn collect_callable_overload_groups_inner(
9191
let Some(signature) = db.get_signature_index().get(sig_id) else {
9292
return Ok(());
9393
};
94-
// 主签名描述了函数实现本身, 当它和 overload 同时可匹配时应作为同等匹配下的优先候选.
95-
let mut overloads = vec![signature.to_doc_func_type()];
96-
overloads.extend(signature.overloads.iter().cloned());
94+
let main_signature = signature.to_doc_func_type();
95+
let mut overloads = signature.overloads.clone();
96+
// 显式声明参数或返回值的主签名, 在匹配程度相同时优先于 overload.
97+
if signature.has_explicit_docs {
98+
overloads.insert(0, main_signature);
99+
} else {
100+
overloads.push(main_signature);
101+
}
97102
groups.push(overloads);
98103
}
99104
LuaType::Instance(instance) => {
@@ -136,7 +141,9 @@ fn push_call_operator_overload_group(
136141
};
137142

138143
// 同一个 owner 的 call operators 作为一个 overload group, 由调用方再做参数匹配.
139-
let mut overloads = Vec::new();
144+
let mut declared_signatures = Vec::new();
145+
let mut doc_functions = Vec::new();
146+
let mut undeclared_signatures = Vec::new();
140147
for operator_id in operator_ids {
141148
let Some(operator) = db.get_operator_index().get_operator(operator_id) else {
142149
continue;
@@ -148,22 +155,29 @@ fn push_call_operator_overload_group(
148155
}
149156

150157
match func_type {
151-
LuaType::DocFunction(func) => overloads.push(func),
158+
LuaType::DocFunction(func) => doc_functions.push(func),
152159
LuaType::Signature(signature_id) => {
153160
let Some(signature) = db.get_signature_index().get(&signature_id) else {
154161
continue;
155162
};
156163
// 未解析返回的 signature 不能安全转换成候选, 这里先跳过.
157164
if signature.is_resolve_return() {
158-
overloads.push(signature.to_call_operator_func_type());
165+
let function = signature.to_call_operator_func_type();
166+
if signature.has_explicit_docs {
167+
declared_signatures.push(function);
168+
} else {
169+
undeclared_signatures.push(function);
170+
}
159171
}
160172
}
161173
_ => {}
162174
}
163175
}
164176

165-
if !overloads.is_empty() {
166-
groups.push(overloads);
177+
declared_signatures.extend(doc_functions);
178+
declared_signatures.extend(undeclared_signatures);
179+
if !declared_signatures.is_empty() {
180+
groups.push(declared_signatures);
167181
}
168182
}
169183

0 commit comments

Comments
 (0)