diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 220baeb152..4938478bb1 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -4682,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/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 0b76f2e112..708db2c08d 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -1441,7 +1441,7 @@ impl<'db> SemanticsImpl<'db> { /// Env is used to derive the trait environment // FIXME: better api for the trait environment - pub fn resolve_impl_method( + pub fn resolve_trait_impl_method( &self, env: Type, trait_: Trait, diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index 2c6c6e6e6c..99d0d6af71 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -141,15 +141,35 @@ fn find_definition_for_known_blanket_dual_impls( let method_call = ast::MethodCallExpr::cast(original_token.parent()?.parent()?)?; let callable = sema.resolve_method_call_as_callable(&method_call)?; let CallableKind::Function(f) = callable.kind() else { return None }; - let t = f.as_assoc_item(sema.db)?.container_trait(sema.db)?; + 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 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_impl_method( + sema.resolve_trait_impl_method( return_type.clone(), dual, dual_f, @@ -158,7 +178,7 @@ fn find_definition_for_known_blanket_dual_impls( } 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_impl_method( + sema.resolve_trait_impl_method( return_type.clone(), dual, dual_f, @@ -3191,6 +3211,26 @@ impl TryInto for A { fn f() { let a = A; let b: Result = a.try_into$0(); +} + "#, + ); + } + + #[test] + fn parse_call_to_from_str_definition() { + check( + r#" +//- minicore: from, str +struct A; +impl FromStr for A { + type Error = String; + fn from_str(value: &str) -> Result { + //^^^^^^^^ + Ok(A) + } +} +fn f() { + let a: Result = "aaaaaa".parse$0(); } "#, ); diff --git a/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs index e7c3668c0f..59c0c9dca1 100644 --- a/crates/intern/src/symbol/symbols.rs +++ b/crates/intern/src/symbol/symbols.rs @@ -243,6 +243,7 @@ define_symbols! { from, From, FromStr, + from_str, from_output, from_residual, from_usize, diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index 4a44eaf5d1..fd06736a25 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -1574,6 +1574,15 @@ pub mod str { pub const unsafe fn from_utf8_unchecked(v: &[u8]) -> &str { "" } + pub trait FromStr: Sized { + type Err; + fn from_str(s: &str) -> Result; + } + impl str { + pub fn parse(&self) -> Result { + FromStr::from_str(self) + } + } } // endregion:str @@ -1848,6 +1857,7 @@ pub mod prelude { option::Option::{self, None, Some}, // :option panic, // :panic result::Result::{self, Err, Ok}, // :result + str::FromStr, // :str }; }