Merge pull request #19390 from Veykril/push-nnuxnoqkxlyu

refactor: Do not use `Expander` in assoc item lowering
This commit is contained in:
Lukas Wirth 2025-03-18 10:27:53 +00:00 committed by GitHub
commit 5742bdf587
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -1,19 +1,15 @@
//! Expansion of associated items //! Expansion of associated items
use hir_expand::{ use hir_expand::{AstId, InFile, Intern, Lookup, MacroCallKind, MacroDefKind, name::Name};
AstId, ExpandResult, InFile, Intern, Lookup, MacroCallKind, MacroDefKind, name::Name, use span::MacroCallId;
}; use syntax::ast;
use smallvec::SmallVec;
use span::{HirFileId, MacroCallId};
use syntax::{Parse, ast};
use triomphe::Arc; use triomphe::Arc;
use crate::{ use crate::{
AssocItemId, AstIdWithPath, ConstLoc, FunctionId, FunctionLoc, ImplId, ItemContainerId, AssocItemId, AstIdWithPath, ConstLoc, FunctionId, FunctionLoc, ImplId, ItemContainerId,
ItemLoc, ModuleId, TraitId, TypeAliasId, TypeAliasLoc, ItemLoc, ModuleId, TraitId, TypeAliasId, TypeAliasLoc,
db::DefDatabase, db::DefDatabase,
expander::{Expander, Mark}, item_tree::{AssocItem, ItemTree, ItemTreeId, MacroCall, ModItem, TreeId},
item_tree::{self, AssocItem, ItemTree, ItemTreeId, MacroCall, ModItem, TreeId},
macro_call_as_call_id, macro_call_as_call_id,
nameres::{ nameres::{
DefMap, LocalDefMap, MacroSubNs, DefMap, LocalDefMap, MacroSubNs,
@ -26,6 +22,7 @@ use crate::{
pub struct TraitItems { pub struct TraitItems {
pub items: Box<[(Name, AssocItemId)]>, pub items: Box<[(Name, AssocItemId)]>,
// box it as the vec is usually empty anyways // box it as the vec is usually empty anyways
// FIXME: AstIds are rather unstable...
pub macro_calls: Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>, pub macro_calls: Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>,
} }
@ -40,13 +37,11 @@ impl TraitItems {
tr: TraitId, tr: TraitId,
) -> (Arc<TraitItems>, DefDiagnostics) { ) -> (Arc<TraitItems>, DefDiagnostics) {
let ItemLoc { container: module_id, id: tree_id } = tr.lookup(db); let ItemLoc { container: module_id, id: tree_id } = tr.lookup(db);
let item_tree = tree_id.item_tree(db);
let tr_def = &item_tree[tree_id.value];
let mut collector = let collector = AssocItemCollector::new(db, module_id, ItemContainerId::TraitId(tr));
AssocItemCollector::new(db, module_id, tree_id.file_id(), ItemContainerId::TraitId(tr)); let item_tree = tree_id.item_tree(db);
collector.collect(&item_tree, tree_id.tree_id(), &tr_def.items); let (items, macro_calls, diagnostics) =
let (items, macro_calls, diagnostics) = collector.finish(); collector.collect(&item_tree, tree_id.tree_id(), &item_tree[tree_id.value].items);
(Arc::new(TraitItems { macro_calls, items }), DefDiagnostics::new(diagnostics)) (Arc::new(TraitItems { macro_calls, items }), DefDiagnostics::new(diagnostics))
} }
@ -81,6 +76,7 @@ impl TraitItems {
pub struct ImplItems { pub struct ImplItems {
pub items: Box<[(Name, AssocItemId)]>, pub items: Box<[(Name, AssocItemId)]>,
// box it as the vec is usually empty anyways // box it as the vec is usually empty anyways
// FIXME: AstIds are rather unstable...
pub macro_calls: Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>, pub macro_calls: Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>,
} }
@ -97,13 +93,10 @@ impl ImplItems {
let _p = tracing::info_span!("impl_items_with_diagnostics_query").entered(); let _p = tracing::info_span!("impl_items_with_diagnostics_query").entered();
let ItemLoc { container: module_id, id: tree_id } = id.lookup(db); let ItemLoc { container: module_id, id: tree_id } = id.lookup(db);
let collector = AssocItemCollector::new(db, module_id, ItemContainerId::ImplId(id));
let item_tree = tree_id.item_tree(db); let item_tree = tree_id.item_tree(db);
let impl_def = &item_tree[tree_id.value]; let (items, macro_calls, diagnostics) =
let mut collector = collector.collect(&item_tree, tree_id.tree_id(), &item_tree[tree_id.value].items);
AssocItemCollector::new(db, module_id, tree_id.file_id(), ItemContainerId::ImplId(id));
collector.collect(&item_tree, tree_id.tree_id(), &impl_def.items);
let (items, macro_calls, diagnostics) = collector.finish();
(Arc::new(ImplItems { items, macro_calls }), DefDiagnostics::new(diagnostics)) (Arc::new(ImplItems { items, macro_calls }), DefDiagnostics::new(diagnostics))
} }
@ -120,19 +113,14 @@ struct AssocItemCollector<'a> {
local_def_map: Arc<LocalDefMap>, local_def_map: Arc<LocalDefMap>,
diagnostics: Vec<DefDiagnostic>, diagnostics: Vec<DefDiagnostic>,
container: ItemContainerId, container: ItemContainerId,
expander: Expander,
depth: usize,
items: Vec<(Name, AssocItemId)>, items: Vec<(Name, AssocItemId)>,
macro_calls: Vec<(AstId<ast::Item>, MacroCallId)>, macro_calls: Vec<(AstId<ast::Item>, MacroCallId)>,
} }
impl<'a> AssocItemCollector<'a> { impl<'a> AssocItemCollector<'a> {
fn new( fn new(db: &'a dyn DefDatabase, module_id: ModuleId, container: ItemContainerId) -> Self {
db: &'a dyn DefDatabase,
module_id: ModuleId,
file_id: HirFileId,
container: ItemContainerId,
) -> Self {
let (def_map, local_def_map) = module_id.local_def_map(db); let (def_map, local_def_map) = module_id.local_def_map(db);
Self { Self {
db, db,
@ -140,20 +128,28 @@ impl<'a> AssocItemCollector<'a> {
def_map, def_map,
local_def_map, local_def_map,
container, container,
expander: Expander::new(db, file_id, module_id),
items: Vec::new(), items: Vec::new(),
depth: 0,
macro_calls: Vec::new(), macro_calls: Vec::new(),
diagnostics: Vec::new(), diagnostics: Vec::new(),
} }
} }
fn finish( fn collect(
self, mut self,
item_tree: &ItemTree,
tree_id: TreeId,
assoc_items: &[AssocItem],
) -> ( ) -> (
Box<[(Name, AssocItemId)]>, Box<[(Name, AssocItemId)]>,
Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>, Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>,
Vec<DefDiagnostic>, Vec<DefDiagnostic>,
) { ) {
self.items.reserve(assoc_items.len());
for &item in assoc_items {
self.collect_item(item_tree, tree_id, item);
}
( (
self.items.into_boxed_slice(), self.items.into_boxed_slice(),
if self.macro_calls.is_empty() { None } else { Some(Box::new(self.macro_calls)) }, if self.macro_calls.is_empty() { None } else { Some(Box::new(self.macro_calls)) },
@ -161,116 +157,99 @@ impl<'a> AssocItemCollector<'a> {
) )
} }
fn collect(&mut self, item_tree: &ItemTree, tree_id: TreeId, assoc_items: &[AssocItem]) { fn collect_item(&mut self, item_tree: &ItemTree, tree_id: TreeId, item: AssocItem) {
let container = self.container; let attrs = item_tree.attrs(self.db, self.module_id.krate, ModItem::from(item).into());
self.items.reserve(assoc_items.len()); if !attrs.is_cfg_enabled(self.module_id.krate.cfg_options(self.db)) {
self.diagnostics.push(DefDiagnostic::unconfigured_code(
self.module_id.local_id,
tree_id,
ModItem::from(item).into(),
attrs.cfg().unwrap(),
self.module_id.krate.cfg_options(self.db).clone(),
));
return;
}
'items: for &item in assoc_items { 'attrs: for attr in &*attrs {
let attrs = item_tree.attrs(self.db, self.module_id.krate, ModItem::from(item).into()); let ast_id = AstId::new(tree_id.file_id(), item.ast_id(item_tree).upcast());
if !attrs.is_cfg_enabled(self.expander.cfg_options(self.db)) { let ast_id_with_path = AstIdWithPath { path: attr.path.clone(), ast_id };
self.diagnostics.push(DefDiagnostic::unconfigured_code(
self.module_id.local_id,
tree_id,
ModItem::from(item).into(),
attrs.cfg().unwrap(),
self.expander.cfg_options(self.db).clone(),
));
continue;
}
'attrs: for attr in &*attrs { match self.def_map.resolve_attr_macro(
let ast_id = &self.local_def_map,
AstId::new(self.expander.current_file_id(), item.ast_id(item_tree).upcast()); self.db,
let ast_id_with_path = AstIdWithPath { path: attr.path.clone(), ast_id }; self.module_id.local_id,
ast_id_with_path,
match self.def_map.resolve_attr_macro( attr,
&self.local_def_map, ) {
self.db, Ok(ResolvedAttr::Macro(call_id)) => {
self.module_id.local_id, let loc = self.db.lookup_intern_macro_call(call_id);
ast_id_with_path, if let MacroDefKind::ProcMacro(_, exp, _) = loc.def.kind {
attr, // If there's no expander for the proc macro (e.g. the
) { // proc macro is ignored, or building the proc macro
Ok(ResolvedAttr::Macro(call_id)) => { // crate failed), skip expansion like we would if it was
let loc = self.db.lookup_intern_macro_call(call_id); // disabled. This is analogous to the handling in
if let MacroDefKind::ProcMacro(_, exp, _) = loc.def.kind { // `DefCollector::collect_macros`.
// If there's no expander for the proc macro (e.g. the if let Some(err) = exp.as_expand_error(self.module_id.krate) {
// proc macro is ignored, or building the proc macro self.diagnostics.push(DefDiagnostic::macro_error(
// crate failed), skip expansion like we would if it was self.module_id.local_id,
// disabled. This is analogous to the handling in
// `DefCollector::collect_macros`.
if let Some(err) = exp.as_expand_error(self.module_id.krate) {
self.diagnostics.push(DefDiagnostic::macro_error(
self.module_id.local_id,
ast_id,
(*attr.path).clone(),
err,
));
continue 'attrs;
}
}
self.macro_calls.push((ast_id, call_id));
let res =
self.expander.enter_expand_id::<ast::MacroItems>(self.db, call_id);
self.collect_macro_items(res);
continue 'items;
}
Ok(_) => (),
Err(_) => {
self.diagnostics.push(DefDiagnostic::unresolved_macro_call(
self.module_id.local_id,
MacroCallKind::Attr {
ast_id, ast_id,
attr_args: None, (*attr.path).clone(),
invoc_attr_index: attr.id, err,
}, ));
attr.path().clone(), continue 'attrs;
)); }
} }
self.macro_calls.push((ast_id, call_id));
self.collect_macro_items(call_id);
return;
}
Ok(_) => (),
Err(_) => {
self.diagnostics.push(DefDiagnostic::unresolved_macro_call(
self.module_id.local_id,
MacroCallKind::Attr { ast_id, attr_args: None, invoc_attr_index: attr.id },
attr.path().clone(),
));
} }
} }
self.collect_item(item_tree, tree_id, container, item);
} }
self.record_item(item_tree, tree_id, item);
} }
fn collect_item( fn record_item(&mut self, item_tree: &ItemTree, tree_id: TreeId, item: AssocItem) {
&mut self,
item_tree: &ItemTree,
tree_id: TreeId,
container: ItemContainerId,
item: AssocItem,
) {
match item { match item {
AssocItem::Function(id) => { AssocItem::Function(id) => {
let item = &item_tree[id]; let item = &item_tree[id];
let def = let def =
FunctionLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db); FunctionLoc { container: self.container, id: ItemTreeId::new(tree_id, id) }
.intern(self.db);
self.items.push((item.name.clone(), def.into())); self.items.push((item.name.clone(), def.into()));
} }
AssocItem::TypeAlias(id) => { AssocItem::TypeAlias(id) => {
let item = &item_tree[id]; let item = &item_tree[id];
let def = let def =
TypeAliasLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db); TypeAliasLoc { container: self.container, id: ItemTreeId::new(tree_id, id) }
.intern(self.db);
self.items.push((item.name.clone(), def.into())); self.items.push((item.name.clone(), def.into()));
} }
AssocItem::Const(id) => { AssocItem::Const(id) => {
let item = &item_tree[id]; let item = &item_tree[id];
let Some(name) = item.name.clone() else { return }; let Some(name) = item.name.clone() else { return };
let def = ConstLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db); let def = ConstLoc { container: self.container, id: ItemTreeId::new(tree_id, id) }
.intern(self.db);
self.items.push((name, def.into())); self.items.push((name, def.into()));
} }
AssocItem::MacroCall(call) => { AssocItem::MacroCall(call) => {
let file_id = self.expander.current_file_id();
let MacroCall { ast_id, expand_to, ctxt, ref path } = item_tree[call]; let MacroCall { ast_id, expand_to, ctxt, ref path } = item_tree[call];
let module = self.expander.module.local_id;
let resolver = |path: &_| { let resolver = |path: &_| {
self.def_map self.def_map
.resolve_path( .resolve_path(
&self.local_def_map, &self.local_def_map,
self.db, self.db,
module, self.module_id.local_id,
path, path,
crate::item_scope::BuiltinShadowMode::Other, crate::item_scope::BuiltinShadowMode::Other,
Some(MacroSubNs::Bang), Some(MacroSubNs::Bang),
@ -281,24 +260,23 @@ impl<'a> AssocItemCollector<'a> {
}; };
match macro_call_as_call_id( match macro_call_as_call_id(
self.db.upcast(), self.db.upcast(),
&AstIdWithPath::new(file_id, ast_id, Clone::clone(path)), &AstIdWithPath::new(tree_id.file_id(), ast_id, Clone::clone(path)),
ctxt, ctxt,
expand_to, expand_to,
self.expander.krate(), self.module_id.krate(),
resolver, resolver,
) { ) {
Ok(Some(call_id)) => { Ok(Some(call_id)) => {
let res = self.macro_calls
self.expander.enter_expand_id::<ast::MacroItems>(self.db, call_id); .push((InFile::new(tree_id.file_id(), ast_id.upcast()), call_id));
self.macro_calls.push((InFile::new(file_id, ast_id.upcast()), call_id)); self.collect_macro_items(call_id);
self.collect_macro_items(res);
} }
Ok(None) => (), Ok(None) => (),
Err(_) => { Err(_) => {
self.diagnostics.push(DefDiagnostic::unresolved_macro_call( self.diagnostics.push(DefDiagnostic::unresolved_macro_call(
self.module_id.local_id, self.module_id.local_id,
MacroCallKind::FnLike { MacroCallKind::FnLike {
ast_id: InFile::new(file_id, ast_id), ast_id: InFile::new(tree_id.file_id(), ast_id),
expand_to, expand_to,
eager: None, eager: None,
}, },
@ -310,16 +288,19 @@ impl<'a> AssocItemCollector<'a> {
} }
} }
fn collect_macro_items(&mut self, res: ExpandResult<Option<(Mark, Parse<ast::MacroItems>)>>) { fn collect_macro_items(&mut self, macro_call_id: MacroCallId) {
let Some((mark, _parse)) = res.value else { return }; if self.depth > self.def_map.recursion_limit() as usize {
tracing::warn!("macro expansion is too deep");
return;
}
let file_id = macro_call_id.as_file();
let tree_id = TreeId::new(file_id, None);
let item_tree = self.db.file_item_tree(file_id);
let tree_id = item_tree::TreeId::new(self.expander.current_file_id(), None); self.depth += 1;
let item_tree = tree_id.item_tree(self.db); for item in item_tree.top_level_items().iter().filter_map(ModItem::as_assoc_item) {
let iter: SmallVec<[_; 2]> = self.collect_item(&item_tree, tree_id, item);
item_tree.top_level_items().iter().filter_map(ModItem::as_assoc_item).collect(); }
self.depth -= 1;
self.collect(&item_tree, tree_id, &iter);
self.expander.exit(mark);
} }
} }