Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir/src/source_analyzer.rs')
| -rw-r--r-- | crates/hir/src/source_analyzer.rs | 72 |
1 files changed, 48 insertions, 24 deletions
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 23fcd02b57..762f172c2a 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -29,8 +29,9 @@ use syntax::{ }; use crate::{ - db::HirDatabase, semantics::PathResolution, Adt, BuiltinType, Const, Field, Function, Local, - MacroDef, ModuleDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, Variant, + db::HirDatabase, semantics::PathResolution, Adt, BuiltinAttr, BuiltinType, Const, Field, + Function, Local, MacroDef, ModuleDef, Static, Struct, ToolModule, Trait, Type, TypeAlias, + TypeParam, Variant, }; use base_db::CrateId; @@ -246,24 +247,14 @@ impl SourceAnalyzer { } } - pub(crate) fn resolve_path_as_macro( - &self, - db: &dyn HirDatabase, - path: &ast::Path, - ) -> Option<MacroDef> { - // This must be a normal source file rather than macro file. - let hygiene = Hygiene::new(db.upcast(), self.file_id); - let ctx = body::LowerCtx::with_hygiene(db.upcast(), &hygiene); - let hir_path = Path::from_src(path.clone(), &ctx)?; - resolve_hir_path_as_macro(db, &self.resolver, &hir_path) - } - pub(crate) fn resolve_path( &self, db: &dyn HirDatabase, path: &ast::Path, ) -> Option<PathResolution> { - let parent = || path.syntax().parent(); + let parent = path.syntax().parent(); + let parent = || parent.clone(); + let mut prefer_value_ns = false; if let Some(path_expr) = parent().and_then(ast::PathExpr::cast) { let expr_id = self.expr_id(db, &path_expr.into())?; @@ -318,29 +309,62 @@ impl SourceAnalyzer { let ctx = body::LowerCtx::with_hygiene(db.upcast(), &hygiene); let hir_path = Path::from_src(path.clone(), &ctx)?; - // Case where path is a qualifier of another path, e.g. foo::bar::Baz where we are + // Case where path is a qualifier of a use tree, e.g. foo::bar::{Baz, Qux} where we are // trying to resolve foo::bar. - if let Some(outer_path) = parent().and_then(ast::Path::cast) { - if let Some(qualifier) = outer_path.qualifier() { - if path == &qualifier { + if let Some(use_tree) = parent().and_then(ast::UseTree::cast) { + if let Some(qualifier) = use_tree.path() { + if path == &qualifier && use_tree.coloncolon_token().is_some() { return resolve_hir_path_qualifier(db, &self.resolver, &hir_path); } } } - // Case where path is a qualifier of a use tree, e.g. foo::bar::{Baz, Qux} where we are + + let is_path_of_attr = path + .top_path() + .syntax() + .ancestors() + .nth(2) // Path -> Meta -> Attr + .map_or(false, |it| ast::Attr::can_cast(it.kind())); + + // Case where path is a qualifier of another path, e.g. foo::bar::Baz where we are // trying to resolve foo::bar. - if let Some(use_tree) = parent().and_then(ast::UseTree::cast) { - if let Some(qualifier) = use_tree.path() { - if path == &qualifier && use_tree.coloncolon_token().is_some() { + if let Some(outer_path) = path.parent_path() { + if let Some(qualifier) = outer_path.qualifier() { + if path == &qualifier { return resolve_hir_path_qualifier(db, &self.resolver, &hir_path); } } + } else if is_path_of_attr { + let res = resolve_hir_path_as_macro(db, &self.resolver, &hir_path); + return match res { + Some(_) => res.map(PathResolution::Macro), + None => path.as_single_name_ref().and_then(|name_ref| { + if let builtin @ Some(_) = BuiltinAttr::by_name(&name_ref.text()) { + builtin.map(PathResolution::BuiltinAttr) + } else if let tool @ Some(_) = ToolModule::by_name(&name_ref.text()) { + tool.map(PathResolution::ToolModule) + } else { + None + } + }), + }; } - if parent().map_or(false, |it| ast::Visibility::can_cast(it.kind())) { + let res = if parent().map_or(false, |it| ast::Visibility::can_cast(it.kind())) { resolve_hir_path_qualifier(db, &self.resolver, &hir_path) } else { resolve_hir_path_(db, &self.resolver, &hir_path, prefer_value_ns) + }; + match res { + Some(_) => res, + // this labels any path that starts with a tool module as the tool itself, this is technically wrong + // but there is no benefit in differentiating these two cases for the time being + None if is_path_of_attr => path + .first_segment() + .and_then(|seg| seg.name_ref()) + .and_then(|name_ref| ToolModule::by_name(&name_ref.text())) + .map(PathResolution::ToolModule), + None => None, } } |