Skip to content

Commit 14f9876

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

2 files changed

Lines changed: 123 additions & 9 deletions

File tree

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: 59 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1+
use emmylua_parser::{
2+
LuaAstNode, LuaClosureExpr, LuaCommentOwner, LuaDocTagParam, LuaDocTagReturn, LuaStat,
3+
LuaTableField,
4+
};
15
use hashbrown::HashSet;
26
use std::sync::Arc;
37

48
use crate::{
5-
DbIndex, LuaOperatorMetaMethod, LuaOperatorOwner, LuaTypeDeclId, LuaUnionType,
9+
DbIndex, LuaOperatorMetaMethod, LuaOperatorOwner, LuaSignatureId, LuaTypeDeclId, LuaUnionType,
610
db_index::{LuaFunctionType, LuaType},
711
semantic::{
812
generic::{TypeSubstitutor, instantiate_type_generic},
@@ -91,9 +95,14 @@ fn collect_callable_overload_groups_inner(
9195
let Some(signature) = db.get_signature_index().get(sig_id) else {
9296
return Ok(());
9397
};
94-
// 主签名描述了函数实现本身, 当它和 overload 同时可匹配时应作为同等匹配下的优先候选.
95-
let mut overloads = vec![signature.to_doc_func_type()];
96-
overloads.extend(signature.overloads.iter().cloned());
98+
let main_signature = signature.to_doc_func_type();
99+
let mut overloads = signature.overloads.clone();
100+
// 如果存在参数/返回值签名, 那么他的优先级在同等条件下是更高的
101+
if signature_has_declared_docs(db, sig_id) {
102+
overloads.insert(0, main_signature);
103+
} else {
104+
overloads.push(main_signature);
105+
}
97106
groups.push(overloads);
98107
}
99108
LuaType::Instance(instance) => {
@@ -136,7 +145,9 @@ fn push_call_operator_overload_group(
136145
};
137146

138147
// 同一个 owner 的 call operators 作为一个 overload group, 由调用方再做参数匹配.
139-
let mut overloads = Vec::new();
148+
let mut declared_signatures = Vec::new();
149+
let mut doc_functions = Vec::new();
150+
let mut undeclared_signatures = Vec::new();
140151
for operator_id in operator_ids {
141152
let Some(operator) = db.get_operator_index().get_operator(operator_id) else {
142153
continue;
@@ -148,22 +159,29 @@ fn push_call_operator_overload_group(
148159
}
149160

150161
match func_type {
151-
LuaType::DocFunction(func) => overloads.push(func),
162+
LuaType::DocFunction(func) => doc_functions.push(func),
152163
LuaType::Signature(signature_id) => {
153164
let Some(signature) = db.get_signature_index().get(&signature_id) else {
154165
continue;
155166
};
156167
// 未解析返回的 signature 不能安全转换成候选, 这里先跳过.
157168
if signature.is_resolve_return() {
158-
overloads.push(signature.to_call_operator_func_type());
169+
let function = signature.to_call_operator_func_type();
170+
if signature_has_declared_docs(db, &signature_id) {
171+
declared_signatures.push(function);
172+
} else {
173+
undeclared_signatures.push(function);
174+
}
159175
}
160176
}
161177
_ => {}
162178
}
163179
}
164180

165-
if !overloads.is_empty() {
166-
groups.push(overloads);
181+
declared_signatures.extend(doc_functions);
182+
declared_signatures.extend(undeclared_signatures);
183+
if !declared_signatures.is_empty() {
184+
groups.push(declared_signatures);
167185
}
168186
}
169187

@@ -260,3 +278,35 @@ fn call_operator_self_for_type_id(
260278
visiting_aliases.remove(type_id);
261279
has_call.then(|| ty.clone())
262280
}
281+
282+
fn signature_has_declared_docs(db: &DbIndex, signature_id: &LuaSignatureId) -> bool {
283+
let Some(tree) = db.get_vfs().get_syntax_tree(&signature_id.get_file_id()) else {
284+
return false;
285+
};
286+
let Some(token) = tree
287+
.get_red_root()
288+
.token_at_offset(signature_id.get_position())
289+
.right_biased()
290+
else {
291+
return false;
292+
};
293+
let Some(closure) = token
294+
.parent_ancestors()
295+
.find_map(LuaClosureExpr::cast)
296+
.filter(|closure| closure.get_position() == signature_id.get_position())
297+
else {
298+
return false;
299+
};
300+
let comments = if let Some(table_field) = closure.get_parent::<LuaTableField>() {
301+
table_field.get_comments()
302+
} else if let Some(stat) = closure.ancestors::<LuaStat>().next() {
303+
stat.get_comments()
304+
} else {
305+
return false;
306+
};
307+
308+
comments.into_iter().any(|comment| {
309+
comment.children::<LuaDocTagParam>().next().is_some()
310+
|| comment.children::<LuaDocTagReturn>().next().is_some()
311+
})
312+
}

0 commit comments

Comments
 (0)