Unnamed repository; edit this file 'description' to name the repository.
fix: resolve comparison operators to explicit trait impls (rust-lang/rust-analyzer#13332)
Amit5601 4 weeks ago
parent c41c134 · commit 32800d4
-rw-r--r--crates/ide/src/goto_definition.rs81
1 files changed, 81 insertions, 0 deletions
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index 4cdf0eac75..4890badcbf 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -95,6 +95,11 @@ pub(crate) fn goto_definition(
continue;
}
+ if let Some(n) = find_definition_for_comparison_operators(sema, &token.value) {
+ navs.extend(n);
+ continue;
+ }
+
let parent = token.value.parent()?;
if let Some(question_mark_conversion) = goto_question_mark_conversions(sema, &parent) {
@@ -264,6 +269,62 @@ fn find_definition_for_known_blanket_dual_impls(
Some(def_to_nav(sema, def))
}
+// 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).
+fn find_definition_for_comparison_operators(
+ sema: &Semantics<'_, RootDatabase>,
+ original_token: &SyntaxToken,
+) -> Option<Vec<NavigationTarget>> {
+ let bin_expr = ast::BinExpr::cast(original_token.parent()?)?;
+
+ let f = sema.resolve_bin_expr(&bin_expr)?;
+ let assoc = f.as_assoc_item(sema.db)?;
+
+ let lhs_type = sema.type_of_expr(&bin_expr.lhs()?)?.original;
+ let rhs_type = sema.type_of_expr(&bin_expr.rhs()?)?.original;
+
+ let t = match assoc.container(sema.db) {
+ hir::AssocItemContainer::Trait(t) => t,
+ hir::AssocItemContainer::Impl(_) => return None, // Already implemented by the type
+ };
+
+ let fn_name = f.name(sema.db);
+ let fn_name_str = fn_name.as_str();
+
+ let trait_name = t.name(sema.db);
+ let trait_name_str = trait_name.as_str();
+
+ let (target_fn_name, expected_trait) = match fn_name_str {
+ "ne" => ("eq", "PartialEq"),
+ "lt" | "le" | "gt" | "ge" => ("partial_cmp", "PartialOrd"),
+ _ => return None,
+ };
+
+ if trait_name_str != expected_trait {
+ return None;
+ }
+
+ let primary_f = t.items(sema.db).into_iter().find_map(|item| {
+ if let hir::AssocItem::Function(func) = item
+ && func.name(sema.db).as_str() == target_fn_name
+ {
+ return Some(func);
+ }
+ None
+ })?;
+
+ // Chalk requires ALL trait substitutions, including `Self`!
+ // We must pass [Self, Rhs]
+ let resolved_f = sema.resolve_trait_impl_method(
+ lhs_type.clone(),
+ t,
+ primary_f,
+ [lhs_type.clone(), rhs_type.clone()],
+ )?;
+
+ let def = Definition::from(resolved_f);
+
+ Some(def_to_nav(sema, def))
+}
fn try_lookup_include_path(
sema: &Semantics<'_, RootDatabase>,
token: InFile<ast::String>,
@@ -4099,4 +4160,24 @@ fn foo() -> Result<(), Bar> {
"#,
);
}
+
+ #[test]
+ fn goto_definition_for_comparison_operators() {
+ check(
+ r#"
+//- minicore: eq, ord
+struct Foo;
+impl PartialEq for Foo {
+ fn eq(&self, other: &Self) -> bool { true }
+ //^^
+}
+
+fn main() {
+ let a = Foo;
+ let b = Foo;
+ let _ = a !=$0 b;
+}
+"#,
+ );
+ }
}