Skip to content

Commit 4429a9c

Browse files
Merge pull request #22107 from Amit5601/fix-goto-def-13332
fix: resolve comparison operators to explicit trait impls (#13332)
2 parents cc5bc48 + 32800d4 commit 4429a9c

1 file changed

Lines changed: 81 additions & 0 deletions

File tree

crates/ide/src/goto_definition.rs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@ pub(crate) fn goto_definition(
9595
continue;
9696
}
9797

98+
if let Some(n) = find_definition_for_comparison_operators(sema, &token.value) {
99+
navs.extend(n);
100+
continue;
101+
}
102+
98103
let parent = token.value.parent()?;
99104

100105
if let Some(question_mark_conversion) = goto_question_mark_conversions(sema, &parent) {
@@ -264,6 +269,62 @@ fn find_definition_for_known_blanket_dual_impls(
264269
Some(def_to_nav(sema, def))
265270
}
266271

272+
// If the token is a comparison operator (!=, <, <=, >, >=) that resolves to a default trait method, navigate to the corresponding primary method (eq for ne, partial_cmp for the others).
273+
fn find_definition_for_comparison_operators(
274+
sema: &Semantics<'_, RootDatabase>,
275+
original_token: &SyntaxToken,
276+
) -> Option<Vec<NavigationTarget>> {
277+
let bin_expr = ast::BinExpr::cast(original_token.parent()?)?;
278+
279+
let f = sema.resolve_bin_expr(&bin_expr)?;
280+
let assoc = f.as_assoc_item(sema.db)?;
281+
282+
let lhs_type = sema.type_of_expr(&bin_expr.lhs()?)?.original;
283+
let rhs_type = sema.type_of_expr(&bin_expr.rhs()?)?.original;
284+
285+
let t = match assoc.container(sema.db) {
286+
hir::AssocItemContainer::Trait(t) => t,
287+
hir::AssocItemContainer::Impl(_) => return None, // Already implemented by the type
288+
};
289+
290+
let fn_name = f.name(sema.db);
291+
let fn_name_str = fn_name.as_str();
292+
293+
let trait_name = t.name(sema.db);
294+
let trait_name_str = trait_name.as_str();
295+
296+
let (target_fn_name, expected_trait) = match fn_name_str {
297+
"ne" => ("eq", "PartialEq"),
298+
"lt" | "le" | "gt" | "ge" => ("partial_cmp", "PartialOrd"),
299+
_ => return None,
300+
};
301+
302+
if trait_name_str != expected_trait {
303+
return None;
304+
}
305+
306+
let primary_f = t.items(sema.db).into_iter().find_map(|item| {
307+
if let hir::AssocItem::Function(func) = item
308+
&& func.name(sema.db).as_str() == target_fn_name
309+
{
310+
return Some(func);
311+
}
312+
None
313+
})?;
314+
315+
// Chalk requires ALL trait substitutions, including `Self`!
316+
// We must pass [Self, Rhs]
317+
let resolved_f = sema.resolve_trait_impl_method(
318+
lhs_type.clone(),
319+
t,
320+
primary_f,
321+
[lhs_type.clone(), rhs_type.clone()],
322+
)?;
323+
324+
let def = Definition::from(resolved_f);
325+
326+
Some(def_to_nav(sema, def))
327+
}
267328
fn try_lookup_include_path(
268329
sema: &Semantics<'_, RootDatabase>,
269330
token: InFile<ast::String>,
@@ -4099,4 +4160,24 @@ fn foo() -> Result<(), Bar> {
40994160
"#,
41004161
);
41014162
}
4163+
4164+
#[test]
4165+
fn goto_definition_for_comparison_operators() {
4166+
check(
4167+
r#"
4168+
//- minicore: eq, ord
4169+
struct Foo;
4170+
impl PartialEq for Foo {
4171+
fn eq(&self, other: &Self) -> bool { true }
4172+
//^^
4173+
}
4174+
4175+
fn main() {
4176+
let a = Foo;
4177+
let b = Foo;
4178+
let _ = a !=$0 b;
4179+
}
4180+
"#,
4181+
);
4182+
}
41024183
}

0 commit comments

Comments
 (0)