mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-01 11:31:15 +00:00
Merge pull request #18984 from Veykril/push-nszlmxoxyxss
internal: Move dual blanket impl logic from source analyzer to goto_def
This commit is contained in:
commit
9aa0ee1bba
@ -910,6 +910,7 @@ pub enum AssocItemId {
|
|||||||
ConstId(ConstId),
|
ConstId(ConstId),
|
||||||
TypeAliasId(TypeAliasId),
|
TypeAliasId(TypeAliasId),
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: not every function, ... is actually an assoc item. maybe we should make
|
// FIXME: not every function, ... is actually an assoc item. maybe we should make
|
||||||
// sure that you can only turn actual assoc items into AssocItemIds. This would
|
// sure that you can only turn actual assoc items into AssocItemIds. This would
|
||||||
// require not implementing From, and instead having some checked way of
|
// require not implementing From, and instead having some checked way of
|
||||||
|
@ -51,12 +51,24 @@ impl PartialEq<Symbol> for Name {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialEq<&Symbol> for Name {
|
||||||
|
fn eq(&self, &sym: &&Symbol) -> bool {
|
||||||
|
self.symbol == *sym
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PartialEq<Name> for Symbol {
|
impl PartialEq<Name> for Symbol {
|
||||||
fn eq(&self, name: &Name) -> bool {
|
fn eq(&self, name: &Name) -> bool {
|
||||||
*self == name.symbol
|
*self == name.symbol
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialEq<Name> for &Symbol {
|
||||||
|
fn eq(&self, name: &Name) -> bool {
|
||||||
|
**self == name.symbol
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Wrapper of `Name` to print the name without "r#" even when it is a raw identifier.
|
/// Wrapper of `Name` to print the name without "r#" even when it is a raw identifier.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct UnescapedName<'a>(&'a Name);
|
pub struct UnescapedName<'a>(&'a Name);
|
||||||
|
@ -2756,6 +2756,15 @@ impl Trait {
|
|||||||
traits.iter().map(|tr| Trait::from(*tr)).collect()
|
traits.iter().map(|tr| Trait::from(*tr)).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn function(self, db: &dyn HirDatabase, name: impl PartialEq<Name>) -> Option<Function> {
|
||||||
|
db.trait_data(self.id).items.iter().find(|(n, _)| name == *n).and_then(
|
||||||
|
|&(_, it)| match it {
|
||||||
|
AssocItemId::FunctionId(id) => Some(Function { id }),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn items(self, db: &dyn HirDatabase) -> Vec<AssocItem> {
|
pub fn items(self, db: &dyn HirDatabase) -> Vec<AssocItem> {
|
||||||
db.trait_data(self.id).items.iter().map(|(_name, it)| (*it).into()).collect()
|
db.trait_data(self.id).items.iter().map(|(_name, it)| (*it).into()).collect()
|
||||||
}
|
}
|
||||||
@ -4673,6 +4682,10 @@ impl Type {
|
|||||||
matches!(self.ty.kind(Interner), TyKind::Scalar(Scalar::Bool))
|
matches!(self.ty.kind(Interner), TyKind::Scalar(Scalar::Bool))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_str(&self) -> bool {
|
||||||
|
matches!(self.ty.kind(Interner), TyKind::Str)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_never(&self) -> bool {
|
pub fn is_never(&self) -> bool {
|
||||||
matches!(self.ty.kind(Interner), TyKind::Never)
|
matches!(self.ty.kind(Interner), TyKind::Never)
|
||||||
}
|
}
|
||||||
|
@ -1439,8 +1439,20 @@ impl<'db> SemanticsImpl<'db> {
|
|||||||
self.analyze(call.syntax())?.resolve_method_call_fallback(self.db, call)
|
self.analyze(call.syntax())?.resolve_method_call_fallback(self.db, call)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve_known_blanket_dual_impls(&self, call: &ast::MethodCallExpr) -> Option<Function> {
|
/// Env is used to derive the trait environment
|
||||||
self.analyze(call.syntax())?.resolve_known_blanket_dual_impls(self.db, call)
|
// FIXME: better api for the trait environment
|
||||||
|
pub fn resolve_trait_impl_method(
|
||||||
|
&self,
|
||||||
|
env: Type,
|
||||||
|
trait_: Trait,
|
||||||
|
func: Function,
|
||||||
|
subst: impl IntoIterator<Item = Type>,
|
||||||
|
) -> Option<Function> {
|
||||||
|
let mut substs = hir_ty::TyBuilder::subst_for_def(self.db, TraitId::from(trait_), None);
|
||||||
|
for s in subst {
|
||||||
|
substs = substs.push(s.ty);
|
||||||
|
}
|
||||||
|
Some(self.db.lookup_impl_method(env.env, func.into(), substs.build()).0.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_range_pat(&self, range_pat: &ast::RangePat) -> Option<StructId> {
|
fn resolve_range_pat(&self, range_pat: &ast::RangePat) -> Option<StructId> {
|
||||||
|
@ -322,68 +322,6 @@ impl SourceAnalyzer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the method is into(), try_into(), parse(), resolve it to from, try_from, from_str.
|
|
||||||
pub(crate) fn resolve_known_blanket_dual_impls(
|
|
||||||
&self,
|
|
||||||
db: &dyn HirDatabase,
|
|
||||||
call: &ast::MethodCallExpr,
|
|
||||||
) -> Option<Function> {
|
|
||||||
// e.g. if the method call is let b = a.into(),
|
|
||||||
// - receiver_type is A (type of a)
|
|
||||||
// - return_type is B (type of b)
|
|
||||||
// We will find the definition of B::from(a: A).
|
|
||||||
let callable = self.resolve_method_call_as_callable(db, call)?;
|
|
||||||
let (_, receiver_type) = callable.receiver_param(db)?;
|
|
||||||
let return_type = callable.return_type();
|
|
||||||
let (search_method, substs) = match call.name_ref()?.text().as_str() {
|
|
||||||
"into" => {
|
|
||||||
let trait_ =
|
|
||||||
self.resolver.resolve_known_trait(db.upcast(), &path![core::convert::From])?;
|
|
||||||
(
|
|
||||||
self.trait_fn(db, trait_, "from")?,
|
|
||||||
hir_ty::TyBuilder::subst_for_def(db, trait_, None)
|
|
||||||
.push(return_type.ty)
|
|
||||||
.push(receiver_type.ty)
|
|
||||||
.build(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
"try_into" => {
|
|
||||||
let trait_ = self
|
|
||||||
.resolver
|
|
||||||
.resolve_known_trait(db.upcast(), &path![core::convert::TryFrom])?;
|
|
||||||
(
|
|
||||||
self.trait_fn(db, trait_, "try_from")?,
|
|
||||||
hir_ty::TyBuilder::subst_for_def(db, trait_, None)
|
|
||||||
// If the method is try_into() or parse(), return_type is Result<T, Error>.
|
|
||||||
// Get T from type arguments of Result<T, Error>.
|
|
||||||
.push(return_type.type_arguments().next()?.ty)
|
|
||||||
.push(receiver_type.ty)
|
|
||||||
.build(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
"parse" => {
|
|
||||||
let trait_ =
|
|
||||||
self.resolver.resolve_known_trait(db.upcast(), &path![core::str::FromStr])?;
|
|
||||||
(
|
|
||||||
self.trait_fn(db, trait_, "from_str")?,
|
|
||||||
hir_ty::TyBuilder::subst_for_def(db, trait_, None)
|
|
||||||
.push(return_type.type_arguments().next()?.ty)
|
|
||||||
.build(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
_ => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let found_method = self.resolve_impl_method_or_trait_def(db, search_method, substs);
|
|
||||||
// If found_method == search_method, the method in trait itself is resolved.
|
|
||||||
// It means the blanket dual impl is not found.
|
|
||||||
if found_method == search_method {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(found_method.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn resolve_expr_as_callable(
|
pub(crate) fn resolve_expr_as_callable(
|
||||||
&self,
|
&self,
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
@ -1309,18 +1247,6 @@ impl SourceAnalyzer {
|
|||||||
Some((trait_id, fn_id))
|
Some((trait_id, fn_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trait_fn(
|
|
||||||
&self,
|
|
||||||
db: &dyn HirDatabase,
|
|
||||||
trait_id: TraitId,
|
|
||||||
method_name: &str,
|
|
||||||
) -> Option<FunctionId> {
|
|
||||||
db.trait_data(trait_id).items.iter().find_map(|(item_name, item)| match item {
|
|
||||||
AssocItemId::FunctionId(t) if item_name.as_str() == method_name => Some(*t),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ty_of_expr(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<&Ty> {
|
fn ty_of_expr(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<&Ty> {
|
||||||
self.infer.as_ref()?.type_of_expr_or_pat(self.expr_id(db, expr)?)
|
self.infer.as_ref()?.type_of_expr_or_pat(self.expr_id(db, expr)?)
|
||||||
}
|
}
|
||||||
|
@ -46,6 +46,10 @@ impl FamousDefs<'_, '_> {
|
|||||||
self.find_trait("core:cmp:Ord")
|
self.find_trait("core:cmp:Ord")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn core_convert_FromStr(&self) -> Option<Trait> {
|
||||||
|
self.find_trait("core:str:FromStr")
|
||||||
|
}
|
||||||
|
|
||||||
pub fn core_convert_From(&self) -> Option<Trait> {
|
pub fn core_convert_From(&self) -> Option<Trait> {
|
||||||
self.find_trait("core:convert:From")
|
self.find_trait("core:convert:From")
|
||||||
}
|
}
|
||||||
@ -54,6 +58,14 @@ impl FamousDefs<'_, '_> {
|
|||||||
self.find_trait("core:convert:Into")
|
self.find_trait("core:convert:Into")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn core_convert_TryFrom(&self) -> Option<Trait> {
|
||||||
|
self.find_trait("core:convert:TryFrom")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn core_convert_TryInto(&self) -> Option<Trait> {
|
||||||
|
self.find_trait("core:convert:TryInto")
|
||||||
|
}
|
||||||
|
|
||||||
pub fn core_convert_Index(&self) -> Option<Trait> {
|
pub fn core_convert_Index(&self) -> Option<Trait> {
|
||||||
self.find_trait("core:ops:Index")
|
self.find_trait("core:ops:Index")
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,14 @@ use crate::{
|
|||||||
navigation_target::{self, ToNav},
|
navigation_target::{self, ToNav},
|
||||||
FilePosition, NavigationTarget, RangeInfo, TryToNav, UpmappingResult,
|
FilePosition, NavigationTarget, RangeInfo, TryToNav, UpmappingResult,
|
||||||
};
|
};
|
||||||
use hir::{AsAssocItem, AssocItem, FileRange, InFile, MacroFileIdExt, ModuleDef, Semantics};
|
use hir::{
|
||||||
|
sym, AsAssocItem, AssocItem, CallableKind, FileRange, HasCrate, InFile, MacroFileIdExt,
|
||||||
|
ModuleDef, Semantics,
|
||||||
|
};
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
base_db::{AnchoredPath, FileLoader, SourceDatabase},
|
base_db::{AnchoredPath, FileLoader, SourceDatabase},
|
||||||
defs::{Definition, IdentClass},
|
defs::{Definition, IdentClass},
|
||||||
|
famous_defs::FamousDefs,
|
||||||
helpers::pick_best_token,
|
helpers::pick_best_token,
|
||||||
RootDatabase, SymbolKind,
|
RootDatabase, SymbolKind,
|
||||||
};
|
};
|
||||||
@ -129,15 +133,65 @@ pub(crate) fn goto_definition(
|
|||||||
Some(RangeInfo::new(original_token.text_range(), navs))
|
Some(RangeInfo::new(original_token.text_range(), navs))
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the token is into(), try_into(), parse(), search the definition of From, TryFrom, FromStr.
|
// If the token is into(), try_into(), search the definition of From, TryFrom.
|
||||||
fn find_definition_for_known_blanket_dual_impls(
|
fn find_definition_for_known_blanket_dual_impls(
|
||||||
sema: &Semantics<'_, RootDatabase>,
|
sema: &Semantics<'_, RootDatabase>,
|
||||||
original_token: &SyntaxToken,
|
original_token: &SyntaxToken,
|
||||||
) -> Option<Vec<NavigationTarget>> {
|
) -> Option<Vec<NavigationTarget>> {
|
||||||
let method_call = ast::MethodCallExpr::cast(original_token.parent()?.parent()?)?;
|
let method_call = ast::MethodCallExpr::cast(original_token.parent()?.parent()?)?;
|
||||||
let target_method = sema.resolve_known_blanket_dual_impls(&method_call)?;
|
let callable = sema.resolve_method_call_as_callable(&method_call)?;
|
||||||
|
let CallableKind::Function(f) = callable.kind() else { return None };
|
||||||
|
let assoc = f.as_assoc_item(sema.db)?;
|
||||||
|
|
||||||
let def = Definition::from(target_method);
|
let return_type = callable.return_type();
|
||||||
|
let fd = FamousDefs(sema, return_type.krate(sema.db));
|
||||||
|
|
||||||
|
let t = match assoc.container(sema.db) {
|
||||||
|
hir::AssocItemContainer::Trait(t) => t,
|
||||||
|
hir::AssocItemContainer::Impl(impl_)
|
||||||
|
if impl_.self_ty(sema.db).is_str() && f.name(sema.db) == sym::parse =>
|
||||||
|
{
|
||||||
|
let t = fd.core_convert_FromStr()?;
|
||||||
|
let t_f = t.function(sema.db, &sym::from_str)?;
|
||||||
|
return sema
|
||||||
|
.resolve_trait_impl_method(
|
||||||
|
return_type.clone(),
|
||||||
|
t,
|
||||||
|
t_f,
|
||||||
|
[return_type.type_arguments().next()?],
|
||||||
|
)
|
||||||
|
.map(|f| def_to_nav(sema.db, f.into()));
|
||||||
|
}
|
||||||
|
hir::AssocItemContainer::Impl(_) => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let fn_name = f.name(sema.db);
|
||||||
|
let f = if fn_name == sym::into && fd.core_convert_Into() == Some(t) {
|
||||||
|
let dual = fd.core_convert_From()?;
|
||||||
|
let dual_f = dual.function(sema.db, &sym::from)?;
|
||||||
|
sema.resolve_trait_impl_method(
|
||||||
|
return_type.clone(),
|
||||||
|
dual,
|
||||||
|
dual_f,
|
||||||
|
[return_type, callable.receiver_param(sema.db)?.1],
|
||||||
|
)?
|
||||||
|
} else if fn_name == sym::try_into && fd.core_convert_TryInto() == Some(t) {
|
||||||
|
let dual = fd.core_convert_TryFrom()?;
|
||||||
|
let dual_f = dual.function(sema.db, &sym::try_from)?;
|
||||||
|
sema.resolve_trait_impl_method(
|
||||||
|
return_type.clone(),
|
||||||
|
dual,
|
||||||
|
dual_f,
|
||||||
|
// Extract the `T` from `Result<T, ..>`
|
||||||
|
[return_type.type_arguments().next()?, callable.receiver_param(sema.db)?.1],
|
||||||
|
)?
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
// Assert that we got a trait impl function, if we are back in a trait definition we didn't
|
||||||
|
// succeed
|
||||||
|
let _t = f.as_assoc_item(sema.db)?.implemented_trait(sema.db)?;
|
||||||
|
let def = Definition::from(f);
|
||||||
Some(def_to_nav(sema.db, def))
|
Some(def_to_nav(sema.db, def))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3168,16 +3222,13 @@ fn f() {
|
|||||||
r#"
|
r#"
|
||||||
//- minicore: from, str
|
//- minicore: from, str
|
||||||
struct A;
|
struct A;
|
||||||
|
|
||||||
impl FromStr for A {
|
impl FromStr for A {
|
||||||
type Error = String;
|
type Error = String;
|
||||||
|
|
||||||
fn from_str(value: &str) -> Result<Self, Self::Error> {
|
fn from_str(value: &str) -> Result<Self, Self::Error> {
|
||||||
//^^^^^^^^
|
//^^^^^^^^
|
||||||
Ok(A)
|
Ok(A)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn f() {
|
fn f() {
|
||||||
let a: Result<A, _> = "aaaaaa".parse$0();
|
let a: Result<A, _> = "aaaaaa".parse$0();
|
||||||
}
|
}
|
||||||
|
@ -240,8 +240,10 @@ define_symbols! {
|
|||||||
format_unsafe_arg,
|
format_unsafe_arg,
|
||||||
format,
|
format,
|
||||||
freeze,
|
freeze,
|
||||||
|
from,
|
||||||
From,
|
From,
|
||||||
FromStr,
|
FromStr,
|
||||||
|
from_str,
|
||||||
from_output,
|
from_output,
|
||||||
from_residual,
|
from_residual,
|
||||||
from_usize,
|
from_usize,
|
||||||
@ -273,6 +275,8 @@ define_symbols! {
|
|||||||
index_mut,
|
index_mut,
|
||||||
index,
|
index,
|
||||||
Index,
|
Index,
|
||||||
|
into,
|
||||||
|
Into,
|
||||||
into_future,
|
into_future,
|
||||||
into_iter,
|
into_iter,
|
||||||
IntoFuture,
|
IntoFuture,
|
||||||
@ -361,6 +365,7 @@ define_symbols! {
|
|||||||
panic_nounwind,
|
panic_nounwind,
|
||||||
panic,
|
panic,
|
||||||
Param,
|
Param,
|
||||||
|
parse,
|
||||||
partial_ord,
|
partial_ord,
|
||||||
PartialEq,
|
PartialEq,
|
||||||
PartialOrd,
|
PartialOrd,
|
||||||
@ -459,8 +464,10 @@ define_symbols! {
|
|||||||
transmute_opts,
|
transmute_opts,
|
||||||
transmute_trait,
|
transmute_trait,
|
||||||
transparent,
|
transparent,
|
||||||
|
try_into,
|
||||||
Try,
|
Try,
|
||||||
TryFrom,
|
TryFrom,
|
||||||
|
try_from,
|
||||||
tuple_trait,
|
tuple_trait,
|
||||||
u128,
|
u128,
|
||||||
u16,
|
u16,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user