Unnamed repository; edit this file 'description' to name the repository.
feat: Handle operators like their trait functions in the IDE
Lukas Wirth 2022-08-05
parent cb52271 · commit d6e78b0
-rw-r--r--crates/hir-expand/src/name.rs2
-rw-r--r--crates/hir-ty/src/infer/expr.rs63
-rw-r--r--crates/hir-ty/src/method_resolution.rs51
-rw-r--r--crates/hir/src/semantics.rs40
-rw-r--r--crates/hir/src/source_analyzer.rs116
-rw-r--r--crates/ide-db/src/defs.rs80
-rw-r--r--crates/ide/src/goto_definition.rs122
-rw-r--r--crates/ide/src/highlight_related.rs3
-rw-r--r--crates/ide/src/hover.rs12
-rw-r--r--crates/ide/src/hover/tests.rs34
-rw-r--r--crates/ide/src/moniker.rs2
-rw-r--r--crates/ide/src/static_index.rs2
12 files changed, 459 insertions, 68 deletions
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..36756866f9 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)
}
+ pub 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)
+ }
+
+ pub fn resolve_prefix_expr(&self, prefix_expr: &ast::PrefixExpr) -> Option<FunctionId> {
+ self.analyze(prefix_expr.syntax())?.resolve_prefix_expr(self.db, prefix_expr)
+ }
+
+ pub fn resolve_index_expr(&self, index_expr: &ast::IndexExpr) -> Option<FunctionId> {
+ self.analyze(index_expr.syntax())?.resolve_index_expr(self.db, index_expr)
+ }
+
+ pub fn resolve_bin_expr(&self, bin_expr: &ast::BinExpr) -> Option<FunctionId> {
+ self.analyze(bin_expr.syntax())?.resolve_bin_expr(self.db, bin_expr)
+ }
+
+ pub 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..45879ff9c6 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -25,15 +25,17 @@ 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, 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, TyExt, TyKind,
+ TyLoweringContext,
};
use itertools::Itertools;
use smallvec::SmallVec;
@@ -255,8 +257,103 @@ 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 fn resolve_await_to_poll(
+ &self,
+ db: &dyn HirDatabase,
+ await_expr: &ast::AwaitExpr,
+ ) -> Option<FunctionId> {
+ let expr_id = self.expr_id(db, &await_expr.expr()?.into())?;
+ let ty = self.infer.as_ref()?.type_of_expr.get(expr_id)?;
+
+ 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 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 expr_id = self.expr_id(db, &prefix_expr.expr()?.into())?;
+ let ty = self.infer.as_ref()?.type_of_expr.get(expr_id)?;
+
+ let trait_ =
+ db.lang_item(self.resolver.krate(), lang_item_name.to_smol_str())?.as_trait()?;
+ let op_fn = db.trait_data(trait_).method_by_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 fn resolve_index_expr(
+ &self,
+ db: &dyn HirDatabase,
+ index_expr: &ast::IndexExpr,
+ ) -> Option<FunctionId> {
+ let base_expr_id = self.expr_id(db, &index_expr.base()?.into())?;
+ let index_expr_id = self.expr_id(db, &index_expr.index()?.into())?;
+ let base_ty = self.infer.as_ref()?.type_of_expr.get(base_expr_id)?;
+ let index_ty = self.infer.as_ref()?.type_of_expr.get(index_expr_id)?;
+
+ let lang_item_name = name![index];
+
+ let trait_ =
+ db.lang_item(self.resolver.krate(), lang_item_name.to_smol_str())?.as_trait()?;
+ let op_fn = db.trait_data(trait_).method_by_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 fn resolve_bin_expr(
+ &self,
+ db: &dyn HirDatabase,
+ binop_expr: &ast::BinExpr,
+ ) -> Option<FunctionId> {
+ let op = binop_expr.op_kind()?;
+ let lhs_expr_id = self.expr_id(db, &binop_expr.lhs()?.into())?;
+ let rhs_expr_id = self.expr_id(db, &binop_expr.rhs()?.into())?;
+ let lhs = self.infer.as_ref()?.type_of_expr.get(lhs_expr_id)?;
+ let rhs = self.infer.as_ref()?.type_of_expr.get(rhs_expr_id)?;
+
+ let op_fn = lang_names_for_bin_op(op).and_then(|(name, lang_item)| {
+ db.trait_data(db.lang_item(self.resolver.krate(), lang_item.to_smol_str())?.as_trait()?)
+ .method_by_name(&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 fn resolve_try_expr(
+ &self,
+ db: &dyn HirDatabase,
+ try_expr: &ast::TryExpr,
+ ) -> Option<FunctionId> {
+ let expr_id = self.expr_id(db, &try_expr.expr()?.into())?;
+ let ty = self.infer.as_ref()?.type_of_expr.get(expr_id)?;
+
+ 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 +763,15 @@ 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 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,
})?;
@@ -1631,4 +1635,120 @@ 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/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
+ ```
+ "#]],
+ );
+}
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 {