aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorKirill Bulatov <[email protected]>2020-02-09 22:22:12 +0000
committerKirill Bulatov <[email protected]>2020-02-12 15:18:41 +0000
commit24ab3e80ca258a3db21bf263225c52d9995a2ea0 (patch)
tree8704eab78d09528e5d81a1fb328abb26fa448c01 /crates
parent5bf669860984a2c058b3bdc3e43b4993a0f25b31 (diff)
Resolve methods and functions better
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_assists/src/handlers/auto_import.rs23
-rw-r--r--crates/ra_hir/src/source_analyzer.rs60
2 files changed, 74 insertions, 9 deletions
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs
index 1fb701da5..10984d8ad 100644
--- a/crates/ra_assists/src/handlers/auto_import.rs
+++ b/crates/ra_assists/src/handlers/auto_import.rs
@@ -46,9 +46,9 @@ pub(crate) fn auto_import(ctx: AssistCtx) -> Option<Assist> {
46 46
47 let name_ref_to_import = 47 let name_ref_to_import =
48 path_under_caret.syntax().descendants().find_map(ast::NameRef::cast)?; 48 path_under_caret.syntax().descendants().find_map(ast::NameRef::cast)?;
49 if source_analyzer 49 if dbg!(source_analyzer
50 .resolve_path(ctx.db, &name_ref_to_import.syntax().ancestors().find_map(ast::Path::cast)?) 50 .resolve_path(ctx.db, &name_ref_to_import.syntax().ancestors().find_map(ast::Path::cast)?))
51 .is_some() 51 .is_some()
52 { 52 {
53 return None; 53 return None;
54 } 54 }
@@ -290,4 +290,21 @@ mod tests {
290 ", 290 ",
291 ); 291 );
292 } 292 }
293
294 #[test]
295 fn not_applicable_for_imported_function() {
296 check_assist_not_applicable(
297 auto_import,
298 r"
299 pub mod test_mod {
300 pub fn test_function() {}
301 }
302
303 use test_mod::test_function;
304 fn main() {
305 test_function<|>
306 }
307 ",
308 );
309 }
293} 310}
diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs
index bb9a35c5d..49e1a10e0 100644
--- a/crates/ra_hir/src/source_analyzer.rs
+++ b/crates/ra_hir/src/source_analyzer.rs
@@ -20,7 +20,10 @@ use hir_def::{
20use hir_expand::{ 20use hir_expand::{
21 hygiene::Hygiene, name::AsName, AstId, HirFileId, InFile, MacroCallId, MacroCallKind, 21 hygiene::Hygiene, name::AsName, AstId, HirFileId, InFile, MacroCallId, MacroCallKind,
22}; 22};
23use hir_ty::{InEnvironment, InferenceResult, TraitEnvironment}; 23use hir_ty::{
24 method_resolution::{iterate_method_candidates, LookupMode},
25 Canonical, InEnvironment, InferenceResult, TraitEnvironment,
26};
24use ra_syntax::{ 27use ra_syntax::{
25 ast::{self, AstNode}, 28 ast::{self, AstNode},
26 AstPtr, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextUnit, 29 AstPtr, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextUnit,
@@ -28,8 +31,8 @@ use ra_syntax::{
28use rustc_hash::FxHashSet; 31use rustc_hash::FxHashSet;
29 32
30use crate::{ 33use crate::{
31 db::HirDatabase, Adt, Const, DefWithBody, EnumVariant, Function, Local, MacroDef, Name, Path, 34 db::HirDatabase, Adt, AssocItem, Const, DefWithBody, EnumVariant, Function, Local, MacroDef,
32 ScopeDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, 35 ModuleDef, Name, Path, ScopeDef, Static, Struct, Trait, Type, TypeAlias, TypeParam,
33}; 36};
34 37
35/// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of 38/// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of
@@ -289,9 +292,11 @@ impl SourceAnalyzer {
289 292
290 pub fn resolve_path(&self, db: &impl HirDatabase, path: &ast::Path) -> Option<PathResolution> { 293 pub fn resolve_path(&self, db: &impl HirDatabase, path: &ast::Path) -> Option<PathResolution> {
291 if let Some(path_expr) = path.syntax().parent().and_then(ast::PathExpr::cast) { 294 if let Some(path_expr) = path.syntax().parent().and_then(ast::PathExpr::cast) {
292 let expr_id = self.expr_id(&path_expr.into())?; 295 let path_resolution = self
293 if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_expr(expr_id) { 296 .resolve_as_full_path(path_expr.clone())
294 return Some(PathResolution::AssocItem(assoc.into())); 297 .or_else(|| self.resolve_as_path_to_method(db, &path_expr));
298 if path_resolution.is_some() {
299 return path_resolution;
295 } 300 }
296 } 301 }
297 if let Some(path_pat) = path.syntax().parent().and_then(ast::PathPat::cast) { 302 if let Some(path_pat) = path.syntax().parent().and_then(ast::PathPat::cast) {
@@ -305,6 +310,49 @@ impl SourceAnalyzer {
305 self.resolve_hir_path(db, &hir_path) 310 self.resolve_hir_path(db, &hir_path)
306 } 311 }
307 312
313 fn resolve_as_full_path(&self, path_expr: ast::PathExpr) -> Option<PathResolution> {
314 let expr_id = self.expr_id(&path_expr.into())?;
315 self.infer
316 .as_ref()?
317 .assoc_resolutions_for_expr(expr_id)
318 .map(|assoc| PathResolution::AssocItem(assoc.into()))
319 }
320
321 fn resolve_as_path_to_method(
322 &self,
323 db: &impl HirDatabase,
324 path_expr: &ast::PathExpr,
325 ) -> Option<PathResolution> {
326 let full_path = path_expr.path()?;
327 let path_to_method = full_path.qualifier()?;
328 let method_name = full_path.segment()?.syntax().to_string();
329 match self.resolve_path(db, &path_to_method)? {
330 PathResolution::Def(ModuleDef::Adt(adt)) => {
331 let ty = adt.ty(db);
332 iterate_method_candidates(
333 &Canonical { value: ty.ty.value, num_vars: 0 },
334 db,
335 ty.ty.environment,
336 self.resolver.krate()?,
337 &self.resolver.traits_in_scope(db),
338 None,
339 LookupMode::Path,
340 |_, assoc_item_id| {
341 let assoc = assoc_item_id.into();
342 if let AssocItem::Function(function) = assoc {
343 if function.name(db).to_string() == method_name {
344 return Some(assoc);
345 }
346 }
347 None
348 },
349 )
350 }
351 _ => None,
352 }
353 .map(PathResolution::AssocItem)
354 }
355
308 fn resolve_local_name(&self, name_ref: &ast::NameRef) -> Option<ScopeEntryWithSyntax> { 356 fn resolve_local_name(&self, name_ref: &ast::NameRef) -> Option<ScopeEntryWithSyntax> {
309 let name = name_ref.as_name(); 357 let name = name_ref.as_name();
310 let source_map = self.body_source_map.as_ref()?; 358 let source_map = self.body_source_map.as_ref()?;