Skip to content

Commit 55aee54

Browse files
committed
Use Semantics::resolve_method_call_as_callable to find implementation
1 parent 36d705e commit 55aee54

File tree

6 files changed

+89
-59
lines changed

6 files changed

+89
-59
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,9 @@ macro_rules! __known_path {
398398
(core::fmt::Debug) => {};
399399
(std::fmt::format) => {};
400400
(core::ops::Try) => {};
401+
(core::convert::From) => {};
402+
(core::convert::TryFrom) => {};
403+
(core::str::FromStr) => {};
401404
($path:path) => {
402405
compile_error!("Please register your known path in the path module")
403406
};

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1446,6 +1446,10 @@ impl<'db> SemanticsImpl<'db> {
14461446
self.analyze(call.syntax())?.resolve_method_call_fallback(self.db, call)
14471447
}
14481448

1449+
pub fn resolve_known_blanket_dual_impls(&self, call: &ast::MethodCallExpr) -> Option<Function> {
1450+
self.analyze(call.syntax())?.resolve_known_blanket_dual_impls(self.db, call)
1451+
}
1452+
14491453
fn resolve_range_pat(&self, range_pat: &ast::RangePat) -> Option<StructId> {
14501454
self.analyze(range_pat.syntax())?.resolve_range_pat(self.db, range_pat)
14511455
}

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

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,68 @@ 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+
325387
pub(crate) fn resolve_expr_as_callable(
326388
&self,
327389
db: &dyn HirDatabase,
@@ -1247,6 +1309,18 @@ impl SourceAnalyzer {
12471309
Some((trait_id, fn_id))
12481310
}
12491311

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+
12501324
fn ty_of_expr(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<&Ty> {
12511325
self.infer.as_ref()?.type_of_expr_or_pat(self.expr_id(db, expr)?)
12521326
}

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

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,6 @@ impl FamousDefs<'_, '_> {
5050
self.find_trait("core:convert:From")
5151
}
5252

53-
pub fn core_convert_TryFrom(&self) -> Option<Trait> {
54-
self.find_trait("core:convert:TryFrom")
55-
}
56-
57-
pub fn core_str_FromStr(&self) -> Option<Trait> {
58-
self.find_trait("core:str:FromStr")
59-
}
60-
6153
pub fn core_convert_Into(&self) -> Option<Trait> {
6254
self.find_trait("core:convert:Into")
6355
}

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

Lines changed: 4 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,10 @@ use crate::{
55
navigation_target::{self, ToNav},
66
FilePosition, NavigationTarget, RangeInfo, TryToNav, UpmappingResult,
77
};
8-
use hir::{AsAssocItem, AssocItem, FileRange, Impl, InFile, MacroFileIdExt, ModuleDef, Semantics};
8+
use hir::{AsAssocItem, AssocItem, FileRange, InFile, MacroFileIdExt, ModuleDef, Semantics};
99
use ide_db::{
1010
base_db::{AnchoredPath, FileLoader, SourceDatabase},
1111
defs::{Definition, IdentClass},
12-
famous_defs::FamousDefs,
1312
helpers::pick_best_token,
1413
RootDatabase, SymbolKind,
1514
};
@@ -82,8 +81,7 @@ pub(crate) fn goto_definition(
8281
return Some(RangeInfo::new(original_token.text_range(), navs));
8382
}
8483

85-
if let Some(navs) = find_definition_for_known_blanket_dual_impls(sema, file_id, &original_token)
86-
{
84+
if let Some(navs) = find_definition_for_known_blanket_dual_impls(sema, &original_token) {
8785
return Some(RangeInfo::new(original_token.text_range(), navs));
8886
}
8987

@@ -134,58 +132,13 @@ pub(crate) fn goto_definition(
134132
// If the token is into(), try_into(), parse(), search the definition of From, TryFrom, FromStr.
135133
fn find_definition_for_known_blanket_dual_impls(
136134
sema: &Semantics<'_, RootDatabase>,
137-
file_id: FileId,
138135
original_token: &SyntaxToken,
139136
) -> Option<Vec<NavigationTarget>> {
140-
let db = sema.db;
141-
let krate = sema.file_to_module_def(file_id)?.krate();
142-
143-
// e.g. if the method call is let b = a.into(),
144-
// - receiver_type is A (type of a)
145-
// - return_type is B (type of b)
146-
// We will find the definition of B::from(a: A).
147137
let method_call = ast::MethodCallExpr::cast(original_token.parent()?.parent()?)?;
148-
let callable = sema.resolve_method_call_as_callable(&method_call)?;
149-
let (_, receiver_type) = callable.receiver_param(db)?;
150-
let return_type = callable.return_type();
151-
152-
let (search_method, search_trait, return_type) = match method_call.name_ref()?.text().as_str() {
153-
"into" => ("from", FamousDefs(sema, krate).core_convert_From()?, return_type),
154-
// If the method is try_into() or parse(), return_type is Result<T, Error>.
155-
// Get T from type arguments of Result<T, Error>.
156-
"try_into" => (
157-
"try_from",
158-
FamousDefs(sema, krate).core_convert_TryFrom()?,
159-
return_type.type_arguments().next()?,
160-
),
161-
"parse" => (
162-
"from_str",
163-
FamousDefs(sema, krate).core_str_FromStr()?,
164-
return_type.type_arguments().next()?,
165-
),
166-
_ => return None,
167-
};
168-
169-
let from_impls = Impl::all_for_type(db, return_type)
170-
.into_iter()
171-
.filter(|impl_| impl_.trait_(db).is_some_and(|trait_| trait_ == search_trait));
172-
let from_methods = from_impls.flat_map(|impl_| impl_.items(db)).filter_map(|item| match item {
173-
AssocItem::Function(function) if function.name(db).as_str() == search_method => {
174-
Some(function)
175-
}
176-
_ => None,
177-
});
178-
let target_method = from_methods.into_iter().find(|method| {
179-
let args = method.assoc_fn_params(db);
180-
181-
// FIXME: This condition does not work for complicated cases such as
182-
// receiver_type: Vec<i64>
183-
// arg.ty(): T: IntoIterator<Item = i64>
184-
args.first().is_some_and(|arg| receiver_type.could_coerce_to(db, arg.ty()))
185-
})?;
138+
let target_method = sema.resolve_known_blanket_dual_impls(&method_call)?;
186139

187140
let def = Definition::from(target_method);
188-
Some(def_to_nav(db, def))
141+
Some(def_to_nav(sema.db, def))
189142
}
190143

191144
fn try_lookup_include_path(

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ define_symbols! {
174174
const_param_ty,
175175
Context,
176176
Continue,
177+
convert,
177178
copy,
178179
Copy,
179180
core_panic,
@@ -239,6 +240,8 @@ define_symbols! {
239240
format_unsafe_arg,
240241
format,
241242
freeze,
243+
From,
244+
FromStr,
242245
from_output,
243246
from_residual,
244247
from_usize,
@@ -457,6 +460,7 @@ define_symbols! {
457460
transmute_trait,
458461
transparent,
459462
Try,
463+
TryFrom,
460464
tuple_trait,
461465
u128,
462466
u16,

0 commit comments

Comments
 (0)