about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLukas Wirth <lukastw97@gmail.com>2025-01-20 15:47:43 +0000
committerGitHub <noreply@github.com>2025-01-20 15:47:43 +0000
commitea3897b35ae71e17971055fe376d70cf99e8f1a0 (patch)
treeab3479e1744ca2d30acb0ee6c77befdf66841da1
parent1eb9d15e42922ad6bfc851b5745c59e9a2f6cfbc (diff)
parent4bc683dfd2934ed7e9ae055e393f06ecf9115b19 (diff)
downloadrust-ea3897b35ae71e17971055fe376d70cf99e8f1a0.tar.gz
rust-ea3897b35ae71e17971055fe376d70cf99e8f1a0.zip
Merge pull request #18984 from Veykril/push-nszlmxoxyxss
internal: Move dual blanket impl logic from source analyzer to goto_def
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/lib.rs1
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/name.rs12
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/lib.rs13
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/semantics.rs16
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs74
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs12
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/goto_definition.rs65
-rw-r--r--src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs7
8 files changed, 117 insertions, 83 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
index 84c105a0a34..da9ffae8aab 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
@@ -910,6 +910,7 @@ pub enum AssocItemId {
     ConstId(ConstId),
     TypeAliasId(TypeAliasId),
 }
+
 // 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
 // require not implementing From, and instead having some checked way of
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
index cc53d2e34aa..9cfa3b4b32b 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
@@ -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 {
     fn eq(&self, name: &Name) -> bool {
         *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.
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct UnescapedName<'a>(&'a Name);
diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index db3121d3cd3..4938478bb12 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -2756,6 +2756,15 @@ impl Trait {
         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> {
         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))
     }
 
+    pub fn is_str(&self) -> bool {
+        matches!(self.ty.kind(Interner), TyKind::Str)
+    }
+
     pub fn is_never(&self) -> bool {
         matches!(self.ty.kind(Interner), TyKind::Never)
     }
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
index 523bc6f10aa..708db2c08dd 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
@@ -1439,8 +1439,20 @@ impl<'db> SemanticsImpl<'db> {
         self.analyze(call.syntax())?.resolve_method_call_fallback(self.db, call)
     }
 
-    pub fn resolve_known_blanket_dual_impls(&self, call: &ast::MethodCallExpr) -> Option<Function> {
-        self.analyze(call.syntax())?.resolve_known_blanket_dual_impls(self.db, call)
+    /// Env is used to derive the trait environment
+    // 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> {
diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
index 6b78d7a3631..b699ccde412 100644
--- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
@@ -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(
         &self,
         db: &dyn HirDatabase,
@@ -1309,18 +1247,6 @@ impl SourceAnalyzer {
         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> {
         self.infer.as_ref()?.type_of_expr_or_pat(self.expr_id(db, expr)?)
     }
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs
index 9e3506d6f53..1dc61e3f0a8 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs
@@ -46,6 +46,10 @@ impl FamousDefs<'_, '_> {
         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> {
         self.find_trait("core:convert:From")
     }
@@ -54,6 +58,14 @@ impl FamousDefs<'_, '_> {
         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> {
         self.find_trait("core:ops:Index")
     }
diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
index f804cc36772..99d0d6af716 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
@@ -5,10 +5,14 @@ use crate::{
     navigation_target::{self, ToNav},
     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::{
     base_db::{AnchoredPath, FileLoader, SourceDatabase},
     defs::{Definition, IdentClass},
+    famous_defs::FamousDefs,
     helpers::pick_best_token,
     RootDatabase, SymbolKind,
 };
@@ -129,15 +133,65 @@ pub(crate) fn goto_definition(
     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(
     sema: &Semantics<'_, RootDatabase>,
     original_token: &SyntaxToken,
 ) -> Option<Vec<NavigationTarget>> {
     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 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 def = Definition::from(target_method);
+    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))
 }
 
@@ -3168,16 +3222,13 @@ fn f() {
             r#"
 //- minicore: from, str
 struct A;
-
 impl FromStr for A {
     type Error = String;
-
     fn from_str(value: &str) -> Result<Self, Self::Error> {
      //^^^^^^^^
         Ok(A)
     }
 }
-
 fn f() {
     let a: Result<A, _> = "aaaaaa".parse$0();
 }
diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
index b3b46421b50..59c0c9dca1c 100644
--- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
+++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
@@ -240,8 +240,10 @@ define_symbols! {
     format_unsafe_arg,
     format,
     freeze,
+    from,
     From,
     FromStr,
+    from_str,
     from_output,
     from_residual,
     from_usize,
@@ -273,6 +275,8 @@ define_symbols! {
     index_mut,
     index,
     Index,
+    into,
+    Into,
     into_future,
     into_iter,
     IntoFuture,
@@ -361,6 +365,7 @@ define_symbols! {
     panic_nounwind,
     panic,
     Param,
+    parse,
     partial_ord,
     PartialEq,
     PartialOrd,
@@ -459,8 +464,10 @@ define_symbols! {
     transmute_opts,
     transmute_trait,
     transparent,
+    try_into,
     Try,
     TryFrom,
+    try_from,
     tuple_trait,
     u128,
     u16,