From d6e78b04d00c9144b70b2477d21076b516d5fca7 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 5 Aug 2022 14:16:36 +0200 Subject: [PATCH 1/3] feat: Handle operators like their trait functions in the IDE --- crates/hir-expand/src/name.rs | 2 + crates/hir-ty/src/infer/expr.rs | 63 ++----------- crates/hir-ty/src/method_resolution.rs | 51 ++++++++++- crates/hir/src/semantics.rs | 40 ++++++++ crates/hir/src/source_analyzer.rs | 116 ++++++++++++++++++++++- crates/ide-db/src/defs.rs | 80 ++++++++++++++++ crates/ide/src/goto_definition.rs | 122 ++++++++++++++++++++++++- crates/ide/src/highlight_related.rs | 3 +- crates/ide/src/hover.rs | 12 ++- crates/ide/src/hover/tests.rs | 34 +++++++ crates/ide/src/moniker.rs | 2 +- crates/ide/src/static_index.rs | 2 +- 12 files changed, 459 insertions(+), 68 deletions(-) diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs index 85b0a7735fe94..47d191822d841 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 d164e64a8be07..2a13106390d9f 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 { - 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 15df7b3dd2b9b..64622545f8408 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, diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index c84318b2fb877..36756866f9977 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 { + self.imp.resolve_await_to_poll(await_expr).map(Function::from) + } + + pub fn resolve_prefix_expr(&self, prefix_expr: &ast::PrefixExpr) -> Option { + self.imp.resolve_prefix_expr(prefix_expr).map(Function::from) + } + + pub fn resolve_index_expr(&self, index_expr: &ast::IndexExpr) -> Option { + self.imp.resolve_index_expr(index_expr).map(Function::from) + } + + pub fn resolve_bin_expr(&self, bin_expr: &ast::BinExpr) -> Option { + self.imp.resolve_bin_expr(bin_expr).map(Function::from) + } + + pub fn resolve_try_expr(&self, try_expr: &ast::TryExpr) -> Option { + self.imp.resolve_try_expr(try_expr).map(Function::from) + } + pub fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option { 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 { + self.analyze(await_expr.syntax())?.resolve_await_to_poll(self.db, await_expr) + } + + pub fn resolve_prefix_expr(&self, prefix_expr: &ast::PrefixExpr) -> Option { + self.analyze(prefix_expr.syntax())?.resolve_prefix_expr(self.db, prefix_expr) + } + + pub fn resolve_index_expr(&self, index_expr: &ast::IndexExpr) -> Option { + self.analyze(index_expr.syntax())?.resolve_index_expr(self.db, index_expr) + } + + pub fn resolve_bin_expr(&self, bin_expr: &ast::BinExpr) -> Option { + self.analyze(bin_expr.syntax())?.resolve_bin_expr(self.db, bin_expr) + } + + pub fn resolve_try_expr(&self, try_expr: &ast::TryExpr) -> Option { + self.analyze(try_expr.syntax())?.resolve_try_expr(self.db, try_expr) + } + fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option { 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 1eb51b20c356b..45879ff9c6ad2 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 { 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 { + 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 { + 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 { + 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 { + 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 { + 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 aeaca00ec65cc..6c13c039723b2 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 { + 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 { + sema.resolve_await_to_poll(await_expr).map(OperatorClass::Await) + } + + pub fn classify_prefix( + sema: &Semantics<'_, RootDatabase>, + prefix_expr: &ast::PrefixExpr, + ) -> Option { + sema.resolve_prefix_expr(prefix_expr).map(OperatorClass::Prefix) + } + + pub fn classify_try( + sema: &Semantics<'_, RootDatabase>, + try_expr: &ast::TryExpr, + ) -> Option { + sema.resolve_try_expr(try_expr).map(OperatorClass::Try) + } + + pub fn classify_index( + sema: &Semantics<'_, RootDatabase>, + index_expr: &ast::IndexExpr, + ) -> Option { + sema.resolve_index_expr(index_expr).map(OperatorClass::Index) + } + + pub fn classify_bin( + sema: &Semantics<'_, RootDatabase>, + bin_expr: &ast::BinExpr, + ) -> Option { + 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 d9c97751c95c3..b2123b9a87938 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 + { + () + } +} + +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 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 f2d7029eab195..f190da326e455 100644 --- a/crates/ide/src/highlight_related.rs +++ b/crates/ide/src/highlight_related.rs @@ -333,7 +333,8 @@ fn cover_range(r0: Option, r1: Option) -> Option, token: SyntaxToken) -> FxHashSet { 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 784f85dbbefb9..3ada181f1ed2b 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 867d1f54d4feb..c6274264b8f1a 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 6bab9fa1ebbb8..4f758967b4619 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 d74b640415c76..cc79ee55b7dac 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 { 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 { From 352d3c6e50f4c64cded06b51ef81d4ac83b4e34c Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 5 Aug 2022 14:28:36 +0200 Subject: [PATCH 2/3] Fix visibilities --- crates/hir/src/semantics.rs | 10 +++++----- crates/hir/src/source_analyzer.rs | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 36756866f9977..416b6f58061da 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -1086,23 +1086,23 @@ 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 { + fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option { self.analyze(await_expr.syntax())?.resolve_await_to_poll(self.db, await_expr) } - pub fn resolve_prefix_expr(&self, prefix_expr: &ast::PrefixExpr) -> Option { + fn resolve_prefix_expr(&self, prefix_expr: &ast::PrefixExpr) -> Option { self.analyze(prefix_expr.syntax())?.resolve_prefix_expr(self.db, prefix_expr) } - pub fn resolve_index_expr(&self, index_expr: &ast::IndexExpr) -> Option { + fn resolve_index_expr(&self, index_expr: &ast::IndexExpr) -> Option { self.analyze(index_expr.syntax())?.resolve_index_expr(self.db, index_expr) } - pub fn resolve_bin_expr(&self, bin_expr: &ast::BinExpr) -> Option { + fn resolve_bin_expr(&self, bin_expr: &ast::BinExpr) -> Option { self.analyze(bin_expr.syntax())?.resolve_bin_expr(self.db, bin_expr) } - pub fn resolve_try_expr(&self, try_expr: &ast::TryExpr) -> Option { + fn resolve_try_expr(&self, try_expr: &ast::TryExpr) -> Option { self.analyze(try_expr.syntax())?.resolve_try_expr(self.db, try_expr) } diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 45879ff9c6ad2..5e0bf7c5fb9fe 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -261,7 +261,7 @@ impl SourceAnalyzer { Some(self.resolve_impl_method_or_trait_def(db, f_in_trait, &substs)) } - pub fn resolve_await_to_poll( + pub(crate) fn resolve_await_to_poll( &self, db: &dyn HirDatabase, await_expr: &ast::AwaitExpr, @@ -277,7 +277,7 @@ impl SourceAnalyzer { Some(self.resolve_impl_method_or_trait_def(db, op_fn, &substs)) } - pub fn resolve_prefix_expr( + pub(crate) fn resolve_prefix_expr( &self, db: &dyn HirDatabase, prefix_expr: &ast::PrefixExpr, @@ -298,7 +298,7 @@ impl SourceAnalyzer { Some(self.resolve_impl_method_or_trait_def(db, op_fn, &substs)) } - pub fn resolve_index_expr( + pub(crate) fn resolve_index_expr( &self, db: &dyn HirDatabase, index_expr: &ast::IndexExpr, @@ -320,7 +320,7 @@ impl SourceAnalyzer { Some(self.resolve_impl_method_or_trait_def(db, op_fn, &substs)) } - pub fn resolve_bin_expr( + pub(crate) fn resolve_bin_expr( &self, db: &dyn HirDatabase, binop_expr: &ast::BinExpr, @@ -341,7 +341,7 @@ impl SourceAnalyzer { Some(self.resolve_impl_method_or_trait_def(db, op_fn, &substs)) } - pub fn resolve_try_expr( + pub(crate) fn resolve_try_expr( &self, db: &dyn HirDatabase, try_expr: &ast::TryExpr, From 8aa50e08af4bd2ecc6b2132d2db48edabe51f352 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 5 Aug 2022 14:54:14 +0200 Subject: [PATCH 3/3] Simplify --- crates/hir/src/source_analyzer.rs | 60 ++++++++++++++++--------------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 5e0bf7c5fb9fe..f5e2e44307090 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -25,8 +25,11 @@ use hir_def::{ Lookup, ModuleDefId, VariantId, }; use hir_expand::{ - builtin_fn_macro::BuiltinFnLikeExpander, hygiene::Hygiene, name, name::AsName, HirFileId, - InFile, + builtin_fn_macro::BuiltinFnLikeExpander, + hygiene::Hygiene, + name, + name::{AsName, Name}, + HirFileId, InFile, }; use hir_ty::{ diagnostics::{ @@ -34,7 +37,7 @@ use hir_ty::{ UnsafeExpr, }, method_resolution::{self, lang_names_for_bin_op}, - Adjust, Adjustment, AutoBorrow, InferenceResult, Interner, Substitution, TyExt, TyKind, + Adjust, Adjustment, AutoBorrow, InferenceResult, Interner, Substitution, Ty, TyExt, TyKind, TyLoweringContext, }; use itertools::Itertools; @@ -266,8 +269,7 @@ impl SourceAnalyzer { db: &dyn HirDatabase, await_expr: &ast::AwaitExpr, ) -> Option { - let expr_id = self.expr_id(db, &await_expr.expr()?.into())?; - let ty = self.infer.as_ref()?.type_of_expr.get(expr_id)?; + 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())? @@ -287,12 +289,9 @@ impl SourceAnalyzer { 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 ty = self.ty_of_expr(db, &prefix_expr.expr()?.into())?; - 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 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)) @@ -303,16 +302,12 @@ impl SourceAnalyzer { db: &dyn HirDatabase, index_expr: &ast::IndexExpr, ) -> Option { - 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 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 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 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()) @@ -326,15 +321,11 @@ impl SourceAnalyzer { binop_expr: &ast::BinExpr, ) -> Option { 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 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(); @@ -346,8 +337,7 @@ impl SourceAnalyzer { db: &dyn HirDatabase, try_expr: &ast::TryExpr, ) -> Option { - let expr_id = self.expr_id(db, &try_expr.expr()?.into())?; - let ty = self.infer.as_ref()?.type_of_expr.get(expr_id)?; + 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()?; @@ -772,6 +762,20 @@ impl SourceAnalyzer { ) -> 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 { + 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(