From b52df9187730abbcd9cbb132f7d184c74b9a3b7f Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Wed, 26 May 2021 01:01:58 +0200 Subject: Stop expanding UseTrees during ItemTree lowering --- crates/hir/src/lib.rs | 26 ++---- crates/hir_def/src/item_tree.rs | 130 ++++++++++++++++++++++++++--- crates/hir_def/src/item_tree/lower.rs | 100 +++++++++++++++++----- crates/hir_def/src/item_tree/pretty.rs | 43 ++++++++-- crates/hir_def/src/item_tree/tests.rs | 17 ++-- crates/hir_def/src/nameres/collector.rs | 66 ++++++++------- crates/hir_def/src/nameres/diagnostics.rs | 16 ++-- crates/hir_def/src/path.rs | 20 +---- crates/hir_def/src/path/lower.rs | 2 +- crates/hir_def/src/path/lower/lower_use.rs | 78 +++++------------ crates/hir_def/src/test_db.rs | 8 +- crates/ide/src/diagnostics.rs | 2 + 12 files changed, 320 insertions(+), 188 deletions(-) diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 1ecd2391b..01b2de515 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -472,27 +472,13 @@ impl Module { }); } - 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()); - } - - cur += 1; - }, - ); + DefDiagnosticKind::UnresolvedImport { id, index } => { + let file_id = id.file_id(); + let item_tree = id.item_tree(db.upcast()); + let import = &item_tree[id.value]; - 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 11767d100..508736885 100644 --- a/crates/hir_def/src/item_tree.rs +++ b/crates/hir_def/src/item_tree.rs @@ -523,21 +523,38 @@ impl Index> for ItemTree { } } -/// 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 b4389371f..a59a3dc37 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 @@ impl<'a> Ctx<'a> { 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 @@ impl<'a> Ctx<'a> { 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 9394a5de6..53631ab19 100644 --- a/crates/hir_def/src/item_tree/pretty.rs +++ b/crates/hir_def/src/item_tree/pretty.rs @@ -163,21 +163,46 @@ impl<'a> Printer<'a> { } } + 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 6407871b5..20773aa69 100644 --- a/crates/hir_def/src/item_tree/tests.rs +++ b/crates/hir_def/src/item_tree/tests.rs @@ -26,6 +26,8 @@ use globs::*; /// 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 @@ use crate::{A, B}; pub(super) extern crate bli; - pub use crate::path::nested; // 0 - - pub use crate::path::items as renamed; // 1 + pub use crate::path::{nested, items as renamed, Trait as _}; - 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 @@ mod outline; #[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 3ea472908..4296c6304 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs @@ -17,6 +17,7 @@ use hir_expand::{ }; use hir_expand::{InFile, MacroCallLoc}; use itertools::Itertools; +use la_arena::Idx; use rustc_hash::{FxHashMap, FxHashSet}; use syntax::ast; @@ -143,7 +144,7 @@ impl PartialResolvedImport { #[derive(Clone, Debug, Eq, PartialEq)] enum ImportSource { - Import(ItemTreeId), + Import { id: ItemTreeId, use_tree: Idx }, ExternCrate(ItemTreeId), } @@ -165,20 +166,26 @@ impl Import { 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 @@ impl DefCollector<'_> { } 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 @@ impl DefCollector<'_> { 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 @@ impl ModCollector<'_, '_> { 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 8f2f0ff9f..57c36c3c6 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 @@ impl DefDiagnostic { 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 d9ec03d2d..16440041d 100644 --- a/crates/hir_def/src/path.rs +++ b/crates/hir_def/src/path.rs @@ -14,10 +14,7 @@ use hir_expand::{ }; 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 @@ impl Display for ImportAlias { 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 @@ impl 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 54ede7393..f6220aa92 100644 --- a/crates/hir_def/src/path/lower.rs +++ b/crates/hir_def/src/path/lower.rs @@ -15,7 +15,7 @@ use crate::{ 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 ee80e3df3..0ee406f63 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 6c357c915..a9c1e13e2 100644 --- a/crates/hir_def/src/test_db.rs +++ b/crates/hir_def/src/test_db.rs @@ -278,9 +278,11 @@ impl TestDB { 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 dcac7c76d..6cf5810fa 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 @@ mod tests { } } + #[track_caller] fn check_nth_fix(nth: usize, ra_fixture_before: &str, ra_fixture_after: &str) { let after = trim_indent(ra_fixture_after); -- cgit v1.2.3 From 356dd3d909f20b5b1e1c44205d522b7b2ead0d0e Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Wed, 26 May 2021 01:09:31 +0200 Subject: Clean up ItemTree lowering now that it's 1:1 --- crates/hir_def/src/item_tree/lower.rs | 79 ++++++++++++----------------------- 1 file changed, 26 insertions(+), 53 deletions(-) diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs index a59a3dc37..798ab46dd 100644 --- a/crates/hir_def/src/item_tree/lower.rs +++ b/crates/hir_def/src/item_tree/lower.rs @@ -3,7 +3,6 @@ use std::{collections::hash_map::Entry, mem, sync::Arc}; use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, name::known, HirFileId}; -use smallvec::SmallVec; use syntax::{ ast::{self, ModuleItemOwner}, SyntaxNode, WalkEvent, @@ -20,17 +19,6 @@ fn id(index: Idx) -> FileItemTreeId { FileItemTreeId { index, _p: PhantomData } } -struct ModItems(SmallVec<[ModItem; 1]>); - -impl From for ModItems -where - T: Into, -{ - fn from(t: T) -> Self { - ModItems(SmallVec::from_buf([t.into(); 1])) - } -} - pub(super) struct Ctx<'a> { db: &'a dyn DefDatabase, tree: ItemTree, @@ -53,11 +41,8 @@ impl<'a> Ctx<'a> { } pub(super) fn lower_module_items(mut self, item_owner: &dyn ModuleItemOwner) -> ItemTree { - self.tree.top_level = item_owner - .items() - .flat_map(|item| self.lower_mod_item(&item, false)) - .flat_map(|items| items.0) - .collect(); + self.tree.top_level = + item_owner.items().flat_map(|item| self.lower_mod_item(&item, false)).collect(); self.tree } @@ -69,7 +54,6 @@ impl<'a> Ctx<'a> { _ => None, }) .flat_map(|item| self.lower_mod_item(&item, false)) - .flat_map(|items| items.0) .collect(); // Non-items need to have their inner items collected. @@ -96,7 +80,7 @@ impl<'a> Ctx<'a> { self.tree.data_mut() } - fn lower_mod_item(&mut self, item: &ast::Item, inner: bool) -> Option { + fn lower_mod_item(&mut self, item: &ast::Item, inner: bool) -> Option { // Collect inner items for 1-to-1-lowered items. match item { ast::Item::Struct(_) @@ -127,34 +111,28 @@ impl<'a> Ctx<'a> { }; let attrs = RawAttrs::new(self.db, item, &self.hygiene); - let items = match item { - ast::Item::Struct(ast) => self.lower_struct(ast).map(Into::into), - ast::Item::Union(ast) => self.lower_union(ast).map(Into::into), - ast::Item::Enum(ast) => self.lower_enum(ast).map(Into::into), - ast::Item::Fn(ast) => self.lower_function(ast).map(Into::into), - ast::Item::TypeAlias(ast) => self.lower_type_alias(ast).map(Into::into), - ast::Item::Static(ast) => self.lower_static(ast).map(Into::into), - ast::Item::Const(ast) => Some(self.lower_const(ast).into()), - ast::Item::Module(ast) => self.lower_module(ast).map(Into::into), - ast::Item::Trait(ast) => self.lower_trait(ast).map(Into::into), - ast::Item::Impl(ast) => self.lower_impl(ast).map(Into::into), - ast::Item::Use(ast) => Some(ModItems( - self.lower_use(ast).into_iter().map(Into::into).collect::>(), - )), - ast::Item::ExternCrate(ast) => self.lower_extern_crate(ast).map(Into::into), - ast::Item::MacroCall(ast) => self.lower_macro_call(ast).map(Into::into), - ast::Item::MacroRules(ast) => self.lower_macro_rules(ast).map(Into::into), - ast::Item::MacroDef(ast) => self.lower_macro_def(ast).map(Into::into), - ast::Item::ExternBlock(ast) => Some(self.lower_extern_block(ast).into()), + let item: ModItem = match item { + ast::Item::Struct(ast) => self.lower_struct(ast)?.into(), + ast::Item::Union(ast) => self.lower_union(ast)?.into(), + ast::Item::Enum(ast) => self.lower_enum(ast)?.into(), + ast::Item::Fn(ast) => self.lower_function(ast)?.into(), + ast::Item::TypeAlias(ast) => self.lower_type_alias(ast)?.into(), + ast::Item::Static(ast) => self.lower_static(ast)?.into(), + ast::Item::Const(ast) => self.lower_const(ast).into(), + ast::Item::Module(ast) => self.lower_module(ast)?.into(), + ast::Item::Trait(ast) => self.lower_trait(ast)?.into(), + ast::Item::Impl(ast) => self.lower_impl(ast)?.into(), + ast::Item::Use(ast) => self.lower_use(ast)?.into(), + ast::Item::ExternCrate(ast) => self.lower_extern_crate(ast)?.into(), + ast::Item::MacroCall(ast) => self.lower_macro_call(ast)?.into(), + ast::Item::MacroRules(ast) => self.lower_macro_rules(ast)?.into(), + ast::Item::MacroDef(ast) => self.lower_macro_def(ast)?.into(), + ast::Item::ExternBlock(ast) => self.lower_extern_block(ast).into(), }; - if !attrs.is_empty() { - for item in items.iter().flat_map(|items| &items.0) { - self.add_attrs((*item).into(), attrs.clone()); - } - } + self.add_attrs(item.into(), attrs.clone()); - items + Some(item) } fn add_attrs(&mut self, item: AttrOwner, attrs: RawAttrs) { @@ -188,12 +166,10 @@ impl<'a> Ctx<'a> { }, ast::Item(item) => { // FIXME: This triggers for macro calls in expression/pattern/type position - let mod_items = self.lower_mod_item(&item, true); + let mod_item = self.lower_mod_item(&item, true); let current_block = block_stack.last(); - if let (Some(mod_items), Some(block)) = (mod_items, current_block) { - if !mod_items.0.is_empty() { - self.data().inner_items.entry(*block).or_default().extend(mod_items.0.iter().copied()); - } + if let (Some(mod_item), Some(block)) = (mod_item, current_block) { + self.data().inner_items.entry(*block).or_default().push(mod_item); } }, _ => {} @@ -478,10 +454,7 @@ impl<'a> Ctx<'a> { items: module .item_list() .map(|list| { - list.items() - .flat_map(|item| self.lower_mod_item(&item, false)) - .flat_map(|items| items.0) - .collect() + list.items().flat_map(|item| self.lower_mod_item(&item, false)).collect() }) .unwrap_or_else(|| { cov_mark::hit!(name_res_works_for_broken_modules); -- cgit v1.2.3 From fe910c7bc4aac8a33fc1933d64aa260d42a3c4f1 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Wed, 26 May 2021 01:26:16 +0200 Subject: Reduce memory usage a bit --- crates/hir_def/src/item_tree.rs | 8 ++++---- crates/hir_def/src/item_tree/lower.rs | 15 ++++++++++----- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs index 508736885..c960f66d6 100644 --- a/crates/hir_def/src/item_tree.rs +++ b/crates/hir_def/src/item_tree.rs @@ -543,18 +543,18 @@ pub enum UseTreeKind { /// use path::to::Item as Renamed; /// use path::to::Trait as _; /// ``` - Single { path: ModPath, alias: Option }, + Single { path: Interned, alias: Option }, /// ```ignore /// use *; // (invalid, but can occur in nested tree) /// use path::*; /// ``` - Glob { path: Option }, + Glob { path: Option> }, /// ```ignore /// use prefix::{self, Item, ...}; /// ``` - Prefixed { prefix: Option, list: Vec }, + Prefixed { prefix: Option>, list: Box<[UseTree]> }, } #[derive(Debug, Clone, Eq, PartialEq)] @@ -811,7 +811,7 @@ impl UseTree { }, None => prefix, }; - for tree in list { + for tree in &**list { tree.expand_impl(prefix.clone(), cb); } } diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs index 798ab46dd..40f3428b7 100644 --- a/crates/hir_def/src/item_tree/lower.rs +++ b/crates/hir_def/src/item_tree/lower.rs @@ -864,7 +864,12 @@ impl UseTreeLowering<'_> { 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)) + Some( + self.use_tree( + UseTreeKind::Prefixed { prefix: prefix.map(Interned::new), list }, + tree, + ), + ) } else { let is_glob = tree.star_token().is_some(); let path = match tree.path() { @@ -883,15 +888,15 @@ impl UseTreeLowering<'_> { if path.is_none() { cov_mark::hit!(glob_enum_group); } - Some(self.use_tree(UseTreeKind::Glob { path }, tree)) + Some(self.use_tree(UseTreeKind::Glob { path: path.map(Interned::new) }, 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)) - } + (Some(path), alias, false) => Some( + self.use_tree(UseTreeKind::Single { path: Interned::new(path), alias }, tree), + ), } } } -- cgit v1.2.3 From 196cb65ead398f81340de431400103224d7de660 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Thu, 27 May 2021 13:55:31 +0200 Subject: Drop `ignore` from doctests --- crates/hir_def/src/item_tree.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs index c960f66d6..f84c4cf2b 100644 --- a/crates/hir_def/src/item_tree.rs +++ b/crates/hir_def/src/item_tree.rs @@ -538,20 +538,20 @@ pub struct UseTree { #[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: Interned, 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: Box<[UseTree]> }, -- cgit v1.2.3