Skip to content

Commit 48d17b4

Browse files
committed
Move dual blanket impl logic from source analyzer to goto_def
1 parent 1eb9d15 commit 48d17b4

File tree

9 files changed

+93
-114
lines changed

9 files changed

+93
-114
lines changed

src/tools/rust-analyzer/crates/hir-def/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -910,6 +910,7 @@ pub enum AssocItemId {
910910
ConstId(ConstId),
911911
TypeAliasId(TypeAliasId),
912912
}
913+
913914
// FIXME: not every function, ... is actually an assoc item. maybe we should make
914915
// sure that you can only turn actual assoc items into AssocItemIds. This would
915916
// require not implementing From, and instead having some checked way of

src/tools/rust-analyzer/crates/hir-expand/src/name.rs

+12
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,24 @@ impl PartialEq<Symbol> for Name {
5151
}
5252
}
5353

54+
impl PartialEq<&Symbol> for Name {
55+
fn eq(&self, &sym: &&Symbol) -> bool {
56+
self.symbol == *sym
57+
}
58+
}
59+
5460
impl PartialEq<Name> for Symbol {
5561
fn eq(&self, name: &Name) -> bool {
5662
*self == name.symbol
5763
}
5864
}
5965

66+
impl PartialEq<Name> for &Symbol {
67+
fn eq(&self, name: &Name) -> bool {
68+
**self == name.symbol
69+
}
70+
}
71+
6072
/// Wrapper of `Name` to print the name without "r#" even when it is a raw identifier.
6173
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
6274
pub struct UnescapedName<'a>(&'a Name);

src/tools/rust-analyzer/crates/hir/src/lib.rs

+9
Original file line numberDiff line numberDiff line change
@@ -2756,6 +2756,15 @@ impl Trait {
27562756
traits.iter().map(|tr| Trait::from(*tr)).collect()
27572757
}
27582758

2759+
pub fn function(self, db: &dyn HirDatabase, name: impl PartialEq<Name>) -> Option<Function> {
2760+
db.trait_data(self.id).items.iter().find(|(n, _)| name == *n).and_then(
2761+
|&(_, it)| match it {
2762+
AssocItemId::FunctionId(id) => Some(Function { id }),
2763+
_ => None,
2764+
},
2765+
)
2766+
}
2767+
27592768
pub fn items(self, db: &dyn HirDatabase) -> Vec<AssocItem> {
27602769
db.trait_data(self.id).items.iter().map(|(_name, it)| (*it).into()).collect()
27612770
}

src/tools/rust-analyzer/crates/hir/src/semantics.rs

+14-2
Original file line numberDiff line numberDiff line change
@@ -1439,8 +1439,20 @@ impl<'db> SemanticsImpl<'db> {
14391439
self.analyze(call.syntax())?.resolve_method_call_fallback(self.db, call)
14401440
}
14411441

