Repository: rust-lang/rust-analyzer
Pull request #12948 (2022-08-05 12:16:50)

feat: Handle operators like their trait functions in the IDE
Allows hover and goto implementation to work on `?`, indexing brackets, binary operators, prefix operators and `await`. Regarding `await`, hover will continue to show the keyword docs while goto implementation will bring the user to the `poll` function of the `Future` implementation.
![Code_CJmZ3FGFVn](https://user-images.githubusercontent.com/3757771/183076683-c9899bd6-60d3-461b-965f-0c0f9745e2e8.gif)

Fixes https://github.com/rust-lang/rust-analyzer/issues/12810


Patch:
diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs
index 85b0a7735f..47d191822d 100644
--- a/crates/hir-expand/src/name.rs
+++ b/crates/hir-expand/src/name.rs
@@ -381,6 +381,7 @@ pub mod known {
         bitor,
         bitxor_assign,
         bitxor,
+        branch,
         deref_mut,
         deref,
         div_assign,
@@ -396,6 +397,7 @@ pub mod known {
         not,
         owned_box,
         partial_ord,
+        poll,
         r#fn,
         rem_assign,
         rem,
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index d164e64a8b..2a13106390 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -10,13 +10,13 @@ use chalk_ir::{
     cast::Cast, fold::Shift, DebruijnIndex, GenericArgData, Mutability, TyVariableKind,
 };
 use hir_def::{
-    expr::{ArithOp, Array, BinaryOp, CmpOp, Expr, ExprId, Literal, Ordering, Statement, UnaryOp},
+    expr::{ArithOp, Array, BinaryOp, CmpOp, Expr, ExprId, Literal, Statement, UnaryOp},
     generics::TypeOrConstParamData,
     path::{GenericArg, GenericArgs},
     resolver::resolver_for_expr,
-    ConstParamId, FieldId, FunctionId, ItemContainerId, Lookup,
+    ConstParamId, FieldId, ItemContainerId, Lookup,
 };
-use hir_expand::name::{name, Name};
+use hir_expand::name::Name;
 use stdx::always;
 use syntax::ast::RangeOp;
 
@@ -28,7 +28,7 @@ use crate::{
         const_or_path_to_chalk, generic_arg_to_chalk, lower_to_chalk_mutability, ParamLoweringMode,
     },
     mapping::{from_chalk, ToChalk},
-    method_resolution::{self, VisibleFromModule},
+    method_resolution::{self, lang_names_for_bin_op, VisibleFromModule},
     primitive::{self, UintTy},
     static_lifetime, to_chalk_trait_id,
     utils::{generics, Generics},
@@ -947,7 +947,9 @@ impl<'a> InferenceContext<'a> {
         let lhs_ty = self.infer_expr(lhs, &lhs_expectation);
         let rhs_ty = self.table.new_type_var();
 
-        let func = self.resolve_binop_method(op);
+        let func = lang_names_for_bin_op(op).and_then(|(name, lang_item)| {
+            self.db.trait_data(self.resolve_lang_item(lang_item)?.as_trait()?).method_by_name(&name)
+        });
         let func = match func {
             Some(func) => func,
             None => {
@@ -1473,55 +1475,4 @@ impl<'a> InferenceContext<'a> {
             },
         })
     }
-
-    fn resolve_binop_method(&self, op: BinaryOp) -> Option<FunctionId> {
-        let (name, lang_item) = match op {
-            BinaryOp::LogicOp(_) => return None,
-            BinaryOp::ArithOp(aop) => match aop {
-                ArithOp::Add => (name!(add), name!(add)),
-                ArithOp::Mul => (name!(mul), name!(mul)),
-                ArithOp::Sub => (name!(sub), name!(sub)),
-                ArithOp::Div => (name!(div), name!(div)),
-                ArithOp::Rem => (name!(rem), name!(rem)),
-                ArithOp::Shl => (name!(shl), name!(shl)),
-                ArithOp::Shr => (name!(shr), name!(shr)),
-                ArithOp::BitXor => (name!(bitxor), name!(bitxor)),
-                ArithOp::BitOr => (name!(bitor), name!(bitor)),
-                ArithOp::BitAnd => (name!(bitand), name!(bitand)),
-            },
-            BinaryOp::Assignment { op: Some(aop) } => match aop {
-                ArithOp::Add => (name!(add_assign), name!(add_assign)),
-                ArithOp::Mul => (name!(mul_assign), name!(mul_assign)),
-                ArithOp::Sub => (name!(sub_assign), name!(sub_assign)),
-                ArithOp::Div => (name!(div_assign), name!(div_assign)),
-                ArithOp::Rem => (name!(rem_assign), name!(rem_assign)),
-                ArithOp::Shl => (name!(shl_assign), name!(shl_assign)),
-                ArithOp::Shr => (name!(shr_assign), name!(shr_assign)),
-                ArithOp::BitXor => (name!(bitxor_assign), name!(bitxor_assign)),
-                ArithOp::BitOr => (name!(bitor_assign), name!(bitor_assign)),
-                ArithOp::BitAnd => (name!(bitand_assign), name!(bitand_assign)),
-            },
-            BinaryOp::CmpOp(cop) => match cop {
-                CmpOp::Eq { negated: false } => (name!(eq), name!(eq)),
-                CmpOp::Eq { negated: true } => (name!(ne), name!(eq)),
-                CmpOp::Ord { ordering: Ordering::Less, strict: false } => {
-                    (name!(le), name!(partial_ord))
-                }
-                CmpOp::Ord { ordering: Ordering::Less, strict: true } => {
-                    (name!(lt), name!(partial_ord))
-                }
-                CmpOp::Ord { ordering: Ordering::Greater, strict: false } => {
-                    (name!(ge), name!(partial_ord))
-                }
-                CmpOp::Ord { ordering: Ordering::Greater, strict: true } => {
-                    (name!(gt), name!(partial_ord))
-                }
-            },
-            BinaryOp::Assignment { op: None } => return None,
-        };
-
-        let trait_ = self.resolve_lang_item(lang_item)?.as_trait()?;
-
-        self.db.trait_data(trait_).method_by_name(&name)
-    }
 }
diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs
index 15df7b3dd2..64622545f8 100644
--- a/crates/hir-ty/src/method_resolution.rs
+++ b/crates/hir-ty/src/method_resolution.rs
@@ -336,7 +336,7 @@ impl InherentImpls {
     }
 }
 
-pub fn inherent_impl_crates_query(
+pub(crate) fn inherent_impl_crates_query(
     db: &dyn HirDatabase,
     krate: CrateId,
     fp: TyFingerprint,
@@ -419,6 +419,55 @@ pub fn def_crates(
     }
 }
 
+pub fn lang_names_for_bin_op(op: syntax::ast::BinaryOp) -> Option<(Name, Name)> {
+    use hir_expand::name;
+    use syntax::ast::{ArithOp, BinaryOp, CmpOp, Ordering};
+    Some(match op {
+        BinaryOp::LogicOp(_) => return None,
+        BinaryOp::ArithOp(aop) => match aop {
+            ArithOp::Add => (name!(add), name!(add)),
+            ArithOp::Mul => (name!(mul), name!(mul)),
+            ArithOp::Sub => (name!(sub), name!(sub)),
+            ArithOp::Div => (name!(div), name!(div)),
+            ArithOp::Rem => (name!(rem), name!(rem)),
+            ArithOp::Shl => (name!(shl), name!(shl)),
+            ArithOp::Shr => (name!(shr), name!(shr)),
+            ArithOp::BitXor => (name!(bitxor), name!(bitxor)),
+            ArithOp::BitOr => (name!(bitor), name!(bitor)),
+            ArithOp::BitAnd => (name!(bitand), name!(bitand)),
+        },
+        BinaryOp::Assignment { op: Some(aop) } => match aop {
+            ArithOp::Add => (name!(add_assign), name!(add_assign)),
+            ArithOp::Mul => (name!(mul_assign), name!(mul_assign)),
+            ArithOp::Sub => (name!(sub_assign), name!(sub_assign)),
+            ArithOp::Div => (name!(div_assign), name!(div_assign)),
+            ArithOp::Rem => (name!(rem_assign), name!(rem_assign)),
+            ArithOp::Shl => (name!(shl_assign), name!(shl_assign)),
+            ArithOp::Shr => (name!(shr_assign), name!(shr_assign)),
+            ArithOp::BitXor => (name!(bitxor_assign), name!(bitxor_assign)),
+            ArithOp::BitOr => (name!(bitor_assign), name!(bitor_assign)),
+            ArithOp::BitAnd => (name!(bitand_assign), name!(bitand_assign)),
+        },
+        BinaryOp::CmpOp(cop) => match cop {
+            CmpOp::Eq { negated: false } => (name!(eq), name!(eq)),
+            CmpOp::Eq { negated: true } => (name!(ne), name!(eq)),
+            CmpOp::Ord { ordering: Ordering::Less, strict: false } => {
+                (name!(le), name!(partial_ord))
+            }
+            CmpOp::Ord { ordering: Ordering::Less, strict: true } => {
+                (name!(lt), name!(partial_ord))
+            }
+            CmpOp::Ord { ordering: Ordering::Greater, strict: false } => {
+                (name!(ge), name!(partial_ord))
+            }
+            CmpOp::Ord { ordering: Ordering::Greater, strict: true } => {
+                (name!(gt), name!(partial_ord))
+            }
+        },
+        BinaryOp::Assignment { op: None } => return None,
+    })
+}
+
 /// Look up the method with the given name.
 pub(crate) fn lookup_method(
     ty: &Canonical<Ty>,
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index c84318b2fb..416b6f5806 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -357,6 +357,26 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
         self.imp.resolve_method_call(call).map(Function::from)
     }
 
+    pub fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option<Function> {
+        self.imp.resolve_await_to_poll(await_expr).map(Function::from)
+    }
+
+    pub fn resolve_prefix_expr(&self, prefix_expr: &ast::PrefixExpr) -> Option<Function> {
+        self.imp.resolve_prefix_expr(prefix_expr).map(Function::from)
+    }
+
+    pub fn resolve_index_expr(&self, index_expr: &ast::IndexExpr) -> Option<Function> {
+        self.imp.resolve_index_expr(index_expr).map(Function::from)
+    }
+
+    pub fn resolve_bin_expr(&self, bin_expr: &ast::BinExpr) -> Option<Function> {
+        self.imp.resolve_bin_expr(bin_expr).map(Function::from)
+    }
+
+    pub fn resolve_try_expr(&self, try_expr: &ast::TryExpr) -> Option<Function> {
+        self.imp.resolve_try_expr(try_expr).map(Function::from)
+    }
+
     pub fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> {
         self.imp.resolve_method_call_as_callable(call)
     }
@@ -1066,6 +1086,26 @@ impl<'db> SemanticsImpl<'db> {
         self.analyze(call.syntax())?.resolve_method_call(self.db, call)
     }
 
+    fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option<FunctionId> {
+        self.analyze(await_expr.syntax())?.resolve_await_to_poll(self.db, await_expr)
+    }
+
+    fn resolve_prefix_expr(&self, prefix_expr: &ast::PrefixExpr) -> Option<FunctionId> {
+        self.analyze(prefix_expr.syntax())?.resolve_prefix_expr(self.db, prefix_expr)
+    }
+
+    fn resolve_index_expr(&self, index_expr: &ast::IndexExpr) -> Option<FunctionId> {
+        self.analyze(index_expr.syntax())?.resolve_index_expr(self.db, index_expr)
+    }
+
+    fn resolve_bin_expr(&self, bin_expr: &ast::BinExpr) -> Option<FunctionId> {
+        self.analyze(bin_expr.syntax())?.resolve_bin_expr(self.db, bin_expr)
+    }
+
+    fn resolve_try_expr(&self, try_expr: &ast::TryExpr) -> Option<FunctionId> {
+        self.analyze(try_expr.syntax())?.resolve_try_expr(self.db, try_expr)
+    }
+
     fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> {
         self.analyze(call.syntax())?.resolve_method_call_as_callable(self.db, call)
     }
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 1eb51b20c3..f5e2e44307 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -25,15 +25,20 @@ use hir_def::{
     Lookup, ModuleDefId, VariantId,
 };
 use hir_expand::{
-    builtin_fn_macro::BuiltinFnLikeExpander, hygiene::Hygiene, name::AsName, HirFileId, InFile,
+    builtin_fn_macro::BuiltinFnLikeExpander,
+    hygiene::Hygiene,
+    name,
+    name::{AsName, Name},
+    HirFileId, InFile,
 };
 use hir_ty::{
     diagnostics::{
         record_literal_missing_fields, record_pattern_missing_fields, unsafe_expressions,
         UnsafeExpr,
     },
-    method_resolution, Adjust, Adjustment, AutoBorrow, InferenceResult, Interner, Substitution,
-    TyExt, TyKind, TyLoweringContext,
+    method_resolution::{self, lang_names_for_bin_op},
+    Adjust, Adjustment, AutoBorrow, InferenceResult, Interner, Substitution, Ty, TyExt, TyKind,
+    TyLoweringContext,
 };
 use itertools::Itertools;
 use smallvec::SmallVec;
@@ -255,8 +260,90 @@ impl SourceAnalyzer {
     ) -> Option<FunctionId> {
         let expr_id = self.expr_id(db, &call.clone().into())?;
         let (f_in_trait, substs) = self.infer.as_ref()?.method_resolution(expr_id)?;
-        let f_in_impl = self.resolve_impl_method(db, f_in_trait, &substs);
-        f_in_impl.or(Some(f_in_trait))
+
+        Some(self.resolve_impl_method_or_trait_def(db, f_in_trait, &substs))
+    }
+
+    pub(crate) fn resolve_await_to_poll(
+        &self,
+        db: &dyn HirDatabase,
+        await_expr: &ast::AwaitExpr,
+    ) -> Option<FunctionId> {
+        let ty = self.ty_of_expr(db, &await_expr.expr()?.into())?;
+
+        let op_fn = db
+            .lang_item(self.resolver.krate(), hir_expand::name![poll].to_smol_str())?
+            .as_function()?;
+        let substs = hir_ty::TyBuilder::subst_for_def(db, op_fn).push(ty.clone()).build();
+
+        Some(self.resolve_impl_method_or_trait_def(db, op_fn, &substs))
+    }
+
+    pub(crate) fn resolve_prefix_expr(
+        &self,
+        db: &dyn HirDatabase,
+        prefix_expr: &ast::PrefixExpr,
+    ) -> Option<FunctionId> {
+        let lang_item_name = match prefix_expr.op_kind()? {
+            ast::UnaryOp::Deref => name![deref],
+            ast::UnaryOp::Not => name![not],
+            ast::UnaryOp::Neg => name![neg],
+        };
+        let ty = self.ty_of_expr(db, &prefix_expr.expr()?.into())?;
+
+        let op_fn = self.lang_trait_fn(db, &lang_item_name, &lang_item_name)?;
+        let substs = hir_ty::TyBuilder::subst_for_def(db, op_fn).push(ty.clone()).build();
+
+        Some(self.resolve_impl_method_or_trait_def(db, op_fn, &substs))
+    }
+
+    pub(crate) fn resolve_index_expr(
+        &self,
+        db: &dyn HirDatabase,
+        index_expr: &ast::IndexExpr,
+    ) -> Option<FunctionId> {
+        let base_ty = self.ty_of_expr(db, &index_expr.base()?.into())?;
+        let index_ty = self.ty_of_expr(db, &index_expr.index()?.into())?;
+
+        let lang_item_name = name![index];
+
+        let op_fn = self.lang_trait_fn(db, &lang_item_name, &lang_item_name)?;
+        let substs = hir_ty::TyBuilder::subst_for_def(db, op_fn)
+            .push(base_ty.clone())
+            .push(index_ty.clone())
+            .build();
+        Some(self.resolve_impl_method_or_trait_def(db, op_fn, &substs))
+    }
+
+    pub(crate) fn resolve_bin_expr(
+        &self,
+        db: &dyn HirDatabase,
+        binop_expr: &ast::BinExpr,
+    ) -> Option<FunctionId> {
+        let op = binop_expr.op_kind()?;
+        let lhs = self.ty_of_expr(db, &binop_expr.lhs()?.into())?;
+        let rhs = self.ty_of_expr(db, &binop_expr.rhs()?.into())?;
+
+        let op_fn = lang_names_for_bin_op(op)
+            .and_then(|(name, lang_item)| self.lang_trait_fn(db, &lang_item, &name))?;
+        let substs =
+            hir_ty::TyBuilder::subst_for_def(db, op_fn).push(lhs.clone()).push(rhs.clone()).build();
+
+        Some(self.resolve_impl_method_or_trait_def(db, op_fn, &substs))
+    }
+
+    pub(crate) fn resolve_try_expr(
+        &self,
+        db: &dyn HirDatabase,
+        try_expr: &ast::TryExpr,
+    ) -> Option<FunctionId> {
+        let ty = self.ty_of_expr(db, &try_expr.expr()?.into())?;
+
+        let op_fn =
+            db.lang_item(self.resolver.krate(), name![branch].to_smol_str())?.as_function()?;
+        let substs = hir_ty::TyBuilder::subst_for_def(db, op_fn).push(ty.clone()).build();
+
+        Some(self.resolve_impl_method_or_trait_def(db, op_fn, &substs))
     }
 
     pub(crate) fn resolve_field(
@@ -666,6 +753,29 @@ impl SourceAnalyzer {
         let fun_data = db.function_data(func);
         method_resolution::lookup_impl_method(self_ty, db, trait_env, impled_trait, &fun_data.name)
     }
+
+    fn resolve_impl_method_or_trait_def(
+        &self,
+        db: &dyn HirDatabase,
+        func: FunctionId,
+        substs: &Substitution,
+    ) -> FunctionId {
+        self.resolve_impl_method(db, func, substs).unwrap_or(func)
+    }
+
+    fn lang_trait_fn(
+        &self,
+        db: &dyn HirDatabase,
+        lang_trait: &Name,
+        method_name: &Name,
+    ) -> Option<FunctionId> {
+        db.trait_data(db.lang_item(self.resolver.krate(), lang_trait.to_smol_str())?.as_trait()?)
+            .method_by_name(method_name)
+    }
+
+    fn ty_of_expr(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<&Ty> {
+        self.infer.as_ref()?.type_of_expr.get(self.expr_id(db, &expr)?)
+    }
 }
 
 fn scope_for(
diff --git a/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs
index aeaca00ec6..6c13c03972 100644
--- a/crates/ide-db/src/defs.rs
+++ b/crates/ide-db/src/defs.rs
@@ -127,10 +127,12 @@ impl Definition {
     }
 }
 
+// FIXME: IdentClass as a name no longer fits
 #[derive(Debug)]
 pub enum IdentClass {
     NameClass(NameClass),
     NameRefClass(NameRefClass),
+    Operator(OperatorClass),
 }
 
 impl IdentClass {
@@ -147,6 +149,11 @@ impl IdentClass {
                         .map(IdentClass::NameClass)
                         .or_else(|| NameRefClass::classify_lifetime(sema, &lifetime).map(IdentClass::NameRefClass))
                 },
+                ast::AwaitExpr(await_expr) => OperatorClass::classify_await(sema, &await_expr).map(IdentClass::Operator),
+                ast::BinExpr(bin_expr) => OperatorClass::classify_bin(sema, &bin_expr).map(IdentClass::Operator),
+                ast::IndexExpr(index_expr) => OperatorClass::classify_index(sema, &index_expr).map(IdentClass::Operator),
+                ast::PrefixExpr(prefix_expr) => OperatorClass::classify_prefix(sema,&prefix_expr).map(IdentClass::Operator),
+                ast::TryExpr(try_expr) => OperatorClass::classify_try(sema,&try_expr).map(IdentClass::Operator),
                 _ => None,
             }
         }
@@ -184,6 +191,33 @@ impl IdentClass {
                 res.push(Definition::Local(local_ref));
                 res.push(Definition::Field(field_ref));
             }
+            IdentClass::Operator(
+                OperatorClass::Await(func)
+                | OperatorClass::Prefix(func)
+                | OperatorClass::Bin(func)
+                | OperatorClass::Index(func)
+                | OperatorClass::Try(func),
+            ) => res.push(Definition::Function(func)),
+        }
+        res
+    }
+
+    pub fn definitions_no_ops(self) -> ArrayVec<Definition, 2> {
+        let mut res = ArrayVec::new();
+        match self {
+            IdentClass::NameClass(NameClass::Definition(it) | NameClass::ConstReference(it)) => {
+                res.push(it)
+            }
+            IdentClass::NameClass(NameClass::PatFieldShorthand { local_def, field_ref }) => {
+                res.push(Definition::Local(local_def));
+                res.push(Definition::Field(field_ref));
+            }
+            IdentClass::NameRefClass(NameRefClass::Definition(it)) => res.push(it),
+            IdentClass::NameRefClass(NameRefClass::FieldShorthand { local_ref, field_ref }) => {
+                res.push(Definition::Local(local_ref));
+                res.push(Definition::Field(field_ref));
+            }
+            IdentClass::Operator(_) => (),
         }
         res
     }
