Auto merge of #12948 - Veykril:classify-ops, r=Veykril
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
This commit is contained in:
commit
2364788c3b
@ -381,6 +381,7 @@ macro_rules! known_names {
|
|||||||
bitor,
|
bitor,
|
||||||
bitxor_assign,
|
bitxor_assign,
|
||||||
bitxor,
|
bitxor,
|
||||||
|
branch,
|
||||||
deref_mut,
|
deref_mut,
|
||||||
deref,
|
deref,
|
||||||
div_assign,
|
div_assign,
|
||||||
@ -396,6 +397,7 @@ macro_rules! known_names {
|
|||||||
not,
|
not,
|
||||||
owned_box,
|
owned_box,
|
||||||
partial_ord,
|
partial_ord,
|
||||||
|
poll,
|
||||||
r#fn,
|
r#fn,
|
||||||
rem_assign,
|
rem_assign,
|
||||||
rem,
|
rem,
|
||||||
|
@ -10,13 +10,13 @@
|
|||||||
cast::Cast, fold::Shift, DebruijnIndex, GenericArgData, Mutability, TyVariableKind,
|
cast::Cast, fold::Shift, DebruijnIndex, GenericArgData, Mutability, TyVariableKind,
|
||||||
};
|
};
|
||||||
use hir_def::{
|
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,
|
generics::TypeOrConstParamData,
|
||||||
path::{GenericArg, GenericArgs},
|
path::{GenericArg, GenericArgs},
|
||||||
resolver::resolver_for_expr,
|
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 stdx::always;
|
||||||
use syntax::ast::RangeOp;
|
use syntax::ast::RangeOp;
|
||||||
|
|
||||||
@ -28,7 +28,7 @@
|
|||||||
const_or_path_to_chalk, generic_arg_to_chalk, lower_to_chalk_mutability, ParamLoweringMode,
|
const_or_path_to_chalk, generic_arg_to_chalk, lower_to_chalk_mutability, ParamLoweringMode,
|
||||||
},
|
},
|
||||||
mapping::{from_chalk, ToChalk},
|
mapping::{from_chalk, ToChalk},
|
||||||
method_resolution::{self, VisibleFromModule},
|
method_resolution::{self, lang_names_for_bin_op, VisibleFromModule},
|
||||||
primitive::{self, UintTy},
|
primitive::{self, UintTy},
|
||||||
static_lifetime, to_chalk_trait_id,
|
static_lifetime, to_chalk_trait_id,
|
||||||
utils::{generics, Generics},
|
utils::{generics, Generics},
|
||||||
@ -947,7 +947,9 @@ fn infer_overloadable_binop(
|
|||||||
let lhs_ty = self.infer_expr(lhs, &lhs_expectation);
|
let lhs_ty = self.infer_expr(lhs, &lhs_expectation);
|
||||||
let rhs_ty = self.table.new_type_var();
|
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 {
|
let func = match func {
|
||||||
Some(func) => func,
|
Some(func) => func,
|
||||||
None => {
|
None => {
|
||||||
@ -1473,55 +1475,4 @@ fn builtin_binary_op_rhs_expectation(&mut self, op: BinaryOp, lhs_ty: Ty) -> Opt
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -336,7 +336,7 @@ pub fn all_impls(&self) -> impl Iterator<Item = ImplId> + '_ {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inherent_impl_crates_query(
|
pub(crate) fn inherent_impl_crates_query(
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
krate: CrateId,
|
krate: CrateId,
|
||||||
fp: TyFingerprint,
|
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.
|
/// Look up the method with the given name.
|
||||||
pub(crate) fn lookup_method(
|
pub(crate) fn lookup_method(
|
||||||
ty: &Canonical<Ty>,
|
ty: &Canonical<Ty>,
|
||||||
|
@ -357,6 +357,26 @@ pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function
|
|||||||
self.imp.resolve_method_call(call).map(Function::from)
|
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> {
|
pub fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> {
|
||||||
self.imp.resolve_method_call_as_callable(call)
|
self.imp.resolve_method_call_as_callable(call)
|
||||||
}
|
}
|
||||||
@ -1066,6 +1086,26 @@ fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<FunctionId>
|
|||||||
self.analyze(call.syntax())?.resolve_method_call(self.db, call)
|
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> {
|
fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> {
|
||||||
self.analyze(call.syntax())?.resolve_method_call_as_callable(self.db, call)
|
self.analyze(call.syntax())?.resolve_method_call_as_callable(self.db, call)
|
||||||
}
|
}
|
||||||
|
@ -25,15 +25,20 @@
|
|||||||
Lookup, ModuleDefId, VariantId,
|
Lookup, ModuleDefId, VariantId,
|
||||||
};
|
};
|
||||||
use hir_expand::{
|
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::{
|
use hir_ty::{
|
||||||
diagnostics::{
|
diagnostics::{
|
||||||
record_literal_missing_fields, record_pattern_missing_fields, unsafe_expressions,
|
record_literal_missing_fields, record_pattern_missing_fields, unsafe_expressions,
|
||||||
UnsafeExpr,
|
UnsafeExpr,
|
||||||
},
|
},
|
||||||
method_resolution, Adjust, Adjustment, AutoBorrow, InferenceResult, Interner, Substitution,
|
method_resolution::{self, lang_names_for_bin_op},
|
||||||
TyExt, TyKind, TyLoweringContext,
|
Adjust, Adjustment, AutoBorrow, InferenceResult, Interner, Substitution, Ty, TyExt, TyKind,
|
||||||
|
TyLoweringContext,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
@ -255,8 +260,90 @@ pub(crate) fn resolve_method_call(
|
|||||||
) -> Option<FunctionId> {
|
) -> Option<FunctionId> {
|
||||||
let expr_id = self.expr_id(db, &call.clone().into())?;
|
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_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(
|
pub(crate) fn resolve_field(
|
||||||
@ -666,6 +753,29 @@ fn resolve_impl_method(
|
|||||||
let fun_data = db.function_data(func);
|
let fun_data = db.function_data(func);
|
||||||
method_resolution::lookup_impl_method(self_ty, db, trait_env, impled_trait, &fun_data.name)
|
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(
|
fn scope_for(
|
||||||
|
@ -127,10 +127,12 @@ pub fn name(&self, db: &RootDatabase) -> Option<Name> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: IdentClass as a name no longer fits
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum IdentClass {
|
pub enum IdentClass {
|
||||||
NameClass(NameClass),
|
NameClass(NameClass),
|
||||||
NameRefClass(NameRefClass),
|
NameRefClass(NameRefClass),
|
||||||
|
Operator(OperatorClass),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IdentClass {
|
impl IdentClass {
|
||||||
@ -147,6 +149,11 @@ pub fn classify_node(
|
|||||||
.map(IdentClass::NameClass)
|
.map(IdentClass::NameClass)
|
||||||
.or_else(|| NameRefClass::classify_lifetime(sema, &lifetime).map(IdentClass::NameRefClass))
|
.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,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -184,6 +191,33 @@ pub fn definitions(self) -> ArrayVec<Definition, 2> {
|
|||||||
res.push(Definition::Local(local_ref));
|
res.push(Definition::Local(local_ref));
|
||||||
res.push(Definition::Field(field_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
|
res
|
||||||
}
|
}
|
||||||
@ -332,6 +366,52 @@ pub fn classify_lifetime(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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
|
/// 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
|
/// 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.
|
/// reference most of the time, but there are a couple of annoying exceptions.
|
||||||
|
@ -39,7 +39,11 @@ pub(crate) fn goto_definition(
|
|||||||
| T![super]
|
| T![super]
|
||||||
| T![crate]
|
| T![crate]
|
||||||
| T![Self]
|
| 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,
|
kind if kind.is_trivia() => 0,
|
||||||
_ => 1,
|
_ => 1,
|
||||||
})?;
|
})?;
|
||||||
@ -1628,6 +1632,122 @@ fn foo() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
foo!(bar$0);
|
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;
|
||||||
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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> {
|
fn find_defs(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> FxHashSet<Definition> {
|
||||||
sema.descend_into_macros(token)
|
sema.descend_into_macros(token)
|
||||||
.into_iter()
|
.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()
|
.flatten()
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
use hir::{HasSource, Semantics};
|
use hir::{HasSource, Semantics};
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
base_db::FileRange,
|
base_db::FileRange,
|
||||||
defs::{Definition, IdentClass},
|
defs::{Definition, IdentClass, OperatorClass},
|
||||||
famous_defs::FamousDefs,
|
famous_defs::FamousDefs,
|
||||||
helpers::pick_best_token,
|
helpers::pick_best_token,
|
||||||
FxIndexSet, RootDatabase,
|
FxIndexSet, RootDatabase,
|
||||||
@ -101,7 +101,10 @@ pub(crate) fn hover(
|
|||||||
let offset = range.start();
|
let offset = range.start();
|
||||||
|
|
||||||
let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
|
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,
|
T!['('] | T![')'] => 2,
|
||||||
kind if kind.is_trivia() => 0,
|
kind if kind.is_trivia() => 0,
|
||||||
_ => 1,
|
_ => 1,
|
||||||
@ -136,6 +139,11 @@ pub(crate) fn hover(
|
|||||||
.filter_map(|token| {
|
.filter_map(|token| {
|
||||||
let node = token.parent()?;
|
let node = token.parent()?;
|
||||||
let class = IdentClass::classify_token(sema, token)?;
|
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()))
|
Some(class.definitions().into_iter().zip(iter::once(node).cycle()))
|
||||||
})
|
})
|
||||||
.flatten()
|
.flatten()
|
||||||
|
@ -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
|
||||||
|
```
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -90,7 +90,7 @@ pub(crate) fn moniker(
|
|||||||
.descend_into_macros(original_token.clone())
|
.descend_into_macros(original_token.clone())
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|token| {
|
.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))
|
it.into_iter().flat_map(|def| def_to_moniker(sema.db, def, current_crate))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -204,7 +204,7 @@ pub fn compute(analysis: &Analysis) -> StaticIndex<'_> {
|
|||||||
|
|
||||||
fn get_definition(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> Option<Definition> {
|
fn get_definition(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> Option<Definition> {
|
||||||
for token in sema.descend_into_macros(token) {
|
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() {
|
if let Some(&[x]) = def.as_deref() {
|
||||||
return Some(x);
|
return Some(x);
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
Reference in New Issue
Block a user