mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-01 11:31:15 +00:00
Merge pull request #19390 from Veykril/push-nnuxnoqkxlyu
refactor: Do not use `Expander` in assoc item lowering
This commit is contained in:
commit
5742bdf587
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user