@@ -332,6 +366,52 @@ impl NameClass {
     }
 }
 
+#[derive(Debug)]
+pub enum OperatorClass {
+    Await(Function),
+    Prefix(Function),
+    Index(Function),
+    Try(Function),
+    Bin(Function),
+}
+
+impl OperatorClass {
+    pub fn classify_await(
+        sema: &Semantics<'_, RootDatabase>,
+        await_expr: &ast::AwaitExpr,
+    ) -> Option<OperatorClass> {
+        sema.resolve_await_to_poll(await_expr).map(OperatorClass::Await)
+    }
+
+    pub fn classify_prefix(
+        sema: &Semantics<'_, RootDatabase>,
+        prefix_expr: &ast::PrefixExpr,
+    ) -> Option<OperatorClass> {
+        sema.resolve_prefix_expr(prefix_expr).map(OperatorClass::Prefix)
+    }
+
+    pub fn classify_try(
+        sema: &Semantics<'_, RootDatabase>,
+        try_expr: &ast::TryExpr,
+    ) -> Option<OperatorClass> {
+        sema.resolve_try_expr(try_expr).map(OperatorClass::Try)
+    }
+
+    pub fn classify_index(
+        sema: &Semantics<'_, RootDatabase>,
+        index_expr: &ast::IndexExpr,
+    ) -> Option<OperatorClass> {
+        sema.resolve_index_expr(index_expr).map(OperatorClass::Index)
+    }
+
+    pub fn classify_bin(
+        sema: &Semantics<'_, RootDatabase>,
+        bin_expr: &ast::BinExpr,
+    ) -> Option<OperatorClass> {
+        sema.resolve_bin_expr(bin_expr).map(OperatorClass::Bin)
+    }
+}
+
 /// This is similar to [`NameClass`], but works for [`ast::NameRef`] rather than
 /// for [`ast::Name`]. Similarly, what looks like a reference in syntax is a
 /// reference most of the time, but there are a couple of annoying exceptions.
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index d9c97751c9..b2123b9a87 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -39,7 +39,11 @@ pub(crate) fn goto_definition(
             | T![super]
             | T![crate]
             | T![Self]
-            | COMMENT => 2,
+            | COMMENT => 4,
+            // index and prefix ops
+            T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] => 3,
+            kind if kind.is_keyword() => 2,
+            T!['('] | T![')'] => 2,
             kind if kind.is_trivia() => 0,
             _ => 1,
         })?;