1442-
pub fn resolve_known_blanket_dual_impls(&self, call: &ast::MethodCallExpr) -> Option<Function> {
1443-
self.analyze(call.syntax())?.resolve_known_blanket_dual_impls(self.db, call)
1442+
/// Env is used to derive the trait environment
1443+
// FIXME: better api for the trait environment
1444+
pub fn resolve_impl_method(
1445+
&self,
1446+
env: Type,
1447+
trait_: Trait,
1448+
func: Function,
1449+
subst: impl IntoIterator<Item = Type>,
1450+
) -> Option<Function> {
1451+
let mut substs = hir_ty::TyBuilder::subst_for_def(self.db, TraitId::from(trait_), None);
1452+
for s in subst {
1453+
substs = substs.push(s.ty);
1454+
}
1455+
Some(self.db.lookup_impl_method(env.env, func.into(), substs.build()).0.into())
14441456
}
14451457

14461458
fn resolve_range_pat(&self, range_pat: &ast::RangePat) -> Option<StructId> {

src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs

-74
Original file line numberDiff line numberDiff line change
@@ -322,68 +322,6 @@ impl SourceAnalyzer {
322322
}
323323
}
324324

325-
// If the method is into(), try_into(), parse(), resolve it to from, try_from, from_str.
326-
pub(crate) fn resolve_known_blanket_dual_impls(
327-
&self,
328-
db: &dyn HirDatabase,
329-
call: &ast::MethodCallExpr,
330-
) -> Option<Function> {
331-
// e.g. if the method call is let b = a.into(),
332-
// - receiver_type is A (type of a)
333-
// - return_type is B (type of b)
334-
// We will find the definition of B::from(a: A).
335-
let callable = self.resolve_method_call_as_callable(db, call)?;
336-
let (_, receiver_type) = callable.receiver_param(db)?;
337-
let return_type = callable.return_type();
338-
let (search_method, substs) = match call.name_ref()?.text().as_str() {
339-
"into" => {
340-
let trait_ =
341-
self.resolver.resolve_known_trait(db.upcast(), &path![core::convert::From])?;
342-
(
343-
self.trait_fn(db, trait_, "from")?,
344-
hir_ty::TyBuilder::subst_for_def(db, trait_, None)
345-
.push(return_type.ty)
346-
.push(receiver_type.ty)
347-
.build(),
348-
)
349-
}
350-
"try_into" => {
351-
let trait_ = self
352-
.resolver
353-
.resolve_known_trait(db.upcast(), &path![core::convert::TryFrom])?;
354-
(
355-
self.trait_fn(db, trait_, "try_from")?,
356-
hir_ty::TyBuilder::subst_for_def(db, trait_, None)
357-
// If the method is try_into() or parse(), return_type is Result<T, Error>.
358-
// Get T from type arguments of Result<T, Error>.
359-
.push(return_type.type_arguments().next()?.ty)
360-
.push(receiver_type.ty)
361-
.build(),
362-
)
363-
}
364-
"parse" => {
365-
let trait_ =
366-
self.resolver.resolve_known_trait(db.upcast(), &path![core::str::FromStr])?;
367-
(
368-
self.trait_fn(db, trait_, "from_str")?,
369-
hir_ty::TyBuilder::subst_for_def(db, trait_, None)
370-
.push(return_type.type_arguments().next()?.ty)
371-
.build(),
372-
)
373-
}
374-
_ => return None,
375-
};
376-
377-
let found_method = self.resolve_impl_method_or_trait_def(db, search_method, substs);
378-
// If found_method == search_method, the method in trait itself is resolved.
379-
// It means the blanket dual impl is not found.
380-
if found_method == search_method {
381-
None
382-
} else {
383-
Some(found_method.into())
384-
}
385-
}
386-
387325
pub(crate) fn resolve_expr_as_callable(
388326
&self,
389327
db: &dyn HirDatabase,
@@ -1309,18 +1247,6 @@ impl SourceAnalyzer {
13091247
Some((trait_id, fn_id))
13101248
}
13111249

1312-
fn trait_fn(
1313-
&self,
1314-
db: &dyn HirDatabase,
1315-
trait_id: TraitId,
1316-
method_name: &str,
1317-
) -> Option<FunctionId> {
1318-
db.trait_data(trait_id).items.iter().find_map(|(item_name, item)| match item {
1319-
AssocItemId::FunctionId(t) if item_name.as_str() == method_name => Some(*t),
1320-
_ => None,
1321-
})
1322-
}
1323-
13241250
fn ty_of_expr(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<&Ty> {
13251251
self.infer.as_ref()?.type_of_expr_or_pat(self.expr_id(db, expr)?)
13261252
}

src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs

+12
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ impl FamousDefs<'_, '_> {
4646
self.find_trait("core:cmp:Ord")
4747
}
4848

49+
pub fn core_convert_FromStr(&self) -> Option<Trait> {
50+
self.find_trait("core:str:FromStr")
51+
}
52+
4953
pub fn core_convert_From(&self) -> Option<Trait> {
5054
self.find_trait("core:convert:From")
5155
}
@@ -54,6 +58,14 @@ impl FamousDefs<'_, '_> {
5458
self.find_trait("core:convert:Into")
5559
}
5660

61+
pub fn core_convert_TryFrom(&self) -> Option<Trait> {
62+
self.find_trait("core:convert:TryFrom")
63+
}
64+
65+
pub fn core_convert_TryInto(&self) -> Option<Trait> {
66+
self.find_trait("core:convert:TryInto")
67+
}
68+
5769
pub fn core_convert_Index(&self) -> Option<Trait> {
5870
self.find_trait("core:ops:Index")
5971
}

src/tools/rust-analyzer/crates/ide/src/goto_definition.rs

+39-28
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,14 @@ use crate::{
55
navigation_target::{self, ToNav},
66
FilePosition, NavigationTarget, RangeInfo, TryToNav, UpmappingResult,
77
};
8-
use hir::{AsAssocItem, AssocItem, FileRange, InFile, MacroFileIdExt, ModuleDef, Semantics};
8+
use hir::{
9+
sym, AsAssocItem, AssocItem, CallableKind, FileRange, HasCrate, InFile, MacroFileIdExt,
10+
ModuleDef, Semantics,
11+
};
912
use ide_db::{
1013
base_db::{AnchoredPath, FileLoader, SourceDatabase},
1114
defs::{Definition, IdentClass},
15+
famous_defs::FamousDefs,
1216
helpers::pick_best_token,
1317
RootDatabase, SymbolKind,
1418
};
@@ -129,15 +133,45 @@ pub(crate) fn goto_definition(
129133
Some(RangeInfo::new(original_token.text_range(), navs))
130134
}
131135

132-
// If the token is into(), try_into(), parse(), search the definition of From, TryFrom, FromStr.
136+
// If the token is into(), try_into(), search the definition of From, TryFrom.
133137
fn find_definition_for_known_blanket_dual_impls(
134138
sema: &Semantics<'_, RootDatabase>,
135139
original_token: &SyntaxToken,
136140
) -> Option<Vec<NavigationTarget>> {
137141
let method_call = ast::MethodCallExpr::cast(original_token.parent()?.parent()?)?;
138-
let target_method = sema.resolve_known_blanket_dual_impls(&method_call)?;
139-
140-
let def = Definition::from(target_method);
142+
let callable = sema.resolve_method_call_as_callable(&method_call)?;
143+
let CallableKind::Function(f) = callable.kind() else { return None };
144+
let t = f.as_assoc_item(sema.db)?.container_trait(sema.db)?;
145+
146+
let return_type = callable.return_type();
147+
let fd = FamousDefs(sema, return_type.krate(sema.db));
148+
let fn_name = f.name(sema.db);
149+
let f = if fn_name == sym::into && fd.core_convert_Into() == Some(t) {
150+
let dual = fd.core_convert_From()?;
151+
let dual_f = dual.function(sema.db, &sym::from)?;
152+
sema.resolve_impl_method(
153+
return_type.clone(),
154+
dual,
155+
dual_f,
156+
[return_type, callable.receiver_param(sema.db)?.1],
157+
)?
158+
} else if fn_name == sym::try_into && fd.core_convert_TryInto() == Some(t) {
159+
let dual = fd.core_convert_TryFrom()?;
160+
let dual_f = dual.function(sema.db, &sym::try_from)?;
161+
sema.resolve_impl_method(
162+
return_type.clone(),
163+
dual,
164+
dual_f,
165+
// Extract the `T` from `Result<T, ..>`
166+
[return_type.type_arguments().next()?, callable.receiver_param(sema.db)?.1],
167+
)?
168+
} else {
169+
return None;
170+
};
171+
// Assert that we got a trait impl function, if we are back in a trait definition we didn't
172+
// succeed
173+
let _t = f.as_assoc_item(sema.db)?.implemented_trait(sema.db)?;
174+
let def = Definition::from(f);
141175
Some(def_to_nav(sema.db, def))
142176
}
143177

@@ -3157,29 +3191,6 @@ impl TryInto<B> for A {
31573191
fn f() {
31583192
let a = A;
31593193
let b: Result<B, _> = a.try_into$0();
3160-
}
3161-
"#,
3162-
);
3163-
}
3164-
3165-
#[test]
3166-
fn parse_call_to_from_str_definition() {
3167-
check(
3168-
r#"
3169-
//- minicore: from, str
3170-
struct A;
3171-
3172-
impl FromStr for A {
3173-
type Error = String;
3174-
3175-
fn from_str(value: &str) -> Result<Self, Self::Error> {
3176-
//^^^^^^^^
3177-
Ok(A)
3178-
}
3179-
}
3180-
3181-
fn f() {
3182-
let a: Result<A, _> = "aaaaaa".parse$0();
31833194
}
31843195
"#,
31853196
);

src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs

+6
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ define_symbols! {
240240
format_unsafe_arg,
241241
format,
242242
freeze,
243+
from,
243244
From,
244245
FromStr,
245246
from_output,
@@ -273,6 +274,8 @@ define_symbols! {
273274
index_mut,
274275
index,
275276
Index,
277+
into,
278+
Into,
276279
into_future,
277280
into_iter,
278281
IntoFuture,
@@ -361,6 +364,7 @@ define_symbols! {
361364
panic_nounwind,
362365
panic,
363366
Param,
367+
parse,
364368
partial_ord,
365369
PartialEq,
366370
PartialOrd,
@@ -459,8 +463,10 @@ define_symbols! {
459463
transmute_opts,
460464
transmute_trait,
461465
transparent,
466+
try_into,
462467
Try,
463468
TryFrom,
469+
try_from,
464470
tuple_trait,
465471
u128,
466472
u16,

src/tools/rust-analyzer/crates/test-utils/src/minicore.rs

-10
Original file line numberDiff line numberDiff line change
@@ -1574,15 +1574,6 @@ pub mod str {
15741574
pub const unsafe fn from_utf8_unchecked(v: &[u8]) -> &str {
15751575
""
15761576
}
1577-
pub trait FromStr: Sized {
1578-
type Err;
1579-
fn from_str(s: &str) -> Result<Self, Self::Err>;
1580-
}
1581-
impl str {
1582-
pub fn parse<F: FromStr>(&self) -> Result<F, F::Err> {
1583-
FromStr::from_str(self)
1584-
}
1585-
}
15861577
}
15871578
// endregion:str
15881579

@@ -1857,7 +1848,6 @@ pub mod prelude {
18571848
option::Option::{self, None, Some}, // :option
18581849
panic, // :panic
18591850
result::Result::{self, Err, Ok}, // :result
1860-
str::FromStr, // :str
18611851
};
18621852
}
18631853

0 commit comments

Comments
 (0)