diff options
-rw-r--r-- | crates/ra_hir/src/semantics.rs | 19 | ||||
-rw-r--r-- | crates/ra_hir/src/source_analyzer.rs | 51 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_qualified_path.rs | 30 | ||||
-rw-r--r-- | crates/ra_ide/src/hover.rs | 17 |
4 files changed, 115 insertions, 2 deletions
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs index 515e5eb17..7c1f79f27 100644 --- a/crates/ra_hir/src/semantics.rs +++ b/crates/ra_hir/src/semantics.rs | |||
@@ -23,7 +23,7 @@ use crate::{ | |||
23 | db::HirDatabase, | 23 | db::HirDatabase, |
24 | diagnostics::Diagnostic, | 24 | diagnostics::Diagnostic, |
25 | semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, | 25 | semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, |
26 | source_analyzer::{resolve_hir_path, SourceAnalyzer}, | 26 | source_analyzer::{resolve_hir_path, resolve_hir_path_qualifier, SourceAnalyzer}, |
27 | AssocItem, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef, Module, ModuleDef, | 27 | AssocItem, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef, Module, ModuleDef, |
28 | Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, | 28 | Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, |
29 | }; | 29 | }; |
@@ -451,6 +451,23 @@ impl<'a, DB: HirDatabase> SemanticsScope<'a, DB> { | |||
451 | pub fn resolve_hir_path(&self, path: &Path) -> Option<PathResolution> { | 451 | pub fn resolve_hir_path(&self, path: &Path) -> Option<PathResolution> { |
452 | resolve_hir_path(self.db, &self.resolver, path) | 452 | resolve_hir_path(self.db, &self.resolver, path) |
453 | } | 453 | } |
454 | |||
455 | /// Resolves a path where we know it is a qualifier of another path. | ||
456 | /// | ||
457 | /// For example, if we have: | ||
458 | /// ``` | ||
459 | /// mod my { | ||
460 | /// pub mod foo { | ||
461 | /// struct Bar; | ||
462 | /// } | ||
463 | /// | ||
464 | /// pub fn foo() {} | ||
465 | /// } | ||
466 | /// ``` | ||
467 | /// then we know that `foo` in `my::foo::Bar` refers to the module, not the function. | ||
468 | pub fn resolve_hir_path_qualifier(&self, path: &Path) -> Option<PathResolution> { | ||
469 | resolve_hir_path_qualifier(self.db, &self.resolver, path) | ||
470 | } | ||
454 | } | 471 | } |
455 | 472 | ||
456 | // FIXME: Change `HasSource` trait to work with `Semantics` and remove this? | 473 | // 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 { | |||
226 | // This must be a normal source file rather than macro file. | 226 | // This must be a normal source file rather than macro file. |
227 | let hir_path = | 227 | let hir_path = |
228 | crate::Path::from_src(path.clone(), &Hygiene::new(db.upcast(), self.file_id))?; | 228 | crate::Path::from_src(path.clone(), &Hygiene::new(db.upcast(), self.file_id))?; |
229 | |||
230 | // Case where path is a qualifier of another path, e.g. foo::bar::Baz where we | ||
231 | // trying to resolve foo::bar. | ||
232 | if let Some(outer_path) = path.syntax().parent().and_then(ast::Path::cast) { | ||
233 | if let Some(qualifier) = outer_path.qualifier() { | ||
234 | if path == &qualifier { | ||
235 | return resolve_hir_path_qualifier(db, &self.resolver, &hir_path); | ||
236 | } | ||
237 | } | ||
238 | } | ||
239 | |||
229 | resolve_hir_path(db, &self.resolver, &hir_path) | 240 | resolve_hir_path(db, &self.resolver, &hir_path) |
230 | } | 241 | } |
231 | 242 | ||
@@ -404,6 +415,7 @@ pub(crate) fn resolve_hir_path( | |||
404 | TypeNs::BuiltinType(it) => PathResolution::Def(it.into()), | 415 | TypeNs::BuiltinType(it) => PathResolution::Def(it.into()), |
405 | TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()), | 416 | TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()), |
406 | }); | 417 | }); |
418 | |||
407 | let body_owner = resolver.body_owner(); | 419 | let body_owner = resolver.body_owner(); |
408 | let values = | 420 | let values = |
409 | resolver.resolve_path_in_value_ns_fully(db.upcast(), path.mod_path()).and_then(|val| { | 421 | 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( | |||
426 | .resolve_module_path_in_items(db.upcast(), path.mod_path()) | 438 | .resolve_module_path_in_items(db.upcast(), path.mod_path()) |
427 | .take_types() | 439 | .take_types() |
428 | .map(|it| PathResolution::Def(it.into())); | 440 | .map(|it| PathResolution::Def(it.into())); |
441 | |||
429 | types.or(values).or(items).or_else(|| { | 442 | types.or(values).or(items).or_else(|| { |
430 | resolver | 443 | resolver |
431 | .resolve_path_as_macro(db.upcast(), path.mod_path()) | 444 | .resolve_path_as_macro(db.upcast(), path.mod_path()) |
432 | .map(|def| PathResolution::Macro(def.into())) | 445 | .map(|def| PathResolution::Macro(def.into())) |
433 | }) | 446 | }) |
434 | } | 447 | } |
448 | |||
449 | /// Resolves a path where we know it is a qualifier of another path. | ||
450 | /// | ||
451 | /// For example, if we have: | ||
452 | /// ``` | ||
453 | /// mod my { | ||
454 | /// pub mod foo { | ||
455 | /// struct Bar; | ||
456 | /// } | ||
457 | /// | ||
458 | /// pub fn foo() {} | ||
459 | /// } | ||
460 | /// ``` | ||
461 | /// then we know that `foo` in `my::foo::Bar` refers to the module, not the function. | ||
462 | pub(crate) fn resolve_hir_path_qualifier( | ||
463 | db: &dyn HirDatabase, | ||
464 | resolver: &Resolver, | ||
465 | path: &crate::Path, | ||
466 | ) -> Option<PathResolution> { | ||
467 | let items = resolver | ||
468 | .resolve_module_path_in_items(db.upcast(), path.mod_path()) | ||
469 | .take_types() | ||
470 | .map(|it| PathResolution::Def(it.into())); | ||
471 | |||
472 | if items.is_some() { | ||
473 | return items; | ||
474 | } | ||
475 | |||
476 | resolver.resolve_path_in_type_ns_fully(db.upcast(), path.mod_path()).map(|ty| match ty { | ||
477 | TypeNs::SelfType(it) => PathResolution::SelfType(it.into()), | ||
478 | TypeNs::GenericParam(id) => PathResolution::TypeParam(TypeParam { id }), | ||
479 | TypeNs::AdtSelfType(it) | TypeNs::AdtId(it) => PathResolution::Def(Adt::from(it).into()), | ||
480 | TypeNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), | ||
481 | TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()), | ||
482 | TypeNs::BuiltinType(it) => PathResolution::Def(it.into()), | ||
483 | TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()), | ||
484 | }) | ||
485 | } | ||
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 | |||
20 | let scope = ctx.scope(); | 20 | let scope = ctx.scope(); |
21 | let context_module = scope.module(); | 21 | let context_module = scope.module(); |
22 | 22 | ||
23 | let res = match scope.resolve_hir_path(&path) { | 23 | let res = match scope.resolve_hir_path_qualifier(&path) { |
24 | Some(res) => res, | 24 | Some(res) => res, |
25 | None => return, | 25 | None => return, |
26 | }; | 26 | }; |
@@ -226,6 +226,34 @@ mod tests { | |||
226 | } | 226 | } |
227 | 227 | ||
228 | #[test] | 228 | #[test] |
229 | fn completes_mod_with_same_name_as_function() { | ||
230 | assert_debug_snapshot!( | ||
231 | do_reference_completion( | ||
232 | r" | ||
233 | use self::my::<|>; | ||
234 | |||
235 | mod my { | ||
236 | pub struct Bar; | ||
237 | } | ||
238 | |||
239 | fn my() {} | ||
240 | " | ||
241 | ), | ||
242 | @r###" | ||
243 | [ | ||
244 | CompletionItem { | ||
245 | label: "Bar", | ||
246 | source_range: 31..31, | ||
247 | delete: 31..31, | ||
248 | insert: "Bar", | ||
249 | kind: Struct, | ||
250 | }, | ||
251 | ] | ||
252 | "### | ||
253 | ); | ||
254 | } | ||
255 | |||
256 | #[test] | ||
229 | fn path_visibility() { | 257 | fn path_visibility() { |
230 | assert_debug_snapshot!( | 258 | assert_debug_snapshot!( |
231 | do_reference_completion( | 259 | do_reference_completion( |
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; }; } | |||
921 | &["unsafe trait foo"], | 921 | &["unsafe trait foo"], |
922 | ); | 922 | ); |
923 | } | 923 | } |
924 | |||
925 | #[test] | ||
926 | fn test_hover_mod_with_same_name_as_function() { | ||
927 | check_hover_result( | ||
928 | " | ||
929 | //- /lib.rs | ||
930 | use self::m<|>y::Bar; | ||
931 | |||
932 | mod my { | ||
933 | pub struct Bar; | ||
934 | } | ||
935 | |||
936 | fn my() {} | ||
937 | ", | ||
938 | &["mod my"], | ||
939 | ); | ||
940 | } | ||
924 | } | 941 | } |