Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-def/src/nameres/collector.rs')
-rw-r--r--crates/hir-def/src/nameres/collector.rs609
1 files changed, 339 insertions, 270 deletions
diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs
index ddcee77ec4..06542b4b1e 100644
--- a/crates/hir-def/src/nameres/collector.rs
+++ b/crates/hir-def/src/nameres/collector.rs
@@ -5,7 +5,7 @@
use std::{iter, mem};
-use base_db::{CrateId, Edition, FileId};
+use base_db::{CrateId, Dependency, Edition, FileId};
use cfg::{CfgExpr, CfgOptions};
use either::Either;
use hir_expand::{
@@ -14,10 +14,11 @@ use hir_expand::{
builtin_attr_macro::find_builtin_attr,
builtin_derive_macro::find_builtin_derive,
builtin_fn_macro::find_builtin_macro,
+ hygiene::Hygiene,
name::{name, AsName, Name},
proc_macro::ProcMacroExpander,
- ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId,
- MacroDefKind,
+ ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroCallLoc,
+ MacroDefId, MacroDefKind,
};
use itertools::{izip, Itertools};
use la_arena::Idx;
@@ -25,6 +26,7 @@ use limit::Limit;
use rustc_hash::{FxHashMap, FxHashSet};
use stdx::always;
use syntax::{ast, SmolStr};
+use triomphe::Arc;
use crate::{
attr::Attrs,
@@ -33,8 +35,8 @@ use crate::{
derive_macro_as_call_id,
item_scope::{ImportType, PerNsGlobImports},
item_tree::{
- self, Fields, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, ItemTreeNode, MacroCall,
- MacroDef, MacroRules, Mod, ModItem, ModKind, TreeId,
+ self, ExternCrate, Fields, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, ItemTreeNode,
+ MacroCall, MacroDef, MacroRules, Mod, ModItem, ModKind, TreeId,
},
macro_call_as_call_id, macro_id_to_def_id,
nameres::{
@@ -42,7 +44,8 @@ use crate::{
mod_resolution::ModDir,
path_resolution::ReachedFixedPoint,
proc_macro::{parse_macro_name_and_helper_attrs, ProcMacroDef, ProcMacroKind},
- BuiltinShadowMode, DefMap, ModuleData, ModuleOrigin, ResolveMode,
+ sub_namespace_match, BuiltinShadowMode, DefMap, MacroSubNs, ModuleData, ModuleOrigin,
+ ResolveMode,
},
path::{ImportAlias, ModPath, PathKind},
per_ns::PerNs,
@@ -59,7 +62,7 @@ static GLOB_RECURSION_LIMIT: Limit = Limit::new(100);
static EXPANSION_DEPTH_LIMIT: Limit = Limit::new(128);
static FIXED_POINT_LIMIT: Limit = Limit::new(8192);
-pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap, tree_id: TreeId) -> DefMap {
+pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeId) -> DefMap {
let crate_graph = db.crate_graph();
let mut deps = FxHashMap::default();
@@ -67,36 +70,33 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap, tree_id: T
let krate = &crate_graph[def_map.krate];
for dep in &krate.dependencies {
tracing::debug!("crate dep {:?} -> {:?}", dep.name, dep.crate_id);
- let dep_def_map = db.crate_def_map(dep.crate_id);
- let dep_root = dep_def_map.module_id(dep_def_map.root);
- deps.insert(dep.as_name(), dep_root);
-
- if dep.is_prelude() && !tree_id.is_block() {
- def_map.extern_prelude.insert(dep.as_name(), dep_root);
- }
+ deps.insert(dep.as_name(), dep.clone());
}
let cfg_options = &krate.cfg_options;
- let proc_macros = match &krate.proc_macro {
- Ok(proc_macros) => {
- proc_macros
- .iter()
- .enumerate()
- .map(|(idx, it)| {
- // FIXME: a hacky way to create a Name from string.
- let name =
- tt::Ident { text: it.name.clone(), span: tt::TokenId::unspecified() };
- (name.as_name(), ProcMacroExpander::new(base_db::ProcMacroId(idx as u32)))
- })
- .collect()
- }
- Err(e) => {
- def_map.proc_macro_loading_error = Some(e.clone().into_boxed_str());
- Vec::new()
+
+ let is_proc_macro = krate.is_proc_macro;
+ let proc_macros = if is_proc_macro {
+ match db.proc_macros().get(&def_map.krate) {
+ Some(Ok(proc_macros)) => {
+ Ok(proc_macros
+ .iter()
+ .enumerate()
+ .map(|(idx, it)| {
+ // FIXME: a hacky way to create a Name from string.
+ let name =
+ tt::Ident { text: it.name.clone(), span: tt::TokenId::unspecified() };
+ (name.as_name(), ProcMacroExpander::new(base_db::ProcMacroId(idx as u32)))
+ })
+ .collect())
+ }
+ Some(Err(e)) => Err(e.clone().into_boxed_str()),
+ None => Err("No proc-macros present for crate".to_owned().into_boxed_str()),
}
+ } else {
+ Ok(vec![])
};
- let is_proc_macro = krate.is_proc_macro;
let mut collector = DefCollector {
db,
@@ -112,6 +112,7 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap, tree_id: T
from_glob_import: Default::default(),
skip_attrs: Default::default(),
is_proc_macro,
+ hygienes: FxHashMap::default(),
};
if tree_id.is_block() {
collector.seed_with_inner(tree_id);
@@ -238,7 +239,7 @@ enum MacroDirectiveKind {
struct DefCollector<'a> {
db: &'a dyn DefDatabase,
def_map: DefMap,
- deps: FxHashMap<Name, ModuleId>,
+ deps: FxHashMap<Name, Dependency>,
glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility)>>,
unresolved_imports: Vec<ImportDirective>,
indeterminate_imports: Vec<ImportDirective>,
@@ -249,7 +250,7 @@ struct DefCollector<'a> {
/// built by the build system, and is the list of proc. macros we can actually expand. It is
/// empty when proc. macro support is disabled (in which case we still do name resolution for
/// them).
- proc_macros: Vec<(Name, ProcMacroExpander)>,
+ proc_macros: Result<Vec<(Name, ProcMacroExpander)>, Box<str>>,
is_proc_macro: bool,
from_glob_import: PerNsGlobImports,
/// If we fail to resolve an attribute on a `ModItem`, we fall back to ignoring the attribute.
@@ -259,6 +260,12 @@ struct DefCollector<'a> {
/// This also stores the attributes to skip when we resolve derive helpers and non-macro
/// non-builtin attributes in general.
skip_attrs: FxHashMap<InFile<ModItem>, AttrId>,
+ /// `Hygiene` cache, because `Hygiene` construction is expensive.
+ ///
+ /// Almost all paths should have been lowered to `ModPath` during `ItemTree` construction.
+ /// However, `DefCollector` still needs to lower paths in attributes, in particular those in
+ /// derive meta item list.
+ hygienes: FxHashMap<HirFileId, Hygiene>,
}
impl DefCollector<'_> {
@@ -267,86 +274,117 @@ impl DefCollector<'_> {
let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id;
let item_tree = self.db.file_item_tree(file_id.into());
- let module_id = self.def_map.root;
+ let module_id = DefMap::ROOT;
let attrs = item_tree.top_level_attrs(self.db, self.def_map.krate);
- if attrs.cfg().map_or(true, |cfg| self.cfg_options.check(&cfg) != Some(false)) {
- self.inject_prelude(&attrs);
-
- // Process other crate-level attributes.
- for attr in &*attrs {
- let attr_name = match attr.path.as_ident() {
- Some(name) => name,
- None => continue,
- };
+ let crate_data = Arc::get_mut(&mut self.def_map.data).unwrap();
- if *attr_name == hir_expand::name![recursion_limit] {
- if let Some(limit) = attr.string_value() {
- if let Ok(limit) = limit.parse() {
- self.def_map.recursion_limit = Some(limit);
- }
- }
- continue;
+ if let Err(e) = &self.proc_macros {
+ crate_data.proc_macro_loading_error = Some(e.clone());
+ }
+
+ for (name, dep) in &self.deps {
+ if dep.is_prelude() {
+ crate_data.extern_prelude.insert(
+ name.clone(),
+ ModuleId { krate: dep.crate_id, block: None, local_id: DefMap::ROOT },
+ );
+ }
+ }
+
+ // Process other crate-level attributes.
+ for attr in &*attrs {
+ if let Some(cfg) = attr.cfg() {
+ if self.cfg_options.check(&cfg) == Some(false) {
+ return;
}
+ }
+ let attr_name = match attr.path.as_ident() {
+ Some(name) => name,
+ None => continue,
+ };
- if *attr_name == hir_expand::name![crate_type] {
- if let Some("proc-macro") = attr.string_value().map(SmolStr::as_str) {
- self.is_proc_macro = true;
+ if *attr_name == hir_expand::name![recursion_limit] {
+ if let Some(limit) = attr.string_value() {
+ if let Ok(limit) = limit.parse() {
+ crate_data.recursion_limit = Some(limit);
}
- continue;
}
+ continue;
+ }
- if attr_name.as_text().as_deref() == Some("rustc_coherence_is_core") {
- self.def_map.rustc_coherence_is_core = true;
- continue;
+ if *attr_name == hir_expand::name![crate_type] {
+ if let Some("proc-macro") = attr.string_value().map(SmolStr::as_str) {
+ self.is_proc_macro = true;
}
+ continue;
+ }
- if *attr_name == hir_expand::name![feature] {
- let features =
- attr.parse_path_comma_token_tree().into_iter().flatten().filter_map(
- |feat| match feat.segments() {
- [name] => Some(name.to_smol_str()),
- _ => None,
- },
- );
- self.def_map.unstable_features.extend(features);
- }
+ if *attr_name == hir_expand::name![no_core] {
+ crate_data.no_core = true;
+ continue;
+ }
- let attr_is_register_like = *attr_name == hir_expand::name![register_attr]
- || *attr_name == hir_expand::name![register_tool];
- if !attr_is_register_like {
- continue;
- }
+ if *attr_name == hir_expand::name![no_std] {
+ crate_data.no_std = true;
+ continue;
+ }
- let registered_name = match attr.single_ident_value() {
- Some(ident) => ident.as_name(),
- _ => continue,
- };
+ if attr_name.as_text().as_deref() == Some("rustc_coherence_is_core") {
+ crate_data.rustc_coherence_is_core = true;
+ continue;
+ }
- if *attr_name == hir_expand::name![register_attr] {
- self.def_map.registered_attrs.push(registered_name.to_smol_str());
- cov_mark::hit!(register_attr);
- } else {
- self.def_map.registered_tools.push(registered_name.to_smol_str());
- cov_mark::hit!(register_tool);
- }
+ if *attr_name == hir_expand::name![feature] {
+ let hygiene = &Hygiene::new_unhygienic();
+ let features = attr
+ .parse_path_comma_token_tree(self.db.upcast(), hygiene)
+ .into_iter()
+ .flatten()
+ .filter_map(|feat| match feat.segments() {
+ [name] => Some(name.to_smol_str()),
+ _ => None,
+ });
+ crate_data.unstable_features.extend(features);
}
- ModCollector {
- def_collector: self,
- macro_depth: 0,
- module_id,
- tree_id: TreeId::new(file_id.into(), None),
- item_tree: &item_tree,
- mod_dir: ModDir::root(),
+ let attr_is_register_like = *attr_name == hir_expand::name![register_attr]
+ || *attr_name == hir_expand::name![register_tool];
+ if !attr_is_register_like {
+ continue;
+ }
+
+ let registered_name = match attr.single_ident_value() {
+ Some(ident) => ident.as_name(),
+ _ => continue,
+ };
+
+ if *attr_name == hir_expand::name![register_attr] {
+ crate_data.registered_attrs.push(registered_name.to_smol_str());
+ cov_mark::hit!(register_attr);
+ } else {
+ crate_data.registered_tools.push(registered_name.to_smol_str());
+ cov_mark::hit!(register_tool);
}
- .collect_in_top_module(item_tree.top_level_items());
}
+
+ crate_data.shrink_to_fit();
+ self.inject_prelude();
+
+ ModCollector {
+ def_collector: self,
+ macro_depth: 0,
+ module_id,
+ tree_id: TreeId::new(file_id.into(), None),
+ item_tree: &item_tree,
+ mod_dir: ModDir::root(),
+ }
+ .collect_in_top_module(item_tree.top_level_items());
}
fn seed_with_inner(&mut self, tree_id: TreeId) {
let item_tree = tree_id.item_tree(self.db);
- let module_id = self.def_map.root;
+ let module_id = DefMap::ROOT;
let is_cfg_enabled = item_tree
.top_level_attrs(self.db, self.def_map.krate)
@@ -428,7 +466,7 @@ impl DefCollector<'_> {
// Additionally, while the proc macro entry points must be `pub`, they are not publicly
// exported in type/value namespace. This function reduces the visibility of all items
// in the crate root that aren't proc macros.
- let root = self.def_map.root;
+ let root = DefMap::ROOT;
let module_id = self.def_map.module_id(root);
let root = &mut self.def_map.modules[root];
root.scope.censor_non_proc_macros(module_id);
@@ -456,12 +494,8 @@ impl DefCollector<'_> {
directive.module_id,
MacroCallKind::Attr {
ast_id: ast_id.ast_id,
- attr_args: std::sync::Arc::new((
- tt::Subtree::empty(),
- Default::default(),
- )),
+ attr_args: Arc::new((tt::Subtree::empty(), Default::default())),
invoc_attr_index: attr.id,
- is_derive: false,
},
attr.path().clone(),
));
@@ -495,15 +529,15 @@ impl DefCollector<'_> {
}
}
- fn inject_prelude(&mut self, crate_attrs: &Attrs) {
+ fn inject_prelude(&mut self) {
// See compiler/rustc_builtin_macros/src/standard_library_imports.rs
- if crate_attrs.by_key("no_core").exists() {
+ if self.def_map.data.no_core {
// libcore does not get a prelude.
return;
}
- let krate = if crate_attrs.by_key("no_std").exists() {
+ let krate = if self.def_map.data.no_std {
name![core]
} else {
let std = name![std];
@@ -516,43 +550,31 @@ impl DefCollector<'_> {
}
};
- let edition = match self.def_map.edition {
+ let edition = match self.def_map.data.edition {
Edition::Edition2015 => name![rust_2015],
Edition::Edition2018 => name![rust_2018],
Edition::Edition2021 => name![rust_2021],
};
- let path_kind = match self.def_map.edition {
+ let path_kind = match self.def_map.data.edition {
Edition::Edition2015 => PathKind::Plain,
_ => PathKind::Abs,
};
- let path =
- ModPath::from_segments(path_kind, [krate.clone(), name![prelude], edition].into_iter());
- // Fall back to the older `std::prelude::v1` for compatibility with Rust <1.52.0
- // FIXME remove this fallback
- let fallback_path =
- ModPath::from_segments(path_kind, [krate, name![prelude], name![v1]].into_iter());
-
- for path in &[path, fallback_path] {
- let (per_ns, _) = self.def_map.resolve_path(
- self.db,
- self.def_map.root,
- path,
- BuiltinShadowMode::Other,
- );
+ let path = ModPath::from_segments(path_kind, [krate, name![prelude], edition]);
- match per_ns.types {
- Some((ModuleDefId::ModuleId(m), _)) => {
- self.def_map.prelude = Some(m);
- break;
- }
- types => {
- tracing::debug!(
- "could not resolve prelude path `{}` to module (resolved to {:?})",
- path,
- types
- );
- }
+ let (per_ns, _) =
+ self.def_map.resolve_path(self.db, DefMap::ROOT, &path, BuiltinShadowMode::Other, None);
+
+ match per_ns.types {
+ Some((ModuleDefId::ModuleId(m), _)) => {
+ self.def_map.prelude = Some(m);
+ }
+ types => {
+ tracing::debug!(
+ "could not resolve prelude path `{}` to module (resolved to {:?})",
+ path.display(self.db.upcast()),
+ types
+ );
}
}
}
@@ -578,23 +600,29 @@ impl DefCollector<'_> {
def: ProcMacroDef,
id: ItemTreeId<item_tree::Function>,
fn_id: FunctionId,
- module_id: ModuleId,
) {
+ if self.def_map.block.is_some() {
+ return;
+ }
+ let crate_root = self.def_map.module_id(DefMap::ROOT);
+
let kind = def.kind.to_basedb_kind();
- let (expander, kind) = match self.proc_macros.iter().find(|(n, _)| n == &def.name) {
- Some(&(_, expander)) => (expander, kind),
- None => (ProcMacroExpander::dummy(), kind),
- };
+ let (expander, kind) =
+ match self.proc_macros.as_ref().map(|it| it.iter().find(|(n, _)| n == &def.name)) {
+ Ok(Some(&(_, expander))) => (expander, kind),
+ _ => (ProcMacroExpander::dummy(), kind),
+ };
let proc_macro_id =
- ProcMacroLoc { container: module_id, id, expander, kind }.intern(self.db);
+ ProcMacroLoc { container: crate_root, id, expander, kind }.intern(self.db);
self.define_proc_macro(def.name.clone(), proc_macro_id);
+ let crate_data = Arc::get_mut(&mut self.def_map.data).unwrap();
if let ProcMacroKind::CustomDerive { helpers } = def.kind {
- self.def_map
+ crate_data
.exported_derives
.insert(macro_id_to_def_id(self.db, proc_macro_id.into()), helpers);
}
- self.def_map.fn_proc_macro_mapping.insert(fn_id, proc_macro_id);
+ crate_data.fn_proc_macro_mapping.insert(fn_id, proc_macro_id);
}
/// Define a macro with `macro_rules`.
@@ -636,7 +664,7 @@ impl DefCollector<'_> {
// In Rust, `#[macro_export]` macros are unconditionally visible at the
// crate root, even if the parent modules is **not** visible.
if export {
- let module_id = self.def_map.root;
+ let module_id = DefMap::ROOT;
self.def_map.modules[module_id].scope.declare(macro_.into());
self.update(
module_id,
@@ -687,7 +715,7 @@ impl DefCollector<'_> {
/// A proc macro is similar to normal macro scope, but it would not visible in legacy textual scoped.
/// And unconditionally exported.
fn define_proc_macro(&mut self, name: Name, macro_: ProcMacroId) {
- let module_id = self.def_map.root;
+ let module_id = DefMap::ROOT;
self.def_map.modules[module_id].scope.declare(macro_.into());
self.update(
module_id,
@@ -697,39 +725,28 @@ impl DefCollector<'_> {
);
}
- /// Import macros from `#[macro_use] extern crate`.
- fn import_macros_from_extern_crate(
- &mut self,
- current_module_id: LocalModuleId,
- extern_crate: &item_tree::ExternCrate,
- ) {
- tracing::debug!(
- "importing macros from extern crate: {:?} ({:?})",
- extern_crate,
- self.def_map.edition,
- );
-
- if let Some(m) = self.resolve_extern_crate(&extern_crate.name) {
- if m == self.def_map.module_id(current_module_id) {
- cov_mark::hit!(ignore_macro_use_extern_crate_self);
- return;
- }
-
- cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use);
- self.import_all_macros_exported(current_module_id, m.krate);
- }
- }
-
- /// Import all exported macros from another crate
+ /// Import exported macros from another crate. `names`, if `Some(_)`, specifies the name of
+ /// macros to be imported. Otherwise this method imports all exported macros.
///
/// Exported macros are just all macros in the root module scope.
/// Note that it contains not only all `#[macro_export]` macros, but also all aliases
/// created by `use` in the root module, ignoring the visibility of `use`.
- fn import_all_macros_exported(&mut self, current_module_id: LocalModuleId, krate: CrateId) {
+ fn import_macros_from_extern_crate(&mut self, krate: CrateId, names: Option<Vec<Name>>) {
let def_map = self.db.crate_def_map(krate);
- for (name, def) in def_map[def_map.root].scope.macros() {
- // `#[macro_use]` brings macros into legacy scope. Yes, even non-`macro_rules!` macros.
- self.define_legacy_macro(current_module_id, name.clone(), def);
+ // `#[macro_use]` brings macros into macro_use prelude. Yes, even non-`macro_rules!`
+ // macros.
+ let root_scope = &def_map[DefMap::ROOT].scope;
+ if let Some(names) = names {
+ for name in names {
+ // FIXME: Report diagnostic on 404.
+ if let Some(def) = root_scope.get(&name).take_macros() {
+ self.def_map.macro_use_prelude.insert(name, def);
+ }
+ }
+ } else {
+ for (name, def) in root_scope.macros() {
+ self.def_map.macro_use_prelude.insert(name.clone(), def);
+ }
}
}
@@ -762,8 +779,9 @@ impl DefCollector<'_> {
}
fn resolve_import(&self, module_id: LocalModuleId, import: &Import) -> PartialResolvedImport {
- let _p = profile::span("resolve_import").detail(|| format!("{}", import.path));
- tracing::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition);
+ let _p = profile::span("resolve_import")
+ .detail(|| format!("{}", import.path.display(self.db.upcast())));
+ tracing::debug!("resolving import: {:?} ({:?})", import, self.def_map.data.edition);
if import.is_extern_crate {
let name = import
.path
@@ -785,6 +803,7 @@ impl DefCollector<'_> {
module_id,
&import.path,
BuiltinShadowMode::Module,
+ None, // An import may resolve to any kind of macro.
);
let def = res.resolved_def;
@@ -815,16 +834,13 @@ impl DefCollector<'_> {
fn resolve_extern_crate(&self, name: &Name) -> Option<ModuleId> {
if *name == name!(self) {
cov_mark::hit!(extern_crate_self_as);
- let root = match self.def_map.block {
- Some(_) => {
- let def_map = self.def_map.crate_root(self.db).def_map(self.db);
- def_map.module_id(def_map.root())
- }
- None => self.def_map.module_id(self.def_map.root()),
- };
- Some(root)
+ Some(self.def_map.crate_root())
} else {
- self.deps.get(name).copied()
+ self.deps.get(name).map(|dep| ModuleId {
+ krate: dep.crate_id,
+ block: None,
+ local_id: DefMap::ROOT,
+ })
}
}
@@ -863,11 +879,14 @@ impl DefCollector<'_> {
// extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658
if import.is_extern_crate
&& self.def_map.block.is_none()
- && module_id == self.def_map.root
+ && module_id == DefMap::ROOT
{
if let (Some(ModuleDefId::ModuleId(def)), Some(name)) = (def.take_types(), name)
{
- self.def_map.extern_prelude.insert(name.clone(), def);
+ Arc::get_mut(&mut self.def_map.data)
+ .unwrap()
+ .extern_prelude
+ .insert(name.clone(), def);
}
}
@@ -1082,7 +1101,14 @@ impl DefCollector<'_> {
resolved.push((directive.module_id, directive.depth, directive.container, call_id));
};
let mut res = ReachedFixedPoint::Yes;
+ // Retain unresolved macros after this round of resolution.
macros.retain(|directive| {
+ let subns = match &directive.kind {
+ MacroDirectiveKind::FnLike { .. } => MacroSubNs::Bang,
+ MacroDirectiveKind::Attr { .. } | MacroDirectiveKind::Derive { .. } => {
+ MacroSubNs::Attr
+ }
+ };
let resolver = |path| {
let resolved_res = self.def_map.resolve_path_fp_with_macro(
self.db,
@@ -1090,6 +1116,7 @@ impl DefCollector<'_> {
directive.module_id,
&path,
BuiltinShadowMode::Module,
+ Some(subns),
);
resolved_res
.resolved_def
@@ -1101,15 +1128,15 @@ impl DefCollector<'_> {
match &directive.kind {
MacroDirectiveKind::FnLike { ast_id, expand_to } => {
let call_id = macro_call_as_call_id(
- self.db,
+ self.db.upcast(),
ast_id,
*expand_to,
self.def_map.krate,
resolver_def_id,
- &mut |_err| (),
);
- if let Ok(Ok(call_id)) = call_id {
+ if let Ok(Some(call_id)) = call_id {
push_resolved(directive, call_id);
+
res = ReachedFixedPoint::No;
return false;
}
@@ -1134,7 +1161,7 @@ impl DefCollector<'_> {
// Record its helper attributes.
if def_id.krate != self.def_map.krate {
let def_map = self.db.crate_def_map(def_id.krate);
- if let Some(helpers) = def_map.exported_derives.get(&def_id) {
+ if let Some(helpers) = def_map.data.exported_derives.get(&def_id) {
self.def_map
.derive_helpers_in_scope
.entry(ast_id.ast_id.map(|it| it.upcast()))
@@ -1214,7 +1241,19 @@ impl DefCollector<'_> {
};
let ast_id = ast_id.with_value(ast_adt_id);
- match attr.parse_path_comma_token_tree() {
+ let extend_unhygenic;
+ let hygiene = if file_id.is_macro() {
+ self.hygienes
+ .entry(file_id)
+ .or_insert_with(|| Hygiene::new(self.db.upcast(), file_id))
+ } else {
+ // Avoid heap allocation (`Hygiene` embraces `Arc`) and hash map entry
+ // when we're in an oridinary (non-macro) file.
+ extend_unhygenic = Hygiene::new_unhygienic();
+ &extend_unhygenic
+ };
+
+ match attr.parse_path_comma_token_tree(self.db.upcast(), hygiene) {
Some(derive_macros) => {
let mut len = 0;
for (idx, path) in derive_macros.enumerate() {
@@ -1241,7 +1280,6 @@ impl DefCollector<'_> {
attr,
self.def_map.krate,
def,
- true,
);
self.def_map.modules[directive.module_id]
.scope
@@ -1261,18 +1299,12 @@ impl DefCollector<'_> {
}
// Not resolved to a derive helper or the derive attribute, so try to treat as a normal attribute.
- let call_id = attr_macro_as_call_id(
- self.db,
- file_ast_id,
- attr,
- self.def_map.krate,
- def,
- false,
- );
+ let call_id =
+ attr_macro_as_call_id(self.db, file_ast_id, attr, self.def_map.krate, def);
let loc: MacroCallLoc = self.db.lookup_intern_macro_call(call_id);
// If proc attribute macro expansion is disabled, skip expanding it here
- if !self.db.enable_proc_attr_macros() {
+ if !self.db.expand_proc_attr_macros() {
self.def_map.diagnostics.push(DefDiagnostic::unresolved_proc_macro(
directive.module_id,
loc.kind,
@@ -1345,25 +1377,31 @@ impl DefCollector<'_> {
let file_id = macro_call_id.as_file();
// First, fetch the raw expansion result for purposes of error reporting. This goes through
- // `macro_expand_error` to avoid depending on the full expansion result (to improve
+ // `parse_macro_expansion_error` to avoid depending on the full expansion result (to improve
// incrementality).
- let loc: MacroCallLoc = self.db.lookup_intern_macro_call(macro_call_id);
- let err = self.db.macro_expand_error(macro_call_id);
+ let ExpandResult { value, err } = self.db.parse_macro_expansion_error(macro_call_id);
if let Some(err) = err {
+ let loc: MacroCallLoc = self.db.lookup_intern_macro_call(macro_call_id);
let diag = match err {
+ // why is this reported here?
hir_expand::ExpandError::UnresolvedProcMacro(krate) => {
always!(krate == loc.def.krate);
- // Missing proc macros are non-fatal, so they are handled specially.
DefDiagnostic::unresolved_proc_macro(module_id, loc.kind.clone(), loc.def.krate)
}
- _ => DefDiagnostic::macro_error(module_id, loc.kind, err.to_string()),
+ _ => DefDiagnostic::macro_error(module_id, loc.kind.clone(), err.to_string()),
};
self.def_map.diagnostics.push(diag);
}
+ if let errors @ [_, ..] = &*value {
+ let loc: MacroCallLoc = self.db.lookup_intern_macro_call(macro_call_id);
+ let diag = DefDiagnostic::macro_expansion_parse_error(module_id, loc.kind, &errors);
+ self.def_map.diagnostics.push(diag);
+ }
// Then, fetch and process the item tree. This will reuse the expansion result from above.
let item_tree = self.db.file_item_tree(file_id);
+
let mod_dir = self.mod_dirs[&module_id].clone();
ModCollector {
def_collector: &mut *self,
@@ -1384,8 +1422,9 @@ impl DefCollector<'_> {
for directive in &self.unresolved_macros {
match &directive.kind {
MacroDirectiveKind::FnLike { ast_id, expand_to } => {
+ // FIXME: we shouldn't need to re-resolve the macro here just to get the unresolved error!
let macro_call_as_call_id = macro_call_as_call_id(
- self.db,
+ self.db.upcast(),
ast_id,
*expand_to,
self.def_map.krate,
@@ -1396,13 +1435,13 @@ impl DefCollector<'_> {
directive.module_id,
&path,
BuiltinShadowMode::Module,
+ Some(MacroSubNs::Bang),
);
resolved_res
.resolved_def
.take_macros()
.map(|it| macro_id_to_def_id(self.db, it))
},
- &mut |_| (),
);
if let Err(UnresolvedMacro { path }) = macro_call_as_call_id {
self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call(
@@ -1489,6 +1528,7 @@ impl ModCollector<'_, '_> {
fn collect(&mut self, items: &[ModItem], container: ItemContainerId) {
let krate = self.def_collector.def_map.krate;
+ let is_crate_root = self.module_id == DefMap::ROOT;
// Note: don't assert that inserted value is fresh: it's simply not true
// for macros.
@@ -1496,28 +1536,22 @@ impl ModCollector<'_, '_> {
// Prelude module is always considered to be `#[macro_use]`.
if let Some(prelude_module) = self.def_collector.def_map.prelude {
- if prelude_module.krate != krate {
+ if prelude_module.krate != krate && is_crate_root {
cov_mark::hit!(prelude_is_macro_use);
- self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate);
+ self.def_collector.import_macros_from_extern_crate(prelude_module.krate, None);
}
}
// This should be processed eagerly instead of deferred to resolving.
// `#[macro_use] extern crate` is hoisted to imports macros before collecting
// any other items.
- for &item in items {
- let attrs = self.item_tree.attrs(self.def_collector.db, krate, item.into());
- if attrs.cfg().map_or(true, |cfg| self.is_cfg_enabled(&cfg)) {
+ //
+ // If we're not at the crate root, `macro_use`d extern crates are an error so let's just
+ // ignore them.
+ if is_crate_root {
+ for &item in items {
if let ModItem::ExternCrate(id) = item {
- let import = &self.item_tree[id];
- let attrs = self.item_tree.attrs(
- self.def_collector.db,
- krate,
- ModItem::from(id).into(),
- );
- if attrs.by_key("macro_use").exists() {
- self.def_collector.import_macros_from_extern_crate(self.module_id, import);
- }
+ self.process_macro_use_extern_crate(id);
}
}
}
@@ -1610,14 +1644,12 @@ impl ModCollector<'_, '_> {
FunctionLoc { container, id: ItemTreeId::new(self.tree_id, id) }.intern(db);
let vis = resolve_vis(def_map, &self.item_tree[it.visibility]);
- if self.def_collector.is_proc_macro && self.module_id == def_map.root {
+ if self.def_collector.is_proc_macro && self.module_id == DefMap::ROOT {
if let Some(proc_macro) = attrs.parse_proc_macro_decl(&it.name) {
- let crate_root = def_map.module_id(def_map.root);
self.def_collector.export_proc_macro(
proc_macro,
ItemTreeId::new(self.tree_id, id),
fn_id,
- crate_root,
);
}
}
@@ -1744,6 +1776,52 @@ impl ModCollector<'_, '_> {
}
}
+ fn process_macro_use_extern_crate(&mut self, extern_crate: FileItemTreeId<ExternCrate>) {
+ let db = self.def_collector.db;
+ let attrs = self.item_tree.attrs(
+ db,
+ self.def_collector.def_map.krate,
+ ModItem::from(extern_crate).into(),
+ );
+ if let Some(cfg) = attrs.cfg() {
+ if !self.is_cfg_enabled(&cfg) {
+ return;
+ }
+ }
+
+ let target_crate =
+ match self.def_collector.resolve_extern_crate(&self.item_tree[extern_crate].name) {
+ Some(m) => {
+ if m == self.def_collector.def_map.module_id(self.module_id) {
+ cov_mark::hit!(ignore_macro_use_extern_crate_self);
+ return;
+ }
+ m.krate
+ }
+ None => return,
+ };
+
+ cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use);
+
+ let mut single_imports = Vec::new();
+ let hygiene = Hygiene::new_unhygienic();
+ for attr in attrs.by_key("macro_use").attrs() {
+ let Some(paths) = attr.parse_path_comma_token_tree(db.upcast(), &hygiene) else {
+ // `#[macro_use]` (without any paths) found, forget collected names and just import
+ // all visible macros.
+ self.def_collector.import_macros_from_extern_crate(target_crate, None);
+ return;
+ };
+ for path in paths {
+ if let Some(name) = path.as_ident() {
+ single_imports.push(name.clone());
+ }
+ }
+ }
+
+ self.def_collector.import_macros_from_extern_crate(target_crate, Some(single_imports));
+ }
+
fn collect_module(&mut self, module_id: FileItemTreeId<Mod>, attrs: &Attrs) {
let path_attr = attrs.by_key("path").string_value();
let is_macro_use = attrs.by_key("macro_use").exists();
@@ -1844,7 +1922,6 @@ impl ModCollector<'_, '_> {
let vis = def_map
.resolve_visibility(self.def_collector.db, self.module_id, visibility, false)
.unwrap_or(Visibility::Public);
- let modules = &mut def_map.modules;
let origin = match definition {
None => ModuleOrigin::Inline {
definition: declaration,
@@ -1858,6 +1935,7 @@ impl ModCollector<'_, '_> {
},
};
+ let modules = &mut def_map.modules;
let res = modules.alloc(ModuleData::new(origin, vis));
modules[res].parent = Some(self.module_id);
for (name, mac) in modules[self.module_id].scope.collect_legacy_macros() {
@@ -1919,7 +1997,10 @@ impl ModCollector<'_, '_> {
if self.def_collector.def_map.is_builtin_or_registered_attr(&attr.path) {
continue;
}
- tracing::debug!("non-builtin attribute {}", attr.path);
+ tracing::debug!(
+ "non-builtin attribute {}",
+ attr.path.display(self.def_collector.db.upcast())
+ );
let ast_id = AstIdWithPath::new(
self.file_id(),
@@ -2053,8 +2134,8 @@ impl ModCollector<'_, '_> {
stdx::always!(
name == mac.name,
"built-in macro {} has #[rustc_builtin_macro] which declares different name {}",
- mac.name,
- name
+ mac.name.display(self.def_collector.db.upcast()),
+ name.display(self.def_collector.db.upcast())
);
helpers_opt = Some(helpers);
}
@@ -2089,73 +2170,60 @@ impl ModCollector<'_, '_> {
&self.item_tree[mac.visibility],
);
if let Some(helpers) = helpers_opt {
- self.def_collector
- .def_map
- .exported_derives
- .insert(macro_id_to_def_id(self.def_collector.db, macro_id.into()), helpers);
+ if self.def_collector.def_map.block.is_none() {
+ Arc::get_mut(&mut self.def_collector.def_map.data)
+ .unwrap()
+ .exported_derives
+ .insert(macro_id_to_def_id(self.def_collector.db, macro_id.into()), helpers);
+ }
}
}
fn collect_macro_call(&mut self, mac: &MacroCall, container: ItemContainerId) {
let ast_id = AstIdWithPath::new(self.file_id(), mac.ast_id, ModPath::clone(&mac.path));
+ let db = self.def_collector.db;
+
+ // FIXME: Immediately expanding in "Case 1" is insufficient since "Case 2" may also define
+ // new legacy macros that create textual scopes. We need a way to resolve names in textual
+ // scopes without eager expansion.
- // Case 1: try to resolve in legacy scope and expand macro_rules
- let mut error = None;
- match macro_call_as_call_id(
- self.def_collector.db,
+ // Case 1: try to resolve macro calls with single-segment name and expand macro_rules
+ if let Ok(res) = macro_call_as_call_id(
+ db.upcast(),
&ast_id,
mac.expand_to,
self.def_collector.def_map.krate,
|path| {
path.as_ident().and_then(|name| {
- self.def_collector.def_map.with_ancestor_maps(
- self.def_collector.db,
- self.module_id,
- &mut |map, module| {
- map[module]
- .scope
- .get_legacy_macro(name)
- .and_then(|it| it.last())
- .map(|&it| macro_id_to_def_id(self.def_collector.db, it))
- },
- )
+ let def_map = &self.def_collector.def_map;
+ def_map
+ .with_ancestor_maps(db, self.module_id, &mut |map, module| {
+ map[module].scope.get_legacy_macro(name)?.last().copied()
+ })
+ .or_else(|| def_map[self.module_id].scope.get(name).take_macros())
+ .or_else(|| def_map.macro_use_prelude.get(name).copied())
+ .filter(|&id| {
+ sub_namespace_match(
+ Some(MacroSubNs::from_id(db, id)),
+ Some(MacroSubNs::Bang),
+ )
+ })
+ .map(|it| macro_id_to_def_id(self.def_collector.db, it))
})
},
- &mut |err| {
- error.get_or_insert(err);
- },
) {
- Ok(Ok(macro_call_id)) => {
- // Legacy macros need to be expanded immediately, so that any macros they produce
- // are in scope.
+ // Legacy macros need to be expanded immediately, so that any macros they produce
+ // are in scope.
+ if let Some(val) = res {
self.def_collector.collect_macro_expansion(
self.module_id,
- macro_call_id,
+ val,
self.macro_depth + 1,
container,
);
-
- if let Some(err) = error {
- self.def_collector.def_map.diagnostics.push(DefDiagnostic::macro_error(
- self.module_id,
- MacroCallKind::FnLike { ast_id: ast_id.ast_id, expand_to: mac.expand_to },
- err.to_string(),
- ));
- }
-
- return;
}
- Ok(Err(_)) => {
- // Built-in macro failed eager expansion.
- self.def_collector.def_map.diagnostics.push(DefDiagnostic::macro_error(
- self.module_id,
- MacroCallKind::FnLike { ast_id: ast_id.ast_id, expand_to: mac.expand_to },
- error.unwrap().to_string(),
- ));
- return;
- }
- Err(UnresolvedMacro { .. }) => (),
+ return;
}
// Case 2: resolve in module scope, expand during name resolution.
@@ -2215,10 +2283,11 @@ mod tests {
unresolved_macros: Vec::new(),
mod_dirs: FxHashMap::default(),
cfg_options: &CfgOptions::default(),
- proc_macros: Default::default(),
+ proc_macros: Ok(vec![]),
from_glob_import: Default::default(),
skip_attrs: Default::default(),
is_proc_macro: false,
+ hygienes: FxHashMap::default(),
};
collector.seed_with_top_level();
collector.collect();