@@ -1628,6 +1632,122 @@ macro_rules! foo {
 }
 
 foo!(bar$0);
+"#,
+        );
+    }
+
+    #[test]
+    fn goto_await_poll() {
+        check(
+            r#"
+//- minicore: future
+
+struct MyFut;
+
+impl core::future::Future for MyFut {
+    type Output = ();
+
+    fn poll(
+     //^^^^
+        self: std::pin::Pin<&mut Self>,
+        cx: &mut std::task::Context<'_>
+    ) -> std::task::Poll<Self::Output>
+    {
+        ()
+    }
+}
+
+fn f() {
+    MyFut.await$0;
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn goto_try_op() {
+        check(
+            r#"
+//- minicore: try
+
+struct Struct;
+
+impl core::ops::Try for Struct {
+    fn branch(
+     //^^^^^^
+        self
+    ) {}
+}
+
+fn f() {
+    Struct?$0;
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn goto_index_op() {
+        check(
+            r#"
+//- minicore: index
+
+struct Struct;
+
+impl core::ops::Index<usize> for Struct {
+    fn index(
+     //^^^^^
+        self
+    ) {}
+}
+
+fn f() {
+    Struct[0]$0;
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn goto_prefix_op() {
+        check(
+            r#"
+//- minicore: deref
+
+struct Struct;
+
+impl core::ops::Deref for Struct {
+    fn deref(
+     //^^^^^
+        self
+    ) {}
+}
+
+fn f() {
+    $0*Struct;
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn goto_bin_op() {
+        check(
+            r#"
+//- minicore: add
+
+struct Struct;
+
+impl core::ops::Add for Struct {
+    fn add(
+     //^^^
+        self
+    ) {}
+}
+
+fn f() {
+    Struct +$0 Struct;
+}
 "#,
         );
     }
diff --git a/crates/ide/src/highlight_related.rs b/crates/ide/src/highlight_related.rs
index f2d7029eab..f190da326e 100644
--- a/crates/ide/src/highlight_related.rs
+++ b/crates/ide/src/highlight_related.rs
@@ -333,7 +333,8 @@ fn cover_range(r0: Option<TextRange>, r1: Option<TextRange>) -> Option<TextRange
 fn find_defs(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> FxHashSet<Definition> {
     sema.descend_into_macros(token)
         .into_iter()
-        .filter_map(|token| IdentClass::classify_token(sema, &token).map(IdentClass::definitions))
+        .filter_map(|token| IdentClass::classify_token(sema, &token))
+        .map(IdentClass::definitions_no_ops)
         .flatten()
         .collect()
 }
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 784f85dbbe..3ada181f1e 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -9,7 +9,7 @@ use either::Either;
 use hir::{HasSource, Semantics};
 use ide_db::{
     base_db::FileRange,
-    defs::{Definition, IdentClass},
+    defs::{Definition, IdentClass, OperatorClass},
     famous_defs::FamousDefs,
     helpers::pick_best_token,
     FxIndexSet, RootDatabase,
@@ -101,7 +101,10 @@ pub(crate) fn hover(
     let offset = range.start();
 
     let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
-        IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] | T![Self] => 3,
+        IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] | T![Self] => 4,
+        // index and prefix ops
+        T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] => 3,
+        kind if kind.is_keyword() => 2,
         T!['('] | T![')'] => 2,
         kind if kind.is_trivia() => 0,
         _ => 1,
@@ -136,6 +139,11 @@ pub(crate) fn hover(
         .filter_map(|token| {
             let node = token.parent()?;
             let class = IdentClass::classify_token(sema, token)?;
+            if let IdentClass::Operator(OperatorClass::Await(_)) = class {
+                // It's better for us to fall back to the keyword hover here,
+                // rendering poll is very confusing
+                return None;
+            }
             Some(class.definitions().into_iter().zip(iter::once(node).cycle()))
         })
         .flatten()
diff --git a/crates/ide/src/moniker.rs b/crates/ide/src/moniker.rs
index 6bab9fa1eb..4f758967b4 100644
--- a/crates/ide/src/moniker.rs
+++ b/crates/ide/src/moniker.rs
@@ -90,7 +90,7 @@ pub(crate) fn moniker(
         .descend_into_macros(original_token.clone())
         .into_iter()
         .filter_map(|token| {
-            IdentClass::classify_token(sema, &token).map(IdentClass::definitions).map(|it| {
+            IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops).map(|it| {
                 it.into_iter().flat_map(|def| def_to_moniker(sema.db, def, current_crate))
             })
         })
diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs
index d74b640415..cc79ee55b7 100644
--- a/crates/ide/src/static_index.rs
+++ b/crates/ide/src/static_index.rs
@@ -204,7 +204,7 @@ impl StaticIndex<'_> {
 
 fn get_definition(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> Option<Definition> {
     for token in sema.descend_into_macros(token) {
-        let def = IdentClass::classify_token(sema, &token).map(IdentClass::definitions);
+        let def = IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops);
         if let Some(&[x]) = def.as_deref() {
             return Some(x);
         } else {


Test patch:
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 867d1f54d4..c6274264b8 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -5051,3 +5051,37 @@ fn f() {
             ```"#]],
     );
 }
+
+#[test]
+fn hover_deref() {
+    check(
+        r#"
+//- minicore: deref
+
+struct Struct(usize);
+
+impl core::ops::Deref for Struct {
+    type Target = usize;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+fn f() {
+    $0*Struct(0);
+}
+"#,
+        expect![[r#"
+            ***
+
+            ```rust
+            test::Struct
+            ```
+
+            ```rust
+            fn deref(&self) -> &Self::Target
+            ```
+        "#]],
+    );
+}