From 001a86dc03104b75df732d69257d22526cf422b7 Mon Sep 17 00:00:00 2001 From: Hasan Ali Date: Fri, 15 May 2020 22:23:49 +0100 Subject: Fix completion and hover for module and function of same name --- crates/ra_hir/src/semantics.rs | 6 ++- crates/ra_hir/src/source_analyzer.rs | 51 ++++++++++++++++++++++ .../src/completion/complete_qualified_path.rs | 30 ++++++++++++- crates/ra_ide/src/hover.rs | 17 ++++++++ 4 files changed, 102 insertions(+), 2 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs index 515e5eb17..c1129a494 100644 --- a/crates/ra_hir/src/semantics.rs +++ b/crates/ra_hir/src/semantics.rs @@ -23,7 +23,7 @@ use crate::{ db::HirDatabase, diagnostics::Diagnostic, semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, - source_analyzer::{resolve_hir_path, SourceAnalyzer}, + source_analyzer::{resolve_hir_path, resolve_hir_path_qualifier, SourceAnalyzer}, AssocItem, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef, Module, ModuleDef, Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, }; @@ -451,6 +451,10 @@ impl<'a, DB: HirDatabase> SemanticsScope<'a, DB> { pub fn resolve_hir_path(&self, path: &Path) -> Option { resolve_hir_path(self.db, &self.resolver, path) } + + pub fn resolve_hir_path_qualifier(&self, path: &Path) -> Option { + resolve_hir_path_qualifier(self.db, &self.resolver, path) + } } // FIXME: Change `HasSource` trait to work with `Semantics` and remove this? diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs index c862a4f48..4b509f07c 100644 --- a/crates/ra_hir/src/source_analyzer.rs +++ b/crates/ra_hir/src/source_analyzer.rs @@ -226,6 +226,17 @@ impl SourceAnalyzer { // This must be a normal source file rather than macro file. let hir_path = crate::Path::from_src(path.clone(), &Hygiene::new(db.upcast(), self.file_id))?; + + // Case where path is a qualifier of another path, e.g. foo::bar::Baz where we + // trying to resolve foo::bar. + if let Some(outer_path) = path.syntax().parent().and_then(ast::Path::cast) { + if let Some(qualifier) = outer_path.qualifier() { + if path == &qualifier { + return resolve_hir_path_qualifier(db, &self.resolver, &hir_path); + } + } + } + resolve_hir_path(db, &self.resolver, &hir_path) } @@ -404,6 +415,7 @@ pub(crate) fn resolve_hir_path( TypeNs::BuiltinType(it) => PathResolution::Def(it.into()), TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()), }); + let body_owner = resolver.body_owner(); let values = resolver.resolve_path_in_value_ns_fully(db.upcast(), path.mod_path()).and_then(|val| { @@ -426,9 +438,48 @@ pub(crate) fn resolve_hir_path( .resolve_module_path_in_items(db.upcast(), path.mod_path()) .take_types() .map(|it| PathResolution::Def(it.into())); + types.or(values).or(items).or_else(|| { resolver .resolve_path_as_macro(db.upcast(), path.mod_path()) .map(|def| PathResolution::Macro(def.into())) }) } + +/// Resolves a path where we know it is a qualifier of another path. +/// +/// For example, if we have: +/// ``` +/// mod my { +/// pub mod foo { +/// struct Bar; +/// } +/// +/// pub fn foo() {} +/// } +/// ``` +/// then we know that `foo` in `my::foo::Bar` refers to the module, not the function. +pub(crate) fn resolve_hir_path_qualifier( + db: &dyn HirDatabase, + resolver: &Resolver, + path: &crate::Path, +) -> Option { + let items = resolver + .resolve_module_path_in_items(db.upcast(), path.mod_path()) + .take_types() + .map(|it| PathResolution::Def(it.into())); + + if items.is_some() { + return items; + } + + resolver.resolve_path_in_type_ns_fully(db.upcast(), path.mod_path()).map(|ty| match ty { + TypeNs::SelfType(it) => PathResolution::SelfType(it.into()), + TypeNs::GenericParam(id) => PathResolution::TypeParam(TypeParam { id }), + TypeNs::AdtSelfType(it) | TypeNs::AdtId(it) => PathResolution::Def(Adt::from(it).into()), + TypeNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), + TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()), + TypeNs::BuiltinType(it) => PathResolution::Def(it.into()), + TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()), + }) +} diff --git a/crates/ra_ide/src/completion/complete_qualified_path.rs b/crates/ra_ide/src/completion/complete_qualified_path.rs index 7fcd22525..db7430454 100644 --- a/crates/ra_ide/src/completion/complete_qualified_path.rs +++ b/crates/ra_ide/src/completion/complete_qualified_path.rs @@ -20,7 +20,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon let scope = ctx.scope(); let context_module = scope.module(); - let res = match scope.resolve_hir_path(&path) { + let res = match scope.resolve_hir_path_qualifier(&path) { Some(res) => res, None => return, }; @@ -225,6 +225,34 @@ mod tests { ); } + #[test] + fn completes_mod_with_same_name_as_function() { + assert_debug_snapshot!( + do_reference_completion( + r" + use self::my::<|>; + + mod my { + pub struct Bar; + } + + fn my() {} + " + ), + @r###" + [ + CompletionItem { + label: "Bar", + source_range: 31..31, + delete: 31..31, + insert: "Bar", + kind: Struct, + }, + ] + "### + ); + } + #[test] fn path_visibility() { assert_debug_snapshot!( diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index 06d4f1c63..befa977c7 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -921,4 +921,21 @@ fn func(foo: i32) { if true { <|>foo; }; } &["unsafe trait foo"], ); } + + #[test] + fn test_hover_mod_with_same_name_as_function() { + check_hover_result( + " + //- /lib.rs + use self::m<|>y::Bar; + + mod my { + pub struct Bar; + } + + fn my() {} + ", + &["mod my"], + ); + } } -- cgit v1.2.3