diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 1ecd2391b0f..01b2de515ad 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -472,27 +472,13 @@ pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { }); } - DefDiagnosticKind::UnresolvedImport { ast, index } => { - let use_item = ast.to_node(db.upcast()); - let hygiene = Hygiene::new(db.upcast(), ast.file_id); - let mut cur = 0; - let mut tree = None; - ModPath::expand_use_item( - db.upcast(), - InFile::new(ast.file_id, use_item), - &hygiene, - |_mod_path, use_tree, _is_glob, _alias| { - if cur == *index { - tree = Some(use_tree.clone()); - } + DefDiagnosticKind::UnresolvedImport { id, index } => { + let file_id = id.file_id(); + let item_tree = id.item_tree(db.upcast()); + let import = &item_tree[id.value]; - cur += 1; - }, - ); - - if let Some(tree) = tree { - sink.push(UnresolvedImport { file: ast.file_id, node: AstPtr::new(&tree) }); - } + let use_tree = import.use_tree_to_ast(db.upcast(), file_id, *index); + sink.push(UnresolvedImport { file: file_id, node: AstPtr::new(&use_tree) }); } DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } => { diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs index 11767d100f9..508736885b9 100644 --- a/crates/hir_def/src/item_tree.rs +++ b/crates/hir_def/src/item_tree.rs @@ -523,21 +523,38 @@ fn index(&self, id: FileItemTreeId) -> &N { } } -/// A desugared `use` import. #[derive(Debug, Clone, Eq, PartialEq)] pub struct Import { - pub path: Interned, - pub alias: Option, pub visibility: RawVisibilityId, - pub is_glob: bool, - /// AST ID of the `use` item this import was derived from. Note that many `Import`s can map to - /// the same `use` item. pub ast_id: FileAstId, - /// Index of this `Import` when the containing `Use` is visited via `ModPath::expand_use_item`. - /// - /// This can be used to get the `UseTree` this `Import` corresponds to and allows emitting - /// precise diagnostics. - pub index: usize, + pub use_tree: UseTree, +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct UseTree { + pub index: Idx, + kind: UseTreeKind, +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum UseTreeKind { + /// ```ignore + /// use path::to::Item; + /// use path::to::Item as Renamed; + /// use path::to::Trait as _; + /// ``` + Single { path: ModPath, alias: Option }, + + /// ```ignore + /// use *; // (invalid, but can occur in nested tree) + /// use path::*; + /// ``` + Glob { path: Option }, + + /// ```ignore + /// use prefix::{self, Item, ...}; + /// ``` + Prefixed { prefix: Option, list: Vec }, } #[derive(Debug, Clone, Eq, PartialEq)] @@ -711,6 +728,97 @@ pub struct MacroDef { pub ast_id: FileAstId, } +impl Import { + /// Maps a `UseTree` contained in this import back to its AST node. + pub fn use_tree_to_ast( + &self, + db: &dyn DefDatabase, + file_id: HirFileId, + index: Idx, + ) -> ast::UseTree { + // Re-lower the AST item and get the source map. + // Note: The AST unwraps are fine, since if they fail we should have never obtained `index`. + let ast = InFile::new(file_id, self.ast_id).to_node(db.upcast()); + let ast_use_tree = ast.use_tree().expect("missing `use_tree`"); + let hygiene = Hygiene::new(db.upcast(), file_id); + let (_, source_map) = + lower::lower_use_tree(db, &hygiene, ast_use_tree).expect("failed to lower use tree"); + source_map[index].clone() + } +} + +impl UseTree { + /// Expands the `UseTree` into individually imported `ModPath`s. + pub fn expand( + &self, + mut cb: impl FnMut(Idx, ModPath, /* is_glob */ bool, Option), + ) { + self.expand_impl(None, &mut cb) + } + + fn expand_impl( + &self, + prefix: Option, + cb: &mut dyn FnMut( + Idx, + ModPath, + /* is_glob */ bool, + Option, + ), + ) { + fn concat_mod_paths(prefix: Option, path: &ModPath) -> Option { + match (prefix, &path.kind) { + (None, _) => Some(path.clone()), + (Some(mut prefix), PathKind::Plain) => { + for segment in path.segments() { + prefix.push_segment(segment.clone()); + } + Some(prefix) + } + (Some(prefix), PathKind::Super(0)) => { + // `some::path::self` == `some::path` + if path.segments().is_empty() { + Some(prefix) + } else { + None + } + } + (Some(_), _) => None, + } + } + + match &self.kind { + UseTreeKind::Single { path, alias } => { + if let Some(path) = concat_mod_paths(prefix, path) { + cb(self.index, path, false, alias.clone()); + } + } + UseTreeKind::Glob { path: Some(path) } => { + if let Some(path) = concat_mod_paths(prefix, path) { + cb(self.index, path, true, None); + } + } + UseTreeKind::Glob { path: None } => { + if let Some(prefix) = prefix { + cb(self.index, prefix, true, None); + } + } + UseTreeKind::Prefixed { prefix: additional_prefix, list } => { + let prefix = match additional_prefix { + Some(path) => match concat_mod_paths(prefix, path) { + Some(path) => Some(path), + None => return, + }, + None => prefix, + }; + for tree in list { + tree.expand_impl(prefix.clone(), cb); + } + } + } + } +} + macro_rules! impl_froms { ($e:ident { $($v:ident ($t:ty)),* $(,)? }) => { $( diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs index b4389371fbb..a59a3dc37e3 100644 --- a/crates/hir_def/src/item_tree/lower.rs +++ b/crates/hir_def/src/item_tree/lower.rs @@ -35,7 +35,6 @@ pub(super) struct Ctx<'a> { db: &'a dyn DefDatabase, tree: ItemTree, hygiene: Hygiene, - file: HirFileId, source_ast_id_map: Arc, body_ctx: crate::body::LowerCtx<'a>, forced_visibility: Option, @@ -47,7 +46,6 @@ pub(super) fn new(db: &'a dyn DefDatabase, hygiene: Hygiene, file: HirFileId) -> db, tree: ItemTree::default(), hygiene, - file, source_ast_id_map: db.ast_id_map(file), body_ctx: crate::body::LowerCtx::new(db, file), forced_visibility: None, @@ -561,30 +559,13 @@ fn lower_impl(&mut self, impl_def: &ast::Impl) -> Option> { Some(id(self.data().impls.alloc(res))) } - fn lower_use(&mut self, use_item: &ast::Use) -> Vec> { + fn lower_use(&mut self, use_item: &ast::Use) -> Option> { let visibility = self.lower_visibility(use_item); let ast_id = self.source_ast_id_map.ast_id(use_item); + let (use_tree, _) = lower_use_tree(self.db, &self.hygiene, use_item.use_tree()?)?; - // Every use item can expand to many `Import`s. - let mut imports = Vec::new(); - let tree = self.tree.data_mut(); - ModPath::expand_use_item( - self.db, - InFile::new(self.file, use_item.clone()), - &self.hygiene, - |path, _use_tree, is_glob, alias| { - imports.push(id(tree.imports.alloc(Import { - path: Interned::new(path), - alias, - visibility, - is_glob, - ast_id, - index: imports.len(), - }))); - }, - ); - - imports + let res = Import { visibility, ast_id, use_tree }; + Some(id(self.data().imports.alloc(res))) } fn lower_extern_crate( @@ -884,3 +865,76 @@ fn lower_abi(abi: ast::Abi) -> Interned { } } } + +struct UseTreeLowering<'a> { + db: &'a dyn DefDatabase, + hygiene: &'a Hygiene, + mapping: Arena, +} + +impl UseTreeLowering<'_> { + fn lower_use_tree(&mut self, tree: ast::UseTree) -> Option { + if let Some(use_tree_list) = tree.use_tree_list() { + let prefix = match tree.path() { + // E.g. use something::{{{inner}}}; + None => None, + // E.g. `use something::{inner}` (prefix is `None`, path is `something`) + // or `use something::{path::{inner::{innerer}}}` (prefix is `something::path`, path is `inner`) + Some(path) => { + match ModPath::from_src(self.db, path, &self.hygiene) { + Some(it) => Some(it), + None => return None, // FIXME: report errors somewhere + } + } + }; + + let list = + use_tree_list.use_trees().filter_map(|tree| self.lower_use_tree(tree)).collect(); + + Some(self.use_tree(UseTreeKind::Prefixed { prefix, list }, tree)) + } else { + let is_glob = tree.star_token().is_some(); + let path = match tree.path() { + Some(path) => Some(ModPath::from_src(self.db, path, &self.hygiene)?), + None => None, + }; + let alias = tree.rename().map(|a| { + a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias) + }); + if alias.is_some() && is_glob { + return None; + } + + match (path, alias, is_glob) { + (path, None, true) => { + if path.is_none() { + cov_mark::hit!(glob_enum_group); + } + Some(self.use_tree(UseTreeKind::Glob { path }, tree)) + } + // Globs can't be renamed + (_, Some(_), true) | (None, None, false) => None, + // `bla::{ as Name}` is invalid + (None, Some(_), false) => None, + (Some(path), alias, false) => { + Some(self.use_tree(UseTreeKind::Single { path, alias }, tree)) + } + } + } + } + + fn use_tree(&mut self, kind: UseTreeKind, ast: ast::UseTree) -> UseTree { + let index = self.mapping.alloc(ast); + UseTree { index, kind } + } +} + +pub(super) fn lower_use_tree( + db: &dyn DefDatabase, + hygiene: &Hygiene, + tree: ast::UseTree, +) -> Option<(UseTree, Arena)> { + let mut lowering = UseTreeLowering { db, hygiene, mapping: Arena::new() }; + let tree = lowering.lower_use_tree(tree)?; + Some((tree, lowering.mapping)) +} diff --git a/crates/hir_def/src/item_tree/pretty.rs b/crates/hir_def/src/item_tree/pretty.rs index 9394a5de66b..53631ab192b 100644 --- a/crates/hir_def/src/item_tree/pretty.rs +++ b/crates/hir_def/src/item_tree/pretty.rs @@ -163,21 +163,46 @@ fn print_fields_and_where_clause(&mut self, fields: &Fields, params: &GenericPar } } + fn print_use_tree(&mut self, use_tree: &UseTree) { + match &use_tree.kind { + UseTreeKind::Single { path, alias } => { + w!(self, "{}", path); + if let Some(alias) = alias { + w!(self, " as {}", alias); + } + } + UseTreeKind::Glob { path } => { + if let Some(path) = path { + w!(self, "{}::", path); + } + w!(self, "*"); + } + UseTreeKind::Prefixed { prefix, list } => { + if let Some(prefix) = prefix { + w!(self, "{}::", prefix); + } + w!(self, "{{"); + for (i, tree) in list.iter().enumerate() { + if i != 0 { + w!(self, ", "); + } + self.print_use_tree(tree); + } + w!(self, "}}"); + } + } + } + fn print_mod_item(&mut self, item: ModItem) { self.print_attrs_of(item); match item { ModItem::Import(it) => { - let Import { visibility, path, is_glob, alias, ast_id: _, index } = &self.tree[it]; + let Import { visibility, use_tree, ast_id: _ } = &self.tree[it]; self.print_visibility(*visibility); - w!(self, "use {}", path); - if *is_glob { - w!(self, "::*"); - } - if let Some(alias) = alias { - w!(self, " as {}", alias); - } - wln!(self, "; // {}", index); + w!(self, "use "); + self.print_use_tree(use_tree); + wln!(self, ";"); } ModItem::ExternCrate(it) => { let ExternCrate { name, alias, visibility, ast_id: _ } = &self.tree[it]; diff --git a/crates/hir_def/src/item_tree/tests.rs b/crates/hir_def/src/item_tree/tests.rs index 6407871b504..20773aa6994 100644 --- a/crates/hir_def/src/item_tree/tests.rs +++ b/crates/hir_def/src/item_tree/tests.rs @@ -26,6 +26,8 @@ fn imports() { /// docs on import use crate::{A, B}; + +use a::{c, d::{e}}; "#, expect![[r##" #![doc = " file comment"] // AttrId { is_doc_comment: true, ast_index: 0 } @@ -36,19 +38,14 @@ fn imports() { pub(super) extern crate bli; - pub use crate::path::nested; // 0 + pub use crate::path::{nested, items as renamed, Trait as _}; - pub use crate::path::items as renamed; // 1 - - pub use crate::path::Trait as _; // 2 - - pub(self) use globs::*; // 0 + pub(self) use globs::*; #[doc = " docs on import"] // AttrId { is_doc_comment: true, ast_index: 0 } - pub(self) use crate::A; // 0 + pub(self) use crate::{A, B}; - #[doc = " docs on import"] // AttrId { is_doc_comment: true, ast_index: 0 } - pub(self) use crate::B; // 1 + pub(self) use a::{c, d::{e}}; "##]], ); } @@ -218,7 +215,7 @@ fn fn_in_module() {} #[doc = " outer"] // AttrId { is_doc_comment: true, ast_index: 0 } #[doc = " inner"] // AttrId { is_doc_comment: true, ast_index: 1 } pub(self) mod inline { - pub(self) use super::*; // 0 + pub(self) use super::*; // flags = 0x2 pub(self) fn fn_in_module() -> (); diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index 3ea47290803..4296c630452 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs @@ -17,6 +17,7 @@ }; use hir_expand::{InFile, MacroCallLoc}; use itertools::Itertools; +use la_arena::Idx; use rustc_hash::{FxHashMap, FxHashSet}; use syntax::ast; @@ -143,7 +144,7 @@ fn namespaces(&self) -> PerNs { #[derive(Clone, Debug, Eq, PartialEq)] enum ImportSource { - Import(ItemTreeId), + Import { id: ItemTreeId, use_tree: Idx }, ExternCrate(ItemTreeId), } @@ -165,20 +166,26 @@ fn from_use( krate: CrateId, tree: &ItemTree, id: ItemTreeId, - ) -> Self { + ) -> Vec { let it = &tree[id.value]; let attrs = &tree.attrs(db, krate, ModItem::from(id.value).into()); let visibility = &tree[it.visibility]; - Self { - path: it.path.clone(), - alias: it.alias.clone(), - visibility: visibility.clone(), - is_glob: it.is_glob, - is_prelude: attrs.by_key("prelude_import").exists(), - is_extern_crate: false, - is_macro_use: false, - source: ImportSource::Import(id), - } + let is_prelude = attrs.by_key("prelude_import").exists(); + + let mut res = Vec::new(); + it.use_tree.expand(|idx, path, is_glob, alias| { + res.push(Self { + path: Interned::new(path), // FIXME this makes little sense + alias, + visibility: visibility.clone(), + is_glob, + is_prelude, + is_extern_crate: false, + is_macro_use: false, + source: ImportSource::Import { id, use_tree: idx }, + }); + }); + res } fn from_extern_crate( @@ -1130,11 +1137,8 @@ fn finish(mut self) -> DefMap { } for directive in &self.unresolved_imports { - if let ImportSource::Import(import) = &directive.import.source { - let item_tree = import.item_tree(self.db); - let import_data = &item_tree[import.value]; - - match (import_data.path.segments().first(), &import_data.path.kind) { + if let ImportSource::Import { id: import, use_tree } = &directive.import.source { + match (directive.import.path.segments().first(), &directive.import.path.kind) { (Some(krate), PathKind::Plain) | (Some(krate), PathKind::Abs) => { if diagnosed_extern_crates.contains(krate) { continue; @@ -1145,8 +1149,8 @@ fn finish(mut self) -> DefMap { self.def_map.diagnostics.push(DefDiagnostic::unresolved_import( directive.module_id, - InFile::new(import.file_id(), import_data.ast_id), - import_data.index, + *import, + *use_tree, )); } } @@ -1222,16 +1226,20 @@ fn collect(&mut self, items: &[ModItem]) { match item { ModItem::Mod(m) => self.collect_module(&self.item_tree[m], &attrs), ModItem::Import(import_id) => { - self.def_collector.unresolved_imports.push(ImportDirective { - module_id: self.module_id, - import: Import::from_use( - self.def_collector.db, - krate, - &self.item_tree, - ItemTreeId::new(self.file_id, import_id), - ), - status: PartialResolvedImport::Unresolved, - }) + let module_id = self.module_id; + let imports = Import::from_use( + self.def_collector.db, + krate, + &self.item_tree, + ItemTreeId::new(self.file_id, import_id), + ); + self.def_collector.unresolved_imports.extend(imports.into_iter().map( + |import| ImportDirective { + module_id, + import, + status: PartialResolvedImport::Unresolved, + }, + )); } ModItem::ExternCrate(import_id) => { self.def_collector.unresolved_imports.push(ImportDirective { diff --git a/crates/hir_def/src/nameres/diagnostics.rs b/crates/hir_def/src/nameres/diagnostics.rs index 8f2f0ff9f57..57c36c3c6cb 100644 --- a/crates/hir_def/src/nameres/diagnostics.rs +++ b/crates/hir_def/src/nameres/diagnostics.rs @@ -2,9 +2,15 @@ use cfg::{CfgExpr, CfgOptions}; use hir_expand::MacroCallKind; +use la_arena::Idx; use syntax::ast; -use crate::{nameres::LocalModuleId, path::ModPath, AstId}; +use crate::{ + item_tree::{self, ItemTreeId}, + nameres::LocalModuleId, + path::ModPath, + AstId, +}; #[derive(Debug, PartialEq, Eq)] pub enum DefDiagnosticKind { @@ -12,7 +18,7 @@ pub enum DefDiagnosticKind { UnresolvedExternCrate { ast: AstId }, - UnresolvedImport { ast: AstId, index: usize }, + UnresolvedImport { id: ItemTreeId, index: Idx }, UnconfiguredCode { ast: AstId, cfg: CfgExpr, opts: CfgOptions }, @@ -53,10 +59,10 @@ pub(super) fn unresolved_extern_crate( pub(super) fn unresolved_import( container: LocalModuleId, - ast: AstId, - index: usize, + id: ItemTreeId, + index: Idx, ) -> Self { - Self { in_module: container, kind: DefDiagnosticKind::UnresolvedImport { ast, index } } + Self { in_module: container, kind: DefDiagnosticKind::UnresolvedImport { id, index } } } pub(super) fn unconfigured_code( diff --git a/crates/hir_def/src/path.rs b/crates/hir_def/src/path.rs index d9ec03d2dae..16440041dcb 100644 --- a/crates/hir_def/src/path.rs +++ b/crates/hir_def/src/path.rs @@ -14,10 +14,7 @@ }; use syntax::ast; -use crate::{ - type_ref::{TypeBound, TypeRef}, - InFile, -}; +use crate::type_ref::{TypeBound, TypeRef}; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct ModPath { @@ -56,8 +53,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { impl ModPath { pub fn from_src(db: &dyn DefDatabase, path: ast::Path, hygiene: &Hygiene) -> Option { - let ctx = LowerCtx::with_hygiene(db, hygiene); - lower::lower_path(path, &ctx).map(|it| (*it.mod_path).clone()) + lower::convert_path(db, None, path, hygiene) } pub fn from_segments(kind: PathKind, segments: impl IntoIterator) -> ModPath { @@ -70,18 +66,6 @@ pub const fn from_kind(kind: PathKind) -> ModPath { ModPath { kind, segments: Vec::new() } } - /// Calls `cb` with all paths, represented by this use item. - pub fn expand_use_item( - db: &dyn DefDatabase, - item_src: InFile, - hygiene: &Hygiene, - mut cb: impl FnMut(ModPath, &ast::UseTree, /* is_glob */ bool, Option), - ) { - if let Some(tree) = item_src.value.use_tree() { - lower::lower_use_tree(db, None, tree, hygiene, &mut cb); - } - } - pub fn segments(&self) -> &[Name] { &self.segments } diff --git a/crates/hir_def/src/path/lower.rs b/crates/hir_def/src/path/lower.rs index 54ede739394..f6220aa9280 100644 --- a/crates/hir_def/src/path/lower.rs +++ b/crates/hir_def/src/path/lower.rs @@ -15,7 +15,7 @@ type_ref::{LifetimeRef, TypeBound, TypeRef}, }; -pub(super) use lower_use::lower_use_tree; +pub(super) use lower_use::convert_path; /// Converts an `ast::Path` to `Path`. Works with use trees. /// It correctly handles `$crate` based path from macro call. diff --git a/crates/hir_def/src/path/lower/lower_use.rs b/crates/hir_def/src/path/lower/lower_use.rs index ee80e3df3ca..0ee406f63a6 100644 --- a/crates/hir_def/src/path/lower/lower_use.rs +++ b/crates/hir_def/src/path/lower/lower_use.rs @@ -4,68 +4,15 @@ use std::iter; use either::Either; -use hir_expand::{hygiene::Hygiene, name::AsName}; -use syntax::ast::{self, NameOwner}; +use hir_expand::hygiene::Hygiene; +use syntax::{ast, AstNode}; use crate::{ db::DefDatabase, - path::{ImportAlias, ModPath, PathKind}, + path::{ModPath, PathKind}, }; -pub(crate) fn lower_use_tree( - db: &dyn DefDatabase, - prefix: Option, - tree: ast::UseTree, - hygiene: &Hygiene, - cb: &mut dyn FnMut(ModPath, &ast::UseTree, bool, Option), -) { - if let Some(use_tree_list) = tree.use_tree_list() { - let prefix = match tree.path() { - // E.g. use something::{{{inner}}}; - None => prefix, - // E.g. `use something::{inner}` (prefix is `None`, path is `something`) - // or `use something::{path::{inner::{innerer}}}` (prefix is `something::path`, path is `inner`) - Some(path) => match convert_path(db, prefix, path, hygiene) { - Some(it) => Some(it), - None => return, // FIXME: report errors somewhere - }, - }; - for child_tree in use_tree_list.use_trees() { - lower_use_tree(db, prefix.clone(), child_tree, hygiene, cb); - } - } else { - let alias = tree.rename().map(|a| { - a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias) - }); - let is_glob = tree.star_token().is_some(); - if let Some(ast_path) = tree.path() { - // Handle self in a path. - // E.g. `use something::{self, <...>}` - if ast_path.qualifier().is_none() { - if let Some(segment) = ast_path.segment() { - if segment.kind() == Some(ast::PathSegmentKind::SelfKw) { - if let Some(prefix) = prefix { - cb(prefix, &tree, false, alias); - return; - } - } - } - } - if let Some(path) = convert_path(db, prefix, ast_path, hygiene) { - cb(path, &tree, is_glob, alias) - } - // FIXME: report errors somewhere - // We get here if we do - } else if is_glob { - cov_mark::hit!(glob_enum_group); - if let Some(prefix) = prefix { - cb(prefix, &tree, is_glob, None) - } - } - } -} - -fn convert_path( +pub(crate) fn convert_path( db: &dyn DefDatabase, prefix: Option, path: ast::Path, @@ -78,7 +25,7 @@ fn convert_path( }; let segment = path.segment()?; - let res = match segment.kind()? { + let mut mod_path = match segment.kind()? { ast::PathSegmentKind::Name(name_ref) => { match hygiene.name_ref_to_name(db.upcast(), name_ref) { Either::Left(name) => { @@ -125,5 +72,18 @@ fn convert_path( return None; } }; - Some(res) + + // handle local_inner_macros : + // Basically, even in rustc it is quite hacky: + // https://github.com/rust-lang/rust/blob/614f273e9388ddd7804d5cbc80b8865068a3744e/src/librustc_resolve/macros.rs#L456 + // We follow what it did anyway :) + if mod_path.segments.len() == 1 && mod_path.kind == PathKind::Plain { + if let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) { + if let Some(crate_id) = hygiene.local_inner_macros(db.upcast(), path) { + mod_path.kind = PathKind::DollarCrate(crate_id); + } + } + } + + Some(mod_path) } diff --git a/crates/hir_def/src/test_db.rs b/crates/hir_def/src/test_db.rs index 6c357c915b5..a9c1e13e2f1 100644 --- a/crates/hir_def/src/test_db.rs +++ b/crates/hir_def/src/test_db.rs @@ -278,9 +278,11 @@ pub(crate) fn diagnostics(&self, cb: &mut dyn FnMut(FileRange, String)) { let node = ast.to_node(self.upcast()); (InFile::new(ast.file_id, node.syntax().clone()), "UnresolvedExternCrate") } - DefDiagnosticKind::UnresolvedImport { ast, .. } => { - let node = ast.to_node(self.upcast()); - (InFile::new(ast.file_id, node.syntax().clone()), "UnresolvedImport") + DefDiagnosticKind::UnresolvedImport { id, .. } => { + let item_tree = id.item_tree(self.upcast()); + let import = &item_tree[id.value]; + let node = InFile::new(id.file_id(), import.ast_id).to_node(self.upcast()); + (InFile::new(id.file_id(), node.syntax().clone()), "UnresolvedImport") } DefDiagnosticKind::UnconfiguredCode { ast, .. } => { let node = ast.to_node(self.upcast()); diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index dcac7c76d20..6cf5810fa4d 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs @@ -311,6 +311,7 @@ mod tests { /// * a diagnostic is produced /// * the first diagnostic fix trigger range touches the input cursor position /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied + #[track_caller] pub(crate) fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) { check_nth_fix(0, ra_fixture_before, ra_fixture_after); } @@ -325,6 +326,7 @@ pub(crate) fn check_fixes(ra_fixture_before: &str, ra_fixtures_after: Vec<&str>) } } + #[track_caller] fn check_nth_fix(nth: usize, ra_fixture_before: &str, ra_fixture_after: &str) { let after = trim_indent(ra_fixture_after);