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.rs739
1 files changed, 446 insertions, 293 deletions
diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs
index a2ce538356..4740e3b99e 100644
--- a/crates/hir-def/src/nameres/collector.rs
+++ b/crates/hir-def/src/nameres/collector.rs
@@ -3,41 +3,42 @@
//! `DefCollector::collect` contains the fixed-point iteration loop which
//! resolves imports and expands macros.
-use std::{cmp::Ordering, iter, mem, ops::Not};
+use std::{iter, mem};
use base_db::{BuiltDependency, Crate, CrateOrigin, LangCrateOrigin};
use cfg::{CfgAtom, CfgExpr, CfgOptions};
use either::Either;
use hir_expand::{
- EditionedFileId, ErasedAstId, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind,
- MacroDefId, MacroDefKind,
+ AttrMacroAttrIds, EditionedFileId, ErasedAstId, ExpandTo, HirFileId, InFile, MacroCallId,
+ MacroCallKind, MacroDefId, MacroDefKind,
attrs::{Attr, AttrId},
- builtin::{find_builtin_attr, find_builtin_derive, find_builtin_macro},
+ builtin::{BuiltinDeriveExpander, find_builtin_attr, find_builtin_derive, find_builtin_macro},
mod_path::{ModPath, PathKind},
name::{AsName, Name},
proc_macro::CustomProcMacroExpander,
};
-use intern::{Interned, sym};
-use itertools::{Itertools, izip};
+use intern::{Interned, Symbol, sym};
+use itertools::izip;
use la_arena::Idx;
use rustc_hash::{FxHashMap, FxHashSet};
+use smallvec::SmallVec;
use span::{Edition, FileAstId, SyntaxContext};
+use stdx::always;
use syntax::ast;
use triomphe::Arc;
use crate::{
- AdtId, AssocItemId, AstId, AstIdWithPath, ConstLoc, CrateRootModuleId, EnumLoc, ExternBlockLoc,
- ExternCrateId, ExternCrateLoc, FunctionId, FunctionLoc, FxIndexMap, ImplLoc, Intern,
- ItemContainerId, LocalModuleId, Lookup, Macro2Id, Macro2Loc, MacroExpander, MacroId,
+ AdtId, AssocItemId, AstId, AstIdWithPath, BuiltinDeriveImplId, BuiltinDeriveImplLoc, ConstLoc,
+ EnumLoc, ExternBlockLoc, ExternCrateId, ExternCrateLoc, FunctionId, FunctionLoc, FxIndexMap,
+ ImplLoc, Intern, ItemContainerId, Lookup, Macro2Id, Macro2Loc, MacroExpander, MacroId,
MacroRulesId, MacroRulesLoc, MacroRulesLocFlags, ModuleDefId, ModuleId, ProcMacroId,
ProcMacroLoc, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro, UseId,
UseLoc,
- attr::Attrs,
db::DefDatabase,
item_scope::{GlobId, ImportId, ImportOrExternCrate, PerNsGlobImports},
item_tree::{
- self, FieldsShape, ImportAlias, ImportKind, ItemTree, ItemTreeAstId, Macro2, MacroCall,
- MacroRules, Mod, ModItemId, ModKind, TreeId,
+ self, Attrs, AttrsOrCfg, FieldsShape, ImportAlias, ImportKind, ItemTree, ItemTreeAstId,
+ Macro2, MacroCall, MacroRules, Mod, ModItemId, ModKind, TreeId,
},
macro_call_as_call_id,
nameres::{
@@ -102,8 +103,10 @@ pub(super) fn collect_defs(
proc_macros,
from_glob_import: Default::default(),
skip_attrs: Default::default(),
+ prev_active_attrs: Default::default(),
unresolved_extern_crates: Default::default(),
is_proc_macro: krate.is_proc_macro,
+ deferred_builtin_derives: Default::default(),
};
if tree_id.is_block() {
collector.seed_with_inner(tree_id);
@@ -176,14 +179,14 @@ impl Import {
#[derive(Debug, Eq, PartialEq)]
struct ImportDirective {
/// The module this import directive is in.
- module_id: LocalModuleId,
+ module_id: ModuleId,
import: Import,
status: PartialResolvedImport,
}
#[derive(Clone, Debug, Eq, PartialEq)]
struct MacroDirective<'db> {
- module_id: LocalModuleId,
+ module_id: ModuleId,
depth: usize,
kind: MacroDirectiveKind<'db>,
container: ItemContainerId,
@@ -206,6 +209,7 @@ enum MacroDirectiveKind<'db> {
},
Attr {
ast_id: AstIdWithPath<ast::Item>,
+ attr_id: AttrId,
attr: Attr,
mod_item: ModItemId,
/* is this needed? */ tree: TreeId,
@@ -213,6 +217,17 @@ enum MacroDirectiveKind<'db> {
},
}
+#[derive(Debug)]
+struct DeferredBuiltinDerive {
+ call_id: MacroCallId,
+ derive: BuiltinDeriveExpander,
+ module_id: ModuleId,
+ depth: usize,
+ container: ItemContainerId,
+ derive_attr_id: AttrId,
+ derive_index: u32,
+}
+
/// Walks the tree of module recursively
struct DefCollector<'db> {
db: &'db dyn DefDatabase,
@@ -222,7 +237,7 @@ struct DefCollector<'db> {
crate_local_def_map: Option<&'db LocalDefMap>,
// The dependencies of the current crate, including optional deps like `test`.
deps: FxIndexMap<Name, BuiltDependency>,
- glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility, GlobId)>>,
+ glob_imports: FxHashMap<ModuleId, Vec<(ModuleId, Visibility, GlobId)>>,
unresolved_imports: Vec<ImportDirective>,
indeterminate_imports: Vec<(ImportDirective, PerNs)>,
unresolved_macros: Vec<MacroDirective<'db>>,
@@ -230,7 +245,7 @@ struct DefCollector<'db> {
// resolve. When we emit diagnostics for unresolved imports, we only do so if the import
// doesn't start with an unresolved crate's name.
unresolved_extern_crates: FxHashSet<Name>,
- mod_dirs: FxHashMap<LocalModuleId, ModDir>,
+ mod_dirs: FxHashMap<ModuleId, ModDir>,
cfg_options: &'db CfgOptions,
/// List of procedural macros defined by this crate. This is read from the dynamic library
/// built by the build system, and is the list of proc-macros we can actually expand. It is
@@ -246,40 +261,44 @@ struct DefCollector<'db> {
/// This also stores the attributes to skip when we resolve derive helpers and non-macro
/// non-builtin attributes in general.
// FIXME: There has to be a better way to do this
- skip_attrs: FxHashMap<InFile<FileAstId<ast::Item>>, AttrId>,
+ skip_attrs: FxHashMap<AstId<ast::Item>, AttrId>,
+ /// When we expand attributes, we need to censor all previous active attributes
+ /// on the same item. Therefore, this holds all active attributes that we already
+ /// expanded.
+ prev_active_attrs: FxHashMap<AstId<ast::Item>, SmallVec<[AttrId; 1]>>,
+ /// To save memory, we do not really expand builtin derives. Instead, we save them as a `BuiltinDeriveImplId`.
+ ///
+ /// However, we can only do that when the derive is directly above the item, and there is no attribute in between.
+ /// Otherwise, all sorts of weird things can happen, like the item name resolving to something else.
+ deferred_builtin_derives: FxHashMap<AstId<ast::Item>, Vec<DeferredBuiltinDerive>>,
}
impl<'db> DefCollector<'db> {
fn seed_with_top_level(&mut self) {
let _p = tracing::info_span!("seed_with_top_level").entered();
- let file_id = self.def_map.krate.data(self.db).root_file_id(self.db);
+ let file_id = self.def_map.krate.root_file_id(self.db);
let item_tree = self.db.file_item_tree(file_id.into());
- let attrs = item_tree.top_level_attrs(self.db, self.def_map.krate);
+ let attrs = match item_tree.top_level_attrs() {
+ AttrsOrCfg::Enabled { attrs } => attrs.as_ref(),
+ AttrsOrCfg::CfgDisabled(it) => it.1.as_ref(),
+ };
let crate_data = Arc::get_mut(&mut self.def_map.data).unwrap();
- let mut process = true;
-
// Process other crate-level attributes.
for attr in &*attrs {
- if let Some(cfg) = attr.cfg()
- && self.cfg_options.check(&cfg) == Some(false)
- {
- process = false;
- break;
- }
let Some(attr_name) = attr.path.as_ident() else { continue };
match () {
() if *attr_name == sym::recursion_limit => {
if let Some(limit) = attr.string_value()
- && let Ok(limit) = limit.as_str().parse()
+ && let Ok(limit) = limit.parse()
{
crate_data.recursion_limit = Some(limit);
}
}
() if *attr_name == sym::crate_type => {
- if attr.string_value() == Some(&sym::proc_dash_macro) {
+ if attr.string_value() == Some("proc-macro") {
self.is_proc_macro = true;
}
}
@@ -291,7 +310,7 @@ impl<'db> DefCollector<'db> {
() if *attr_name == sym::feature => {
let features =
attr.parse_path_comma_token_tree(self.db).into_iter().flatten().filter_map(
- |(feat, _)| match feat.segments() {
+ |(feat, _, _)| match feat.segments() {
[name] => Some(name.symbol().clone()),
_ => None,
},
@@ -336,22 +355,28 @@ impl<'db> DefCollector<'db> {
continue;
}
- self.local_def_map
- .extern_prelude
- .insert(name.clone(), (CrateRootModuleId { krate: dep.crate_id }, None));
+ // This eagerly draws a dependency edge between the crate def maps even if the
+ // things the crates are not used. This is not great, but at the same time we would
+ // like to compute our dependency def maps in parallel here anyways in the future
+ // which will have the same effect.
+ self.local_def_map.extern_prelude.insert(
+ name.clone(),
+ (crate_def_map(self.db, dep.crate_id).root_module_id(), None),
+ );
}
}
self.inject_prelude();
- if !process {
+ if matches!(item_tree.top_level_attrs(), AttrsOrCfg::CfgDisabled(_)) {
return;
}
+ let module_id = self.def_map.root;
ModCollector {
def_collector: self,
macro_depth: 0,
- module_id: DefMap::ROOT,
+ module_id,
tree_id: TreeId::new(file_id.into(), None),
item_tree,
mod_dir: ModDir::root(),
@@ -362,17 +387,15 @@ impl<'db> DefCollector<'db> {
fn seed_with_inner(&mut self, tree_id: TreeId) {
let item_tree = tree_id.item_tree(self.db);
- let is_cfg_enabled = item_tree
- .top_level_attrs(self.db, self.def_map.krate)
- .cfg()
- .is_none_or(|cfg| self.cfg_options.check(&cfg) != Some(false));
+ let is_cfg_enabled = matches!(item_tree.top_level_attrs(), AttrsOrCfg::Enabled { .. });
if is_cfg_enabled {
self.inject_prelude();
+ let module_id = self.def_map.root;
ModCollector {
def_collector: self,
macro_depth: 0,
- module_id: DefMap::ROOT,
+ module_id,
tree_id,
item_tree,
mod_dir: ModDir::root(),
@@ -435,7 +458,8 @@ impl<'db> DefCollector<'db> {
// 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 = &mut self.def_map.modules[DefMap::ROOT];
+ let module_id = self.def_map.root_module_id();
+ let root = &mut self.def_map.modules[module_id];
root.scope.censor_non_proc_macros(self.def_map.krate);
}
}
@@ -456,18 +480,18 @@ impl<'db> DefCollector<'db> {
self.unresolved_macros.iter().enumerate().find_map(|(idx, directive)| match &directive
.kind
{
- MacroDirectiveKind::Attr { ast_id, mod_item, attr, tree, item_tree } => {
+ MacroDirectiveKind::Attr { ast_id, mod_item, attr_id, attr, tree, item_tree } => {
self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call(
directive.module_id,
MacroCallKind::Attr {
ast_id: ast_id.ast_id,
attr_args: None,
- invoc_attr_index: attr.id,
+ censored_attr_ids: AttrMacroAttrIds::from_one(*attr_id),
},
- attr.path().clone(),
+ (*attr.path).clone(),
));
- self.skip_attrs.insert(ast_id.ast_id.with_value(mod_item.ast_id()), attr.id);
+ self.skip_attrs.insert(ast_id.ast_id.with_value(mod_item.ast_id()), *attr_id);
Some((idx, directive, *mod_item, *tree, *item_tree))
}
@@ -539,7 +563,7 @@ impl<'db> DefCollector<'db> {
let (per_ns, _) = self.def_map.resolve_path(
self.crate_local_def_map.unwrap_or(&self.local_def_map),
self.db,
- DefMap::ROOT,
+ self.def_map.root_module_id(),
&path,
BuiltinShadowMode::Other,
None,
@@ -594,7 +618,7 @@ impl<'db> DefCollector<'db> {
};
let proc_macro_id = ProcMacroLoc {
- container: self.def_map.crate_root(),
+ container: self.def_map.root_module_id(),
id: ast_id,
expander,
kind,
@@ -638,7 +662,7 @@ impl<'db> DefCollector<'db> {
/// ```
fn define_macro_rules(
&mut self,
- module_id: LocalModuleId,
+ module_id: ModuleId,
name: Name,
macro_: MacroRulesId,
export: bool,
@@ -650,7 +674,7 @@ impl<'db> DefCollector<'db> {
// 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 = DefMap::ROOT;
+ let module_id = self.def_map.root;
self.def_map.modules[module_id].scope.declare(macro_.into());
self.update(
module_id,
@@ -668,7 +692,7 @@ impl<'db> DefCollector<'db> {
/// the definition of current module.
/// And also, `macro_use` on a module will import all legacy macros visible inside to
/// current legacy scope, with possible shadowing.
- fn define_legacy_macro(&mut self, module_id: LocalModuleId, name: Name, mac: MacroId) {
+ fn define_legacy_macro(&mut self, module_id: ModuleId, name: Name, mac: MacroId) {
// Always shadowing
self.def_map.modules[module_id].scope.define_legacy_macro(name, mac);
}
@@ -678,7 +702,7 @@ impl<'db> DefCollector<'db> {
/// The scoped of macro 2.0 macro is equal to normal function
fn define_macro_def(
&mut self,
- module_id: LocalModuleId,
+ module_id: ModuleId,
name: Name,
macro_: Macro2Id,
vis: &RawVisibility,
@@ -707,7 +731,7 @@ impl<'db> DefCollector<'db> {
/// 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 = DefMap::ROOT;
+ let module_id = self.def_map.root;
self.def_map.modules[module_id].scope.declare(macro_.into());
self.update(
module_id,
@@ -732,7 +756,7 @@ impl<'db> DefCollector<'db> {
let def_map = crate_def_map(self.db, krate);
// `#[macro_use]` brings macros into macro_use prelude. Yes, even non-`macro_rules!`
// macros.
- let root_scope = &def_map[DefMap::ROOT].scope;
+ let root_scope = &def_map[def_map.root].scope;
match names {
Some(names) => {
for name in names {
@@ -805,7 +829,7 @@ impl<'db> DefCollector<'db> {
res
}
- fn resolve_import(&self, module_id: LocalModuleId, import: &Import) -> PartialResolvedImport {
+ fn resolve_import(&self, module_id: ModuleId, import: &Import) -> PartialResolvedImport {
let _p = tracing::info_span!("resolve_import", import_path = %import.path.display(self.db, Edition::LATEST))
.entered();
tracing::debug!("resolving import: {:?} ({:?})", import, self.def_map.data.edition);
@@ -918,11 +942,10 @@ impl<'db> DefCollector<'db> {
// implementation seems to work the same though.
cov_mark::hit!(std_prelude);
self.def_map.prelude = Some((m, Some(id)));
- } else if m.krate != self.def_map.krate {
+ } else if m.krate(self.db) != self.def_map.krate {
cov_mark::hit!(glob_across_crates);
// glob import from other crate => we can just import everything once
- let item_map = m.def_map(self.db);
- let scope = &item_map[m.local_id].scope;
+ let scope = &m.def_map(self.db)[m].scope;
// Module scoped macros is included
let items = scope
@@ -944,12 +967,10 @@ impl<'db> DefCollector<'db> {
// glob import from same crate => we do an initial
// import, and then need to propagate any further
// additions
- let def_map;
- let scope = if m.block == self.def_map.block_id() {
- &self.def_map[m.local_id].scope
+ let scope = if m.block(self.db) == self.def_map.block_id() {
+ &self.def_map[m].scope
} else {
- def_map = m.def_map(self.db);
- &def_map[m.local_id].scope
+ &m.def_map(self.db)[m].scope
};
// Module scoped macros is included
@@ -978,11 +999,12 @@ impl<'db> DefCollector<'db> {
Some(ImportOrExternCrate::Glob(glob)),
);
// record the glob import in case we add further items
- let glob_imports = self.glob_imports.entry(m.local_id).or_default();
+ let glob_imports = self.glob_imports.entry(m).or_default();
match glob_imports.iter_mut().find(|(mid, _, _)| *mid == module_id) {
None => glob_imports.push((module_id, vis, glob)),
Some((_, old_vis, _)) => {
- if let Some(new_vis) = old_vis.max(vis, &self.def_map) {
+ if let Some(new_vis) = old_vis.max(self.db, vis, &self.def_map)
+ {
*old_vis = new_vis;
}
}
@@ -1058,20 +1080,19 @@ impl<'db> DefCollector<'db> {
fn update(
&mut self,
// The module for which `resolutions` have been resolve
- module_id: LocalModuleId,
+ module_id: ModuleId,
resolutions: &[(Option<Name>, PerNs)],
// Visibility this import will have
vis: Visibility,
import: Option<ImportOrExternCrate>,
) {
- self.db.unwind_if_revision_cancelled();
self.update_recursive(module_id, resolutions, vis, import, 0)
}
fn update_recursive(
&mut self,
// The module for which `resolutions` have been resolved.
- module_id: LocalModuleId,
+ module_id: ModuleId,
resolutions: &[(Option<Name>, PerNs)],
// All resolutions are imported with this visibility; the visibilities in
// the `PerNs` values are ignored and overwritten
@@ -1106,7 +1127,7 @@ impl<'db> DefCollector<'db> {
let should_update = match old_vis {
None => true,
Some(old_vis) => {
- let max_vis = old_vis.max(vis, &self.def_map).unwrap_or_else(|| {
+ let max_vis = old_vis.max(self.db, vis, &self.def_map).unwrap_or_else(|| {
panic!("`Tr as _` imports with unrelated visibilities {old_vis:?} and {vis:?} (trait {tr:?})");
});
@@ -1148,7 +1169,7 @@ impl<'db> DefCollector<'db> {
.collect::<Vec<_>>();
for (glob_importing_module, glob_import_vis, glob) in glob_imports {
- let vis = glob_import_vis.min(vis, &self.def_map).unwrap_or(glob_import_vis);
+ let vis = glob_import_vis.min(self.db, vis, &self.def_map).unwrap_or(glob_import_vis);
self.update_recursive(
glob_importing_module,
resolutions,
@@ -1161,20 +1182,20 @@ impl<'db> DefCollector<'db> {
fn push_res_and_update_glob_vis(
&mut self,
- module_id: LocalModuleId,
+ module_id: ModuleId,
name: &Name,
mut defs: PerNs,
vis: Visibility,
def_import_type: Option<ImportOrExternCrate>,
) -> bool {
if let Some(def) = defs.types.as_mut() {
- def.vis = def.vis.min(vis, &self.def_map).unwrap_or(vis);
+ def.vis = def.vis.min(self.db, vis, &self.def_map).unwrap_or(vis);
}
if let Some(def) = defs.values.as_mut() {
- def.vis = def.vis.min(vis, &self.def_map).unwrap_or(vis);
+ def.vis = def.vis.min(self.db, vis, &self.def_map).unwrap_or(vis);
}
if let Some(def) = defs.macros.as_mut() {
- def.vis = def.vis.min(vis, &self.def_map).unwrap_or(vis);
+ def.vis = def.vis.min(self.db, vis, &self.def_map).unwrap_or(vis);
}
let mut changed = false;
@@ -1190,7 +1211,7 @@ impl<'db> DefCollector<'db> {
&& def.def == prev_def.def
&& self.from_glob_import.contains_type(module_id, name.clone())
&& def.vis != prev_def.vis
- && def.vis.max(prev_def.vis, &self.def_map) == Some(def.vis)
+ && def.vis.max(self.db, prev_def.vis, &self.def_map) == Some(def.vis)
{
changed = true;
// This import is being handled here, don't pass it down to
@@ -1204,7 +1225,7 @@ impl<'db> DefCollector<'db> {
&& def.def == prev_def.def
&& self.from_glob_import.contains_value(module_id, name.clone())
&& def.vis != prev_def.vis
- && def.vis.max(prev_def.vis, &self.def_map) == Some(def.vis)
+ && def.vis.max(self.db, prev_def.vis, &self.def_map) == Some(def.vis)
{
changed = true;
// See comment above.
@@ -1217,7 +1238,7 @@ impl<'db> DefCollector<'db> {
&& def.def == prev_def.def
&& self.from_glob_import.contains_macro(module_id, name.clone())
&& def.vis != prev_def.vis
- && def.vis.max(prev_def.vis, &self.def_map) == Some(def.vis)
+ && def.vis.max(self.db, prev_def.vis, &self.def_map) == Some(def.vis)
{
changed = true;
// See comment above.
@@ -1239,8 +1260,18 @@ impl<'db> DefCollector<'db> {
fn resolve_macros(&mut self) -> ReachedFixedPoint {
let mut macros = mem::take(&mut self.unresolved_macros);
let mut resolved = Vec::new();
- let mut push_resolved = |directive: &MacroDirective<'_>, call_id| {
- resolved.push((directive.module_id, directive.depth, directive.container, call_id));
+ let push_resolved = |resolved: &mut Vec<_>, directive: &MacroDirective<'_>, call_id| {
+ let attr_macro_item = match &directive.kind {
+ MacroDirectiveKind::Attr { ast_id, .. } => Some(ast_id.ast_id),
+ MacroDirectiveKind::FnLike { .. } | MacroDirectiveKind::Derive { .. } => None,
+ };
+ resolved.push((
+ directive.module_id,
+ directive.depth,
+ directive.container,
+ call_id,
+ attr_macro_item,
+ ));
};
#[derive(PartialEq, Eq)]
@@ -1259,8 +1290,8 @@ impl<'db> DefCollector<'db> {
MacroSubNs::Attr
}
};
- let resolver = |path: &_| {
- let resolved_res = self.def_map.resolve_path_fp_with_macro(
+ let resolver = |def_map: &DefMap, path: &_| {
+ let resolved_res = def_map.resolve_path_fp_with_macro(
self.crate_local_def_map.unwrap_or(&self.local_def_map),
self.db,
ResolveMode::Other,
@@ -1271,7 +1302,7 @@ impl<'db> DefCollector<'db> {
);
resolved_res.resolved_def.take_macros().map(|it| (it, self.db.macro_def(it)))
};
- let resolver_def_id = |path: &_| resolver(path).map(|(_, it)| it);
+ let resolver_def_id = |path: &_| resolver(&self.def_map, path).map(|(_, it)| it);
match &directive.kind {
MacroDirectiveKind::FnLike { ast_id, expand_to, ctxt: call_site } => {
@@ -1294,7 +1325,7 @@ impl<'db> DefCollector<'db> {
.scope
.add_macro_invoc(ast_id.ast_id, call_id);
- push_resolved(directive, call_id);
+ push_resolved(&mut resolved, directive, call_id);
res = ReachedFixedPoint::No;
return Resolved::Yes;
@@ -1308,6 +1339,7 @@ impl<'db> DefCollector<'db> {
ctxt: call_site,
derive_macro_id,
} => {
+ // FIXME: This code is almost duplicate below.
let id = derive_macro_as_call_id(
self.db,
ast_id,
@@ -1315,7 +1347,7 @@ impl<'db> DefCollector<'db> {
*derive_pos as u32,
*call_site,
self.def_map.krate,
- resolver,
+ |path| resolver(&self.def_map, path),
*derive_macro_id,
);
@@ -1342,7 +1374,8 @@ impl<'db> DefCollector<'db> {
}
}
- push_resolved(directive, call_id);
+ push_resolved(&mut resolved, directive, call_id);
+
res = ReachedFixedPoint::No;
return Resolved::Yes;
}
@@ -1350,6 +1383,7 @@ impl<'db> DefCollector<'db> {
MacroDirectiveKind::Attr {
ast_id: file_ast_id,
mod_item,
+ attr_id,
attr,
tree,
item_tree,
@@ -1362,7 +1396,7 @@ impl<'db> DefCollector<'db> {
let mod_dir = collector.mod_dirs[&directive.module_id].clone();
collector
.skip_attrs
- .insert(InFile::new(file_id, mod_item.ast_id()), attr.id);
+ .insert(InFile::new(file_id, mod_item.ast_id()), *attr_id);
ModCollector {
def_collector: collector,
@@ -1398,7 +1432,6 @@ impl<'db> DefCollector<'db> {
// being cfg'ed out).
// Ideally we will just expand them to nothing here. But we are only collecting macro calls,
// not expanding them, so we have no way to do that.
- // If you add an ignored attribute here, also add it to `Semantics::might_be_inside_macro_call()`.
if matches!(
def.kind,
MacroDefKind::BuiltInAttr(_, expander)
@@ -1410,8 +1443,18 @@ impl<'db> DefCollector<'db> {
}
}
- let call_id = || {
- attr_macro_as_call_id(self.db, file_ast_id, attr, self.def_map.krate, def)
+ let mut call_id = || {
+ let active_attrs = self.prev_active_attrs.entry(ast_id).or_default();
+ active_attrs.push(*attr_id);
+
+ attr_macro_as_call_id(
+ self.db,
+ file_ast_id,
+ attr,
+ AttrMacroAttrIds::from_many(active_attrs),
+ self.def_map.krate,
+ def,
+ )
};
if matches!(def,
MacroDefId { kind: MacroDefKind::BuiltInAttr(_, exp), .. }
@@ -1429,7 +1472,7 @@ impl<'db> DefCollector<'db> {
let diag = DefDiagnostic::invalid_derive_target(
directive.module_id,
ast_id,
- attr.id,
+ *attr_id,
);
self.def_map.diagnostics.push(diag);
return recollect_without(self);
@@ -1438,29 +1481,85 @@ impl<'db> DefCollector<'db> {
let ast_id = ast_id.with_value(ast_adt_id);
+ let mut derive_call_ids = SmallVec::new();
match attr.parse_path_comma_token_tree(self.db) {
Some(derive_macros) => {
let call_id = call_id();
- let mut len = 0;
- for (idx, (path, call_site)) in derive_macros.enumerate() {
+ for (idx, (path, call_site, _)) in derive_macros.enumerate() {
let ast_id = AstIdWithPath::new(
file_id,
ast_id.value,
Interned::new(path),
);
- self.unresolved_macros.push(MacroDirective {
- module_id: directive.module_id,
- depth: directive.depth + 1,
- kind: MacroDirectiveKind::Derive {
- ast_id,
- derive_attr: attr.id,
- derive_pos: idx,
- ctxt: call_site.ctx,
- derive_macro_id: call_id,
- },
- container: directive.container,
- });
- len = idx;
+
+ // Try to resolve the derive immediately. If we succeed, we can also use the fast path
+ // for builtin derives. If not, we cannot use it, as it can cause the ADT to become
+ // interned while the derive is still unresolved, which will cause it to get forgotten.
+ let id = derive_macro_as_call_id(
+ self.db,
+ &ast_id,
+ *attr_id,
+ idx as u32,
+ call_site.ctx,
+ self.def_map.krate,
+ |path| resolver(&self.def_map, path),
+ call_id,
+ );
+
+ if let Ok((macro_id, def_id, call_id)) = id {
+ derive_call_ids.push(Some(call_id));
+ // Record its helper attributes.
+ if def_id.krate != self.def_map.krate {
+ let def_map = crate_def_map(self.db, def_id.krate);
+ if let Some(helpers) =
+ def_map.data.exported_derives.get(&macro_id)
+ {
+ self.def_map
+ .derive_helpers_in_scope
+ .entry(ast_id.ast_id.map(|it| it.upcast()))
+ .or_default()
+ .extend(izip!(
+ helpers.iter().cloned(),
+ iter::repeat(macro_id),
+ iter::repeat(call_id),
+ ));
+ }
+ }
+
+ if super::enable_builtin_derive_fast_path()
+ && let MacroDefKind::BuiltInDerive(_, builtin_derive) =
+ def_id.kind
+ {
+ self.deferred_builtin_derives
+ .entry(ast_id.ast_id.upcast())
+ .or_default()
+ .push(DeferredBuiltinDerive {
+ call_id,
+ derive: builtin_derive,
+ module_id: directive.module_id,
+ container: directive.container,
+ depth: directive.depth,
+ derive_attr_id: *attr_id,
+ derive_index: idx as u32,
+ });
+ } else {
+ push_resolved(&mut resolved, directive, call_id);
+ }
+ } else {
+ derive_call_ids.push(None);
+ self.unresolved_macros.push(MacroDirective {
+ module_id: directive.module_id,
+ depth: directive.depth + 1,
+ kind: MacroDirectiveKind::Derive {
+ ast_id,
+ derive_attr: *attr_id,
+ derive_pos: idx,
+ ctxt: call_site.ctx,
+ derive_macro_id: call_id,
+ },
+ container: directive.container,
+ });
+ }
}
// We treat the #[derive] macro as an attribute call, but we do not resolve it for nameres collection.
@@ -1469,13 +1568,18 @@ impl<'db> DefCollector<'db> {
// Check the comment in [`builtin_attr_macro`].
self.def_map.modules[directive.module_id]
.scope
- .init_derive_attribute(ast_id, attr.id, call_id, len + 1);
+ .init_derive_attribute(
+ ast_id,
+ *attr_id,
+ call_id,
+ derive_call_ids,
+ );
}
None => {
let diag = DefDiagnostic::malformed_derive(
directive.module_id,
ast_id,
- attr.id,
+ *attr_id,
);
self.def_map.diagnostics.push(diag);
}
@@ -1500,12 +1604,25 @@ impl<'db> DefCollector<'db> {
}
}
+ // Clear deferred derives for this item, unfortunately we cannot use them due to the attribute.
+ if let Some(deferred_derives) = self.deferred_builtin_derives.remove(&ast_id) {
+ resolved.extend(deferred_derives.into_iter().map(|derive| {
+ (
+ derive.module_id,
+ derive.depth,
+ derive.container,
+ derive.call_id,
+ Some(ast_id),
+ )
+ }));
+ }
+
let call_id = call_id();
self.def_map.modules[directive.module_id]
.scope
.add_attr_macro_invoc(ast_id, call_id);
- push_resolved(directive, call_id);
+ push_resolved(&mut resolved, directive, call_id);
res = ReachedFixedPoint::No;
return Resolved::Yes;
}
@@ -1522,8 +1639,14 @@ impl<'db> DefCollector<'db> {
self.def_map.modules[module_id].scope.add_macro_invoc(ptr.map(|(_, it)| it), call_id);
}
- for (module_id, depth, container, macro_call_id) in resolved {
- self.collect_macro_expansion(module_id, macro_call_id, depth, container);
+ for (module_id, depth, container, macro_call_id, attr_macro_item) in resolved {
+ self.collect_macro_expansion(
+ module_id,
+ macro_call_id,
+ depth,
+ container,
+ attr_macro_item,
+ );
}
res
@@ -1531,10 +1654,11 @@ impl<'db> DefCollector<'db> {
fn collect_macro_expansion(
&mut self,
- module_id: LocalModuleId,
+ module_id: ModuleId,
macro_call_id: MacroCallId,
depth: usize,
container: ItemContainerId,
+ attr_macro_item: Option<AstId<ast::Item>>,
) {
if depth > self.def_map.recursion_limit() as usize {
cov_mark::hit!(macro_expansion_overflow);
@@ -1545,6 +1669,34 @@ impl<'db> DefCollector<'db> {
let item_tree = self.db.file_item_tree(file_id);
+ // Derive helpers that are in scope for an item are also in scope for attribute macro expansions
+ // of that item (but not derive or fn like macros).
+ // FIXME: This is a hack. The proper way to do this is by having a chain of derive helpers scope,
+ // where the next scope in the chain is the parent hygiene context of the span. Unfortunately
+ // it's difficult to implement with our current name resolution and hygiene system.
+ // This hack is also incorrect since it ignores item in blocks. But the main reason to bring derive
+ // helpers into scope in this case is to help with:
+ // ```
+ // #[derive(DeriveWithHelper)]
+ // #[helper]
+ // #[attr_macro]
+ // struct Foo;
+ // ```
+ // Where `attr_macro`'s input will include `#[helper]` but not the derive, and it will likely therefore
+ // also include it in its output. Therefore I hope not supporting blocks is fine at least for now.
+ if let Some(attr_macro_item) = attr_macro_item
+ && let Some(derive_helpers) = self.def_map.derive_helpers_in_scope.get(&attr_macro_item)
+ {
+ let derive_helpers = derive_helpers.clone();
+ for item in item_tree.top_level_items() {
+ self.def_map
+ .derive_helpers_in_scope
+ .entry(InFile::new(file_id, item.ast_id()))
+ .or_default()
+ .extend(derive_helpers.iter().cloned());
+ }
+ }
+
let mod_dir = if macro_call_id.is_include_macro(self.db) {
ModDir::root()
} else {
@@ -1618,7 +1770,7 @@ impl<'db> DefCollector<'db> {
derive_index: *derive_pos as u32,
derive_macro_id: *derive_macro_id,
},
- ast_id.path.as_ref().clone(),
+ (*ast_id.path).clone(),
));
}
// These are diagnosed by `reseed_with_unresolved_attribute`, as that function consumes them
@@ -1652,6 +1804,12 @@ impl<'db> DefCollector<'db> {
));
}
+ always!(
+ self.deferred_builtin_derives.is_empty(),
+ "self.deferred_builtin_derives={:#?}",
+ self.deferred_builtin_derives,
+ );
+
(self.def_map, self.local_def_map)
}
}
@@ -1660,7 +1818,7 @@ impl<'db> DefCollector<'db> {
struct ModCollector<'a, 'db> {
def_collector: &'a mut DefCollector<'db>,
macro_depth: usize,
- module_id: LocalModuleId,
+ module_id: ModuleId,
tree_id: TreeId,
item_tree: &'db ItemTree,
mod_dir: ModDir,
@@ -1668,14 +1826,13 @@ struct ModCollector<'a, 'db> {
impl ModCollector<'_, '_> {
fn collect_in_top_module(&mut self, items: &[ModItemId]) {
- let module = self.def_collector.def_map.module_id(self.module_id);
- self.collect(items, module.into())
+ self.collect(items, self.module_id.into())
}
fn collect(&mut self, items: &[ModItemId], container: ItemContainerId) {
let krate = self.def_collector.def_map.krate;
- let is_crate_root =
- self.module_id == DefMap::ROOT && self.def_collector.def_map.block.is_none();
+ let is_crate_root = self.module_id == self.def_collector.def_map.root
+ && self.def_collector.def_map.block.is_none();
// Note: don't assert that inserted value is fresh: it's simply not true
// for macros.
@@ -1684,10 +1841,10 @@ impl ModCollector<'_, '_> {
// Prelude module is always considered to be `#[macro_use]`.
if let Some((prelude_module, _use)) = self.def_collector.def_map.prelude {
// Don't insert macros from the prelude into blocks, as they can be shadowed by other macros.
- if prelude_module.krate != krate && is_crate_root {
+ if is_crate_root && prelude_module.krate(self.def_collector.db) != krate {
cov_mark::hit!(prelude_is_macro_use);
self.def_collector.import_macros_from_extern_crate(
- prelude_module.krate,
+ prelude_module.krate(self.def_collector.db),
None,
None,
);
@@ -1695,6 +1852,33 @@ impl ModCollector<'_, '_> {
}
let db = self.def_collector.db;
let module_id = self.module_id;
+ let consider_deferred_derives =
+ |file_id: HirFileId,
+ deferred_derives: &mut FxHashMap<_, Vec<DeferredBuiltinDerive>>,
+ ast_id: FileAstId<ast::Adt>,
+ id: AdtId,
+ def_map: &mut DefMap| {
+ let Some(deferred_derives) =
+ deferred_derives.remove(&InFile::new(file_id, ast_id.upcast()))
+ else {
+ return;
+ };
+ let module = &mut def_map.modules[module_id];
+ for deferred_derive in deferred_derives {
+ crate::builtin_derive::with_derive_traits(deferred_derive.derive, |trait_| {
+ let impl_id = BuiltinDeriveImplId::new(
+ db,
+ BuiltinDeriveImplLoc {
+ adt: id,
+ trait_,
+ derive_attr_id: deferred_derive.derive_attr_id,
+ derive_index: deferred_derive.derive_index,
+ },
+ );
+ module.scope.define_builtin_derive_impl(impl_id);
+ });
+ }
+ };
let update_def =
|def_collector: &mut DefCollector<'_>, id, name: &Name, vis, has_constructor| {
def_collector.def_map.modules[module_id].scope.declare(id);
@@ -1712,32 +1896,34 @@ impl ModCollector<'_, '_> {
};
let mut process_mod_item = |item: ModItemId| {
- let attrs = self.item_tree.attrs(db, krate, item.ast_id());
- if let Some(cfg) = attrs.cfg()
- && !self.is_cfg_enabled(&cfg)
- {
- let ast_id = item.ast_id().erase();
- self.emit_unconfigured_diagnostic(InFile::new(self.file_id(), ast_id), &cfg);
- return;
- }
+ let attrs = match self.item_tree.attrs(item.ast_id()) {
+ Some(AttrsOrCfg::Enabled { attrs }) => attrs.as_ref(),
+ None => Attrs::EMPTY,
+ Some(AttrsOrCfg::CfgDisabled(cfg)) => {
+ let ast_id = item.ast_id().erase();
+ self.emit_unconfigured_diagnostic(InFile::new(self.file_id(), ast_id), &cfg.0);
+ return;
+ }
+ };
- if let Err(()) = self.resolve_attributes(&attrs, item, container) {
+ if let Err(()) = self.resolve_attributes(attrs, item, container) {
// Do not process the item. It has at least one non-builtin attribute, so the
// fixed-point algorithm is required to resolve the rest of them.
return;
}
- let module = self.def_collector.def_map.module_id(module_id);
let def_map = &mut self.def_collector.def_map;
let local_def_map =
self.def_collector.crate_local_def_map.unwrap_or(&self.def_collector.local_def_map);
match item {
- ModItemId::Mod(m) => self.collect_module(m, &attrs),
+ ModItemId::Mod(m) => self.collect_module(m, attrs),
ModItemId::Use(item_tree_id) => {
- let id =
- UseLoc { container: module, id: InFile::new(self.file_id(), item_tree_id) }
- .intern(db);
+ let id = UseLoc {
+ container: module_id,
+ id: InFile::new(self.file_id(), item_tree_id),
+ }
+ .intern(db);
let is_prelude = attrs.by_key(sym::prelude_import).exists();
Import::from_use(self.item_tree, item_tree_id, id, is_prelude, |import| {
self.def_collector.unresolved_imports.push(ImportDirective {
@@ -1752,7 +1938,7 @@ impl ModCollector<'_, '_> {
&self.item_tree[item_tree_id];
let id = ExternCrateLoc {
- container: module,
+ container: module_id,
id: InFile::new(self.tree_id.file_id(), item_tree_id),
}
.intern(db);
@@ -1761,12 +1947,12 @@ impl ModCollector<'_, '_> {
let is_self = *name == sym::self_;
let resolved = if is_self {
cov_mark::hit!(extern_crate_self_as);
- Some(def_map.crate_root())
+ Some(def_map.crate_root(db))
} else {
self.def_collector
.deps
.get(name)
- .map(|dep| CrateRootModuleId { krate: dep.crate_id })
+ .map(|dep| crate_def_map(db, dep.crate_id).root_module_id())
};
let name = match alias {
@@ -1791,7 +1977,7 @@ impl ModCollector<'_, '_> {
self.process_macro_use_extern_crate(
id,
attrs.by_key(sym::macro_use).attrs(),
- resolved.krate,
+ resolved.krate(self.def_collector.db),
);
}
}
@@ -1823,7 +2009,7 @@ impl ModCollector<'_, '_> {
}
ModItemId::ExternBlock(block) => {
let extern_block_id = ExternBlockLoc {
- container: module,
+ container: module_id,
id: InFile::new(self.file_id(), block),
}
.intern(db);
@@ -1836,11 +2022,11 @@ impl ModCollector<'_, '_> {
)
}
ModItemId::MacroCall(mac) => self.collect_macro_call(mac, container),
- ModItemId::MacroRules(id) => self.collect_macro_rules(id, module),
- ModItemId::Macro2(id) => self.collect_macro_def(id, module),
+ ModItemId::MacroRules(id) => self.collect_macro_rules(id, module_id),
+ ModItemId::Macro2(id) => self.collect_macro_def(id, module_id),
ModItemId::Impl(imp) => {
let impl_id =
- ImplLoc { container: module, id: InFile::new(self.file_id(), imp) }
+ ImplLoc { container: module_id, id: InFile::new(self.file_id(), imp) }
.intern(db);
self.def_collector.def_map.modules[self.module_id].scope.define_impl(impl_id)
}
@@ -1854,7 +2040,7 @@ impl ModCollector<'_, '_> {
if self.def_collector.def_map.block.is_none()
&& self.def_collector.is_proc_macro
- && self.module_id == DefMap::ROOT
+ && self.module_id == self.def_collector.def_map.root
&& let Some(proc_macro) = attrs.parse_proc_macro_decl(&it.name)
{
self.def_collector.export_proc_macro(
@@ -1870,11 +2056,21 @@ impl ModCollector<'_, '_> {
let it = &self.item_tree[id];
let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
+ let interned = StructLoc {
+ container: module_id,
+ id: InFile::new(self.tree_id.file_id(), id),
+ }
+ .intern(db);
+ consider_deferred_derives(
+ self.tree_id.file_id(),
+ &mut self.def_collector.deferred_builtin_derives,
+ id.upcast(),
+ interned.into(),
+ def_map,
+ );
update_def(
self.def_collector,
- StructLoc { container: module, id: InFile::new(self.file_id(), id) }
- .intern(db)
- .into(),
+ interned.into(),
&it.name,
vis,
!matches!(it.shape, FieldsShape::Record),
@@ -1884,22 +2080,35 @@ impl ModCollector<'_, '_> {
let it = &self.item_tree[id];
let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
- update_def(
- self.def_collector,
- UnionLoc { container: module, id: InFile::new(self.file_id(), id) }
- .intern(db)
- .into(),
- &it.name,
- vis,
- false,
+ let interned = UnionLoc {
+ container: module_id,
+ id: InFile::new(self.tree_id.file_id(), id),
+ }
+ .intern(db);
+ consider_deferred_derives(
+ self.tree_id.file_id(),
+ &mut self.def_collector.deferred_builtin_derives,
+ id.upcast(),
+ interned.into(),
+ def_map,
);
+ update_def(self.def_collector, interned.into(), &it.name, vis, false);
}
ModItemId::Enum(id) => {
let it = &self.item_tree[id];
- let enum_ =
- EnumLoc { container: module, id: InFile::new(self.tree_id.file_id(), id) }
- .intern(db);
+ let enum_ = EnumLoc {
+ container: module_id,
+ id: InFile::new(self.tree_id.file_id(), id),
+ }
+ .intern(db);
+ consider_deferred_derives(
+ self.tree_id.file_id(),
+ &mut self.def_collector.deferred_builtin_derives,
+ id.upcast(),
+ enum_.into(),
+ def_map,
+ );
let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
update_def(self.def_collector, enum_.into(), &it.name, vis, false);
}
@@ -1943,7 +2152,7 @@ impl ModCollector<'_, '_> {
let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
update_def(
self.def_collector,
- TraitLoc { container: module, id: InFile::new(self.file_id(), id) }
+ TraitLoc { container: module_id, id: InFile::new(self.file_id(), id) }
.intern(db)
.into(),
&it.name,
@@ -2006,7 +2215,7 @@ impl ModCollector<'_, '_> {
);
return;
};
- for (path, _) in paths {
+ for (path, _, _) in paths {
if let Some(name) = path.as_ident() {
single_imports.push(name.clone());
}
@@ -2020,7 +2229,7 @@ impl ModCollector<'_, '_> {
);
}
- fn collect_module(&mut self, module_ast_id: ItemTreeAstId<Mod>, attrs: &Attrs) {
+ fn collect_module(&mut self, module_ast_id: ItemTreeAstId<Mod>, attrs: Attrs<'_>) {
let path_attr = attrs.by_key(sym::path).string_value_unescape();
let is_macro_use = attrs.by_key(sym::macro_use).exists();
let module = &self.item_tree[module_ast_id];
@@ -2061,23 +2270,18 @@ impl ModCollector<'_, '_> {
self.file_id(),
&module.name,
path_attr.as_deref(),
+ self.def_collector.def_map.krate,
) {
Ok((file_id, is_mod_rs, mod_dir)) => {
let item_tree = db.file_item_tree(file_id.into());
- let krate = self.def_collector.def_map.krate;
- let is_enabled = item_tree
- .top_level_attrs(db, krate)
- .cfg()
- .and_then(|cfg| self.is_cfg_enabled(&cfg).not().then_some(cfg))
- .map_or(Ok(()), Err);
- match is_enabled {
- Err(cfg) => {
+ match item_tree.top_level_attrs() {
+ AttrsOrCfg::CfgDisabled(cfg) => {
self.emit_unconfigured_diagnostic(
InFile::new(self.file_id(), module_ast_id.erase()),
- &cfg,
+ &cfg.0,
);
}
- Ok(()) => {
+ AttrsOrCfg::Enabled { attrs } => {
let module_id = self.push_child_module(
module.name.clone(),
ast_id.value,
@@ -2093,11 +2297,8 @@ impl ModCollector<'_, '_> {
mod_dir,
}
.collect_in_top_module(item_tree.top_level_items());
- let is_macro_use = is_macro_use
- || item_tree
- .top_level_attrs(db, krate)
- .by_key(sym::macro_use)
- .exists();
+ let is_macro_use =
+ is_macro_use || attrs.as_ref().by_key(sym::macro_use).exists();
if is_macro_use {
self.import_all_legacy_macros(module_id);
}
@@ -2126,9 +2327,10 @@ impl ModCollector<'_, '_> {
declaration: FileAstId<ast::Module>,
definition: Option<(EditionedFileId, bool)>,
visibility: &crate::visibility::RawVisibility,
- ) -> LocalModuleId {
- let def_map = &mut self.def_collector.def_map;
- let vis = def_map
+ ) -> ModuleId {
+ let vis = self
+ .def_collector
+ .def_map
.resolve_visibility(
self.def_collector.crate_local_def_map.unwrap_or(&self.def_collector.local_def_map),
self.def_collector.db,
@@ -2149,21 +2351,25 @@ impl ModCollector<'_, '_> {
},
};
+ let module = unsafe {
+ crate::ModuleIdLt::new(
+ self.def_collector.db,
+ self.def_collector.def_map.krate,
+ self.def_collector.def_map.block_id(),
+ )
+ .to_static()
+ };
+ let def_map = &mut self.def_collector.def_map;
let modules = &mut def_map.modules;
- let res = modules.alloc(ModuleData::new(origin, vis));
- modules[res].parent = Some(self.module_id);
-
- if let Some((target, source)) = Self::borrow_modules(modules.as_mut(), res, self.module_id)
- {
- for (name, macs) in source.scope.legacy_macros() {
- for &mac in macs {
- target.scope.define_legacy_macro(name.clone(), mac);
- }
+ let mut data = ModuleData::new(origin, vis, Some(self.module_id));
+ for (name, macs) in modules[self.module_id].scope.legacy_macros() {
+ for &mac in macs {
+ data.scope.define_legacy_macro(name.clone(), mac);
}
}
- modules[self.module_id].children.insert(name.clone(), res);
+ modules[self.module_id].children.insert(name.clone(), module);
+ modules.insert(module, data);
- let module = def_map.module_id(res);
let def = ModuleDefId::from(module);
def_map.modules[self.module_id].scope.declare(def);
@@ -2173,7 +2379,7 @@ impl ModCollector<'_, '_> {
vis,
None,
);
- res
+ module
}
/// Resolves attributes on an item.
@@ -2185,36 +2391,16 @@ impl ModCollector<'_, '_> {
/// assumed to be resolved already.
fn resolve_attributes(
&mut self,
- attrs: &Attrs,
+ attrs: Attrs<'_>,
mod_item: ModItemId,
container: ItemContainerId,
) -> Result<(), ()> {
- let mut ignore_up_to = self
+ let ignore_up_to = self
.def_collector
.skip_attrs
.get(&InFile::new(self.file_id(), mod_item.ast_id()))
.copied();
- let iter = attrs
- .iter()
- .dedup_by(|a, b| {
- // FIXME: this should not be required, all attributes on an item should have a
- // unique ID!
- // Still, this occurs because `#[cfg_attr]` can "expand" to multiple attributes:
- // #[cfg_attr(not(off), unresolved, unresolved)]
- // struct S;
- // We should come up with a different way to ID attributes.
- a.id == b.id
- })
- .skip_while(|attr| match ignore_up_to {
- Some(id) if attr.id == id => {
- ignore_up_to = None;
- true
- }
- Some(_) => true,
- None => false,
- });
-
- for attr in iter {
+ for (attr_id, attr) in attrs.iter_after(ignore_up_to) {
if self.def_collector.def_map.is_builtin_or_registered_attr(&attr.path) {
continue;
}
@@ -2229,6 +2415,7 @@ impl ModCollector<'_, '_> {
depth: self.macro_depth + 1,
kind: MacroDirectiveKind::Attr {
ast_id,
+ attr_id,
attr: attr.clone(),
mod_item,
tree: self.tree_id,
@@ -2246,7 +2433,13 @@ impl ModCollector<'_, '_> {
fn collect_macro_rules(&mut self, ast_id: ItemTreeAstId<MacroRules>, module: ModuleId) {
let krate = self.def_collector.def_map.krate;
let mac = &self.item_tree[ast_id];
- let attrs = self.item_tree.attrs(self.def_collector.db, krate, ast_id.upcast());
+ let attrs = match self.item_tree.attrs(ast_id.upcast()) {
+ Some(AttrsOrCfg::Enabled { attrs }) => attrs.as_ref(),
+ None => Attrs::EMPTY,
+ Some(AttrsOrCfg::CfgDisabled(_)) => {
+ unreachable!("we only get here if the macro is not cfg'ed out")
+ }
+ };
let f_ast_id = InFile::new(self.file_id(), ast_id.upcast());
let export_attr = || attrs.by_key(sym::macro_export);
@@ -2267,14 +2460,14 @@ impl ModCollector<'_, '_> {
let name;
let name = match attrs.by_key(sym::rustc_builtin_macro).string_value_with_span() {
Some((it, span)) => {
- name = Name::new_symbol(it.clone(), span.ctx);
+ name = Name::new_symbol(Symbol::intern(it), span.ctx);
&name
}
None => {
let explicit_name =
attrs.by_key(sym::rustc_builtin_macro).tt_values().next().and_then(|tt| {
- match tt.token_trees().flat_tokens().first() {
- Some(tt::TokenTree::Leaf(tt::Leaf::Ident(name))) => Some(name),
+ match tt.token_trees().iter().next() {
+ Some(tt::TtElement::Leaf(tt::Leaf::Ident(name))) => Some(name),
_ => None,
}
});
@@ -2300,7 +2493,10 @@ impl ModCollector<'_, '_> {
}
} else {
// Case 2: normal `macro_rules!` macro
- MacroExpander::Declarative
+ let id = InFile::new(self.file_id(), ast_id);
+ let decl_expander = self.def_collector.db.decl_macro_expander(krate, id.upcast());
+ let styles = decl_expander.mac.rule_styles();
+ MacroExpander::Declarative { styles }
};
let allow_internal_unsafe = attrs.by_key(sym::allow_internal_unsafe).exists();
@@ -2328,7 +2524,13 @@ impl ModCollector<'_, '_> {
fn collect_macro_def(&mut self, ast_id: ItemTreeAstId<Macro2>, module: ModuleId) {
let krate = self.def_collector.def_map.krate;
let mac = &self.item_tree[ast_id];
- let attrs = self.item_tree.attrs(self.def_collector.db, krate, ast_id.upcast());
+ let attrs = match self.item_tree.attrs(ast_id.upcast()) {
+ Some(AttrsOrCfg::Enabled { attrs }) => attrs.as_ref(),
+ None => Attrs::EMPTY,
+ Some(AttrsOrCfg::CfgDisabled(_)) => {
+ unreachable!("we only get here if the macro is not cfg'ed out")
+ }
+ };
let f_ast_id = InFile::new(self.file_id(), ast_id.upcast());
// Case 1: builtin macros
@@ -2369,7 +2571,10 @@ impl ModCollector<'_, '_> {
}
} else {
// Case 2: normal `macro`
- MacroExpander::Declarative
+ let id = InFile::new(self.file_id(), ast_id);
+ let decl_expander = self.def_collector.db.decl_macro_expander(krate, id.upcast());
+ let styles = decl_expander.mac.rule_styles();
+ MacroExpander::Declarative { styles }
};
let allow_internal_unsafe = attrs.by_key(sym::allow_internal_unsafe).exists();
@@ -2429,12 +2634,7 @@ impl ModCollector<'_, '_> {
})
.or_else(|| def_map[self.module_id].scope.get(name).take_macros())
.or_else(|| Some(def_map.macro_use_prelude.get(name).copied()?.0))
- .filter(|&id| {
- sub_namespace_match(
- Some(MacroSubNs::from_id(db, id)),
- Some(MacroSubNs::Bang),
- )
- })
+ .filter(|&id| sub_namespace_match(db, id, Some(MacroSubNs::Bang)))
.map(|it| self.def_collector.db.macro_def(it))
})
},
@@ -2459,6 +2659,7 @@ impl ModCollector<'_, '_> {
call_id,
self.macro_depth + 1,
container,
+ None,
);
}
@@ -2475,12 +2676,10 @@ impl ModCollector<'_, '_> {
});
}
- fn import_all_legacy_macros(&mut self, module_id: LocalModuleId) {
- let Some((source, target)) = Self::borrow_modules(
- self.def_collector.def_map.modules.as_mut(),
- module_id,
- self.module_id,
- ) else {
+ fn import_all_legacy_macros(&mut self, module_id: ModuleId) {
+ let [Some(source), Some(target)] =
+ self.def_collector.def_map.modules.get_disjoint_mut([&module_id, &self.module_id])
+ else {
return;
};
@@ -2491,33 +2690,6 @@ impl ModCollector<'_, '_> {
}
}
- /// Mutably borrow two modules at once, retu
- fn borrow_modules(
- modules: &mut [ModuleData],
- a: LocalModuleId,
- b: LocalModuleId,
- ) -> Option<(&mut ModuleData, &mut ModuleData)> {
- let a = a.into_raw().into_u32() as usize;
- let b = b.into_raw().into_u32() as usize;
-
- let (a, b) = match a.cmp(&b) {
- Ordering::Equal => return None,
- Ordering::Less => {
- let (prefix, b) = modules.split_at_mut(b);
- (&mut prefix[a], &mut b[0])
- }
- Ordering::Greater => {
- let (prefix, a) = modules.split_at_mut(a);
- (&mut a[0], &mut prefix[b])
- }
- };
- Some((a, b))
- }
-
- fn is_cfg_enabled(&self, cfg: &CfgExpr) -> bool {
- self.def_collector.cfg_options.check(cfg) != Some(false)
- }
-
fn emit_unconfigured_diagnostic(&mut self, ast_id: ErasedAstId, cfg: &CfgExpr) {
self.def_collector.def_map.diagnostics.push(DefDiagnostic::unconfigured_code(
self.module_id,
@@ -2537,47 +2709,15 @@ impl ModCollector<'_, '_> {
mod tests {
use test_fixture::WithFixture;
- use crate::{nameres::DefMapCrateData, test_db::TestDB};
+ use crate::test_db::TestDB;
use super::*;
- fn do_collect_defs(db: &dyn DefDatabase, def_map: DefMap) -> DefMap {
- let mut collector = DefCollector {
- db,
- def_map,
- local_def_map: LocalDefMap::default(),
- crate_local_def_map: None,
- deps: FxIndexMap::default(),
- glob_imports: FxHashMap::default(),
- unresolved_imports: Vec::new(),
- indeterminate_imports: Vec::new(),
- unresolved_macros: Vec::new(),
- mod_dirs: FxHashMap::default(),
- cfg_options: &CfgOptions::default(),
- proc_macros: Default::default(),
- from_glob_import: Default::default(),
- skip_attrs: Default::default(),
- is_proc_macro: false,
- unresolved_extern_crates: Default::default(),
- };
- collector.seed_with_top_level();
- collector.collect();
- collector.def_map
- }
-
- fn do_resolve(not_ra_fixture: &str) -> DefMap {
- let (db, file_id) = TestDB::with_single_file(not_ra_fixture);
+ fn do_resolve(not_ra_fixture: &str) {
+ let (db, _) = TestDB::with_single_file(not_ra_fixture);
let krate = db.test_crate();
- let edition = krate.data(&db).edition;
- let module_origin = ModuleOrigin::CrateRoot { definition: file_id };
- let def_map = DefMap::empty(
- krate,
- Arc::new(DefMapCrateData::new(edition)),
- ModuleData::new(module_origin, Visibility::Public),
- None,
- );
- do_collect_defs(&db, def_map)
+ crate_def_map(&db, krate);
}
#[test]
@@ -2617,4 +2757,17 @@ foo!(KABOOM);
"#,
);
}
+
+ #[test]
+ fn crate_attrs() {
+ let fixture = r#"
+//- /lib.rs crate:foo crate-attr:recursion_limit="4" crate-attr:no_core crate-attr:no_std crate-attr:feature(register_tool)
+ "#;
+ let (db, file_id) = TestDB::with_single_file(fixture);
+ let def_map = crate_def_map(&db, file_id.krate(&db));
+ assert_eq!(def_map.recursion_limit(), 4);
+ assert!(def_map.is_no_core());
+ assert!(def_map.is_no_std());
+ assert!(def_map.is_unstable_feature_enabled(&sym::register_tool));
+ }
}