mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-27 20:42:04 +00:00
Try caching macro calls more aggressively
This commit is contained in:
parent
f28f15ac6e
commit
97b58f2846
11 changed files with 382 additions and 300 deletions
|
@ -19,8 +19,8 @@ use hir_def::{
|
|||
AsMacroCall, DefWithBodyId, FunctionId, MacroId, TraitId, VariantId,
|
||||
};
|
||||
use hir_expand::{
|
||||
attrs::collect_attrs, db::ExpandDatabase, files::InRealFile, name::AsName, ExpansionInfo,
|
||||
InMacroFile, MacroCallId, MacroFileId, MacroFileIdExt,
|
||||
attrs::collect_attrs, db::ExpandDatabase, files::InRealFile, name::AsName, InMacroFile,
|
||||
MacroCallId, MacroFileId, MacroFileIdExt,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
|
@ -129,12 +129,9 @@ pub struct Semantics<'db, DB> {
|
|||
|
||||
pub struct SemanticsImpl<'db> {
|
||||
pub db: &'db dyn HirDatabase,
|
||||
s2d_cache: RefCell<SourceToDefCache>,
|
||||
s2d_cache: RefCell<(SourceToDefCache, FxHashMap<MacroFileId, hir_expand::ExpansionInfo>)>,
|
||||
/// Rootnode to HirFileId cache
|
||||
root_to_file_cache: RefCell<FxHashMap<SyntaxNode, HirFileId>>,
|
||||
// These 2 caches are mainly useful for semantic highlighting as nothing else descends a lot of tokens
|
||||
// So we might wanna move them out into something specific for semantic highlighting
|
||||
expansion_info_cache: RefCell<FxHashMap<MacroFileId, ExpansionInfo>>,
|
||||
/// MacroCall to its expansion's MacroFileId cache
|
||||
macro_call_cache: RefCell<FxHashMap<InFile<ast::MacroCall>, MacroFileId>>,
|
||||
}
|
||||
|
@ -295,7 +292,6 @@ impl<'db> SemanticsImpl<'db> {
|
|||
db,
|
||||
s2d_cache: Default::default(),
|
||||
root_to_file_cache: Default::default(),
|
||||
expansion_info_cache: Default::default(),
|
||||
macro_call_cache: Default::default(),
|
||||
}
|
||||
}
|
||||
|
@ -314,7 +310,16 @@ impl<'db> SemanticsImpl<'db> {
|
|||
|
||||
pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> {
|
||||
let sa = self.analyze_no_infer(macro_call.syntax())?;
|
||||
let file_id = sa.expand(self.db, InFile::new(sa.file_id, macro_call))?;
|
||||
|
||||
let macro_call = InFile::new(sa.file_id, macro_call);
|
||||
let file_id = if let Some(call) =
|
||||
<ast::MacroCall as crate::semantics::ToDef>::to_def(self, macro_call)
|
||||
{
|
||||
call.as_macro_file()
|
||||
} else {
|
||||
sa.expand(self.db, macro_call)?
|
||||
};
|
||||
|
||||
let node = self.parse_or_expand(file_id.into());
|
||||
Some(node)
|
||||
}
|
||||
|
@ -322,7 +327,7 @@ impl<'db> SemanticsImpl<'db> {
|
|||
/// If `item` has an attribute macro attached to it, expands it.
|
||||
pub fn expand_attr_macro(&self, item: &ast::Item) -> Option<SyntaxNode> {
|
||||
let src = self.wrap_node_infile(item.clone());
|
||||
let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(src))?;
|
||||
let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(src.as_ref()))?;
|
||||
Some(self.parse_or_expand(macro_call_id.as_file()))
|
||||
}
|
||||
|
||||
|
@ -341,9 +346,7 @@ impl<'db> SemanticsImpl<'db> {
|
|||
Some(
|
||||
calls
|
||||
.into_iter()
|
||||
.map(|call| {
|
||||
macro_call_to_macro_id(ctx, self.db.upcast(), call?).map(|id| Macro { id })
|
||||
})
|
||||
.map(|call| macro_call_to_macro_id(ctx, call?).map(|id| Macro { id }))
|
||||
.collect(),
|
||||
)
|
||||
})
|
||||
|
@ -403,7 +406,7 @@ impl<'db> SemanticsImpl<'db> {
|
|||
|
||||
pub fn is_attr_macro_call(&self, item: &ast::Item) -> bool {
|
||||
let file_id = self.find_file(item.syntax()).file_id;
|
||||
let src = InFile::new(file_id, item.clone());
|
||||
let src = InFile::new(file_id, item);
|
||||
self.with_ctx(|ctx| ctx.item_to_macro_call(src).is_some())
|
||||
}
|
||||
|
||||
|
@ -453,7 +456,7 @@ impl<'db> SemanticsImpl<'db> {
|
|||
token_to_map: SyntaxToken,
|
||||
) -> Option<(SyntaxNode, SyntaxToken)> {
|
||||
let macro_call = self.wrap_node_infile(actual_macro_call.clone());
|
||||
let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(macro_call))?;
|
||||
let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(macro_call.as_ref()))?;
|
||||
hir_expand::db::expand_speculative(
|
||||
self.db.upcast(),
|
||||
macro_call_id,
|
||||
|
@ -705,8 +708,6 @@ impl<'db> SemanticsImpl<'db> {
|
|||
let parent = token.parent()?;
|
||||
let file_id = self.find_file(&parent).file_id.file_id()?;
|
||||
|
||||
let mut cache = self.expansion_info_cache.borrow_mut();
|
||||
|
||||
// iterate related crates and find all include! invocations that include_file_id matches
|
||||
for (invoc, _) in self
|
||||
.db
|
||||
|
@ -716,18 +717,31 @@ impl<'db> SemanticsImpl<'db> {
|
|||
.filter(|&(_, include_file_id)| include_file_id == file_id)
|
||||
{
|
||||
let macro_file = invoc.as_macro_file();
|
||||
let expansion_info = cache.entry(macro_file).or_insert_with(|| {
|
||||
let exp_info = macro_file.expansion_info(self.db.upcast());
|
||||
let expansion_info = {
|
||||
self.with_ctx(|ctx| {
|
||||
ctx.expansion_info_cache
|
||||
.entry(macro_file)
|
||||
.or_insert_with(|| {
|
||||
let exp_info = macro_file.expansion_info(self.db.upcast());
|
||||
|
||||
let InMacroFile { file_id, value } = exp_info.expanded();
|
||||
self.cache(value, file_id.into());
|
||||
let InMacroFile { file_id, value } = exp_info.expanded();
|
||||
if let InFile { file_id, value: Some(value) } = exp_info.call_node() {
|
||||
self.cache(value.ancestors().last().unwrap(), file_id);
|
||||
}
|
||||
self.cache(value, file_id.into());
|
||||
|
||||
exp_info
|
||||
});
|
||||
exp_info
|
||||
})
|
||||
.clone()
|
||||
})
|
||||
};
|
||||
|
||||
// FIXME: uncached parse
|
||||
// Create the source analyzer for the macro call scope
|
||||
let Some(sa) = self.analyze_no_infer(&self.parse_or_expand(expansion_info.call_file()))
|
||||
let Some(sa) = expansion_info
|
||||
.call_node()
|
||||
.value
|
||||
.and_then(|it| self.analyze_no_infer(&it.ancestors().last().unwrap()))
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
@ -785,23 +799,27 @@ impl<'db> SemanticsImpl<'db> {
|
|||
}
|
||||
};
|
||||
|
||||
let mut cache = self.expansion_info_cache.borrow_mut();
|
||||
let mut mcache = self.macro_call_cache.borrow_mut();
|
||||
let mut m_cache = self.macro_call_cache.borrow_mut();
|
||||
let def_map = sa.resolver.def_map();
|
||||
|
||||
let mut stack: Vec<(_, SmallVec<[_; 2]>)> = vec![(file_id, smallvec![token])];
|
||||
let mut process_expansion_for_token = |stack: &mut Vec<_>, macro_file| {
|
||||
let exp_info = cache.entry(macro_file).or_insert_with(|| {
|
||||
let exp_info = macro_file.expansion_info(self.db.upcast());
|
||||
let process_expansion_for_token = |stack: &mut Vec<_>, macro_file| {
|
||||
let InMacroFile { file_id, value: mapped_tokens } = self.with_ctx(|ctx| {
|
||||
Some(
|
||||
ctx.expansion_info_cache
|
||||
.entry(macro_file)
|
||||
.or_insert_with(|| {
|
||||
let exp_info = macro_file.expansion_info(self.db.upcast());
|
||||
|
||||
let InMacroFile { file_id, value } = exp_info.expanded();
|
||||
self.cache(value, file_id.into());
|
||||
let InMacroFile { file_id, value } = exp_info.expanded();
|
||||
self.cache(value, file_id.into());
|
||||
|
||||
exp_info
|
||||
});
|
||||
|
||||
let InMacroFile { file_id, value: mapped_tokens } = exp_info.map_range_down(span)?;
|
||||
let mapped_tokens: SmallVec<[_; 2]> = mapped_tokens.collect();
|
||||
exp_info
|
||||
})
|
||||
.map_range_down(span)?
|
||||
.map(SmallVec::<[_; 2]>::from_iter),
|
||||
)
|
||||
})?;
|
||||
|
||||
// we have found a mapping for the token if the vec is non-empty
|
||||
let res = mapped_tokens.is_empty().not().then_some(());
|
||||
|
@ -818,10 +836,7 @@ impl<'db> SemanticsImpl<'db> {
|
|||
token.parent_ancestors().filter_map(ast::Item::cast).find_map(|item| {
|
||||
// Don't force populate the dyn cache for items that don't have an attribute anyways
|
||||
item.attrs().next()?;
|
||||
Some((
|
||||
ctx.item_to_macro_call(InFile::new(file_id, item.clone()))?,
|
||||
item,
|
||||
))
|
||||
Some((ctx.item_to_macro_call(InFile::new(file_id, &item))?, item))
|
||||
})
|
||||
});
|
||||
if let Some((call_id, item)) = containing_attribute_macro_call {
|
||||
|
@ -874,13 +889,20 @@ impl<'db> SemanticsImpl<'db> {
|
|||
return None;
|
||||
}
|
||||
let macro_call = tt.syntax().parent().and_then(ast::MacroCall::cast)?;
|
||||
let mcall: hir_expand::files::InFileWrapper<HirFileId, ast::MacroCall> =
|
||||
InFile::new(file_id, macro_call);
|
||||
let file_id = match mcache.get(&mcall) {
|
||||
let mcall = InFile::new(file_id, macro_call);
|
||||
let file_id = match m_cache.get(&mcall) {
|
||||
Some(&it) => it,
|
||||
None => {
|
||||
let it = sa.expand(self.db, mcall.as_ref())?;
|
||||
mcache.insert(mcall, it);
|
||||
let it = if let Some(call) =
|
||||
<ast::MacroCall as crate::semantics::ToDef>::to_def(
|
||||
self,
|
||||
mcall.as_ref(),
|
||||
) {
|
||||
call.as_macro_file()
|
||||
} else {
|
||||
sa.expand(self.db, mcall.as_ref())?
|
||||
};
|
||||
m_cache.insert(mcall, it);
|
||||
it
|
||||
}
|
||||
};
|
||||
|
@ -1056,16 +1078,19 @@ impl<'db> SemanticsImpl<'db> {
|
|||
node: SyntaxNode,
|
||||
) -> impl Iterator<Item = SyntaxNode> + Clone + '_ {
|
||||
let node = self.find_file(&node);
|
||||
let db = self.db.upcast();
|
||||
iter::successors(Some(node.cloned()), move |&InFile { file_id, ref value }| {
|
||||
match value.parent() {
|
||||
Some(parent) => Some(InFile::new(file_id, parent)),
|
||||
None => {
|
||||
let call_node = file_id.macro_file()?.call_node(db);
|
||||
// cache the node
|
||||
// FIXME: uncached parse
|
||||
self.parse_or_expand(call_node.file_id);
|
||||
Some(call_node)
|
||||
let macro_file = file_id.macro_file()?;
|
||||
|
||||
self.with_ctx(|ctx| {
|
||||
let expansion_info = ctx
|
||||
.expansion_info_cache
|
||||
.entry(macro_file)
|
||||
.or_insert_with(|| macro_file.expansion_info(self.db.upcast()));
|
||||
expansion_info.call_node().transpose()
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -1090,7 +1115,7 @@ impl<'db> SemanticsImpl<'db> {
|
|||
.find(|tp| tp.lifetime().as_ref().map(|lt| lt.text()).as_ref() == Some(&text))
|
||||
})?;
|
||||
let src = self.wrap_node_infile(lifetime_param);
|
||||
ToDef::to_def(self, src)
|
||||
ToDef::to_def(self, src.as_ref())
|
||||
}
|
||||
|
||||
pub fn resolve_label(&self, lifetime: &ast::Lifetime) -> Option<Label> {
|
||||
|
@ -1112,7 +1137,7 @@ impl<'db> SemanticsImpl<'db> {
|
|||
})
|
||||
})?;
|
||||
let src = self.wrap_node_infile(label);
|
||||
ToDef::to_def(self, src)
|
||||
ToDef::to_def(self, src.as_ref())
|
||||
}
|
||||
|
||||
pub fn resolve_type(&self, ty: &ast::Type) -> Option<Type> {
|
||||
|
@ -1308,8 +1333,8 @@ impl<'db> SemanticsImpl<'db> {
|
|||
pub fn resolve_attr_macro_call(&self, item: &ast::Item) -> Option<Macro> {
|
||||
let item_in_file = self.wrap_node_infile(item.clone());
|
||||
let id = self.with_ctx(|ctx| {
|
||||
let macro_call_id = ctx.item_to_macro_call(item_in_file)?;
|
||||
macro_call_to_macro_id(ctx, self.db.upcast(), macro_call_id)
|
||||
let macro_call_id = ctx.item_to_macro_call(item_in_file.as_ref())?;
|
||||
macro_call_to_macro_id(ctx, macro_call_id)
|
||||
})?;
|
||||
Some(Macro { id })
|
||||
}
|
||||
|
@ -1339,13 +1364,13 @@ impl<'db> SemanticsImpl<'db> {
|
|||
}
|
||||
|
||||
fn with_ctx<F: FnOnce(&mut SourceToDefCtx<'_, '_>) -> T, T>(&self, f: F) -> T {
|
||||
let mut cache = self.s2d_cache.borrow_mut();
|
||||
let mut ctx = SourceToDefCtx { db: self.db, dynmap_cache: &mut cache };
|
||||
let (dynmap_cache, expansion_info_cache) = &mut *self.s2d_cache.borrow_mut();
|
||||
let mut ctx = SourceToDefCtx { db: self.db, dynmap_cache, expansion_info_cache };
|
||||
f(&mut ctx)
|
||||
}
|
||||
|
||||
pub fn to_def<T: ToDef>(&self, src: &T) -> Option<T::Def> {
|
||||
let src = self.find_file(src.syntax()).with_value(src).cloned();
|
||||
let src = self.find_file(src.syntax()).with_value(src);
|
||||
T::to_def(self, src)
|
||||
}
|
||||
|
||||
|
@ -1613,27 +1638,57 @@ impl<'db> SemanticsImpl<'db> {
|
|||
|
||||
fn macro_call_to_macro_id(
|
||||
ctx: &mut SourceToDefCtx<'_, '_>,
|
||||
db: &dyn ExpandDatabase,
|
||||
macro_call_id: MacroCallId,
|
||||
) -> Option<MacroId> {
|
||||
use span::HirFileIdRepr;
|
||||
|
||||
let db: &dyn ExpandDatabase = ctx.db.upcast();
|
||||
let loc = db.lookup_intern_macro_call(macro_call_id);
|
||||
|
||||
match loc.def.ast_id() {
|
||||
Either::Left(it) => ctx.macro_to_def(InFile::new(it.file_id, it.to_node(db))),
|
||||
Either::Right(it) => ctx.proc_macro_to_def(InFile::new(it.file_id, it.to_node(db))),
|
||||
Either::Left(it) => {
|
||||
let node = match it.file_id.repr() {
|
||||
HirFileIdRepr::FileId(file_id) => {
|
||||
it.to_ptr(db).to_node(&db.parse(file_id).syntax_node())
|
||||
}
|
||||
HirFileIdRepr::MacroFile(macro_file) => {
|
||||
let expansion_info = ctx
|
||||
.expansion_info_cache
|
||||
.entry(macro_file)
|
||||
.or_insert_with(|| macro_file.expansion_info(ctx.db.upcast()));
|
||||
it.to_ptr(db).to_node(&expansion_info.expanded().value)
|
||||
}
|
||||
};
|
||||
ctx.macro_to_def(InFile::new(it.file_id, &node))
|
||||
}
|
||||
Either::Right(it) => {
|
||||
let node = match it.file_id.repr() {
|
||||
HirFileIdRepr::FileId(file_id) => {
|
||||
it.to_ptr(db).to_node(&db.parse(file_id).syntax_node())
|
||||
}
|
||||
HirFileIdRepr::MacroFile(macro_file) => {
|
||||
let expansion_info = ctx
|
||||
.expansion_info_cache
|
||||
.entry(macro_file)
|
||||
.or_insert_with(|| macro_file.expansion_info(ctx.db.upcast()));
|
||||
it.to_ptr(db).to_node(&expansion_info.expanded().value)
|
||||
}
|
||||
};
|
||||
ctx.proc_macro_to_def(InFile::new(it.file_id, &node))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ToDef: AstNode + Clone {
|
||||
type Def;
|
||||
|
||||
fn to_def(sema: &SemanticsImpl<'_>, src: InFile<Self>) -> Option<Self::Def>;
|
||||
fn to_def(sema: &SemanticsImpl<'_>, src: InFile<&Self>) -> Option<Self::Def>;
|
||||
}
|
||||
|
||||
macro_rules! to_def_impls {
|
||||
($(($def:path, $ast:path, $meth:ident)),* ,) => {$(
|
||||
impl ToDef for $ast {
|
||||
type Def = $def;
|
||||
fn to_def(sema: &SemanticsImpl<'_>, src: InFile<Self>) -> Option<Self::Def> {
|
||||
fn to_def(sema: &SemanticsImpl<'_>, src: InFile<&Self>) -> Option<Self::Def> {
|
||||
sema.with_ctx(|ctx| ctx.$meth(src)).map(<$def>::from)
|
||||
}
|
||||
}
|
||||
|
@ -1666,6 +1721,7 @@ to_def_impls![
|
|||
(crate::Label, ast::Label, label_to_def),
|
||||
(crate::Adt, ast::Adt, adt_to_def),
|
||||
(crate::ExternCrateDecl, ast::ExternCrate, extern_crate_to_def),
|
||||
(MacroCallId, ast::MacroCall, macro_call_to_macro_call),
|
||||
];
|
||||
|
||||
fn find_root(node: &SyntaxNode) -> SyntaxNode {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue