Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir/src/semantics.rs')
-rw-r--r--crates/hir/src/semantics.rs283
1 files changed, 200 insertions, 83 deletions
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 43de2a6ee7..f6c88edbff 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -19,7 +19,11 @@ use hir_def::{
AsMacroCall, DefWithBodyId, FunctionId, MacroId, TraitId, VariantId,
};
use hir_expand::{
- attrs::collect_attrs, db::ExpandDatabase, files::InRealFile, name::AsName, ExpansionInfo,
+ attrs::collect_attrs,
+ builtin_fn_macro::{BuiltinFnLikeExpander, EagerExpander},
+ db::ExpandDatabase,
+ files::InRealFile,
+ name::AsName,
InMacroFile, MacroCallId, MacroFileId, MacroFileIdExt,
};
use itertools::Itertools;
@@ -132,9 +136,6 @@ pub struct SemanticsImpl<'db> {
s2d_cache: RefCell<SourceToDefCache>,
/// 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 +296,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 +314,58 @@ 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)
+ }
+
+ /// Expands the macro if it isn't one of the built-in ones that expand to custom syntax or dummy
+ /// expansions.
+ pub fn expand_allowed_builtins(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> {
+ let sa = self.analyze_no_infer(macro_call.syntax())?;
+
+ 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 macro_call = self.db.lookup_intern_macro_call(file_id.macro_call_id);
+
+ let skip = matches!(
+ macro_call.def.kind,
+ hir_expand::MacroDefKind::BuiltIn(
+ _,
+ BuiltinFnLikeExpander::Column
+ | BuiltinFnLikeExpander::File
+ | BuiltinFnLikeExpander::ModulePath
+ | BuiltinFnLikeExpander::Asm
+ | BuiltinFnLikeExpander::LlvmAsm
+ | BuiltinFnLikeExpander::GlobalAsm
+ | BuiltinFnLikeExpander::LogSyntax
+ | BuiltinFnLikeExpander::TraceMacros
+ | BuiltinFnLikeExpander::FormatArgs
+ | BuiltinFnLikeExpander::FormatArgsNl
+ | BuiltinFnLikeExpander::ConstFormatArgs,
+ ) | hir_expand::MacroDefKind::BuiltInEager(_, EagerExpander::CompileError)
+ );
+ if skip {
+ // these macros expand to custom builtin syntax and/or dummy things, no point in
+ // showing these to the user
+ return None;
+ }
+
let node = self.parse_or_expand(file_id.into());
Some(node)
}
@@ -322,7 +373,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 +392,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 +452,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())
}
@@ -420,7 +469,7 @@ impl<'db> SemanticsImpl<'db> {
let macro_call = InFile::new(file_id, actual_macro_call);
let krate = resolver.krate();
let macro_call_id = macro_call.as_call_id(self.db.upcast(), krate, |path| {
- resolver.resolve_path_as_macro_def(self.db.upcast(), &path, Some(MacroSubNs::Bang))
+ resolver.resolve_path_as_macro_def(self.db.upcast(), path, Some(MacroSubNs::Bang))
})?;
hir_expand::db::expand_speculative(
self.db.upcast(),
@@ -453,7 +502,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 +754,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 +763,32 @@ 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 InMacroFile { file_id, value } = exp_info.expanded();
- self.cache(value, file_id.into());
+ let expansion_info = {
+ self.with_ctx(|ctx| {
+ ctx.cache
+ .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();
+ if let InFile { file_id, value: Some(value) } = exp_info.arg() {
+ 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
+ .arg()
+ .value
+ .and_then(|it| self.analyze_no_infer(&it.ancestors().last().unwrap()))
else {
continue;
};
@@ -758,7 +819,7 @@ impl<'db> SemanticsImpl<'db> {
mut token: SyntaxToken,
f: &mut dyn FnMut(InFile<SyntaxToken>) -> ControlFlow<()>,
) {
- let _p = tracing::span!(tracing::Level::INFO, "descend_into_macros_impl").entered();
+ let _p = tracing::info_span!("descend_into_macros_impl").entered();
let (sa, span, file_id) =
match token.parent().and_then(|parent| self.analyze_no_infer(&parent)) {
Some(sa) => match sa.file_id.file_id() {
@@ -785,23 +846,28 @@ 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 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();
+ let process_expansion_for_token = |stack: &mut Vec<_>, macro_file| {
+ let InMacroFile { file_id, value: mapped_tokens } = self.with_ctx(|ctx| {
+ Some(
+ ctx.cache
+ .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());
+
+ 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 +884,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 +937,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
}
};
@@ -953,6 +1023,13 @@ impl<'db> SemanticsImpl<'db> {
let helpers =
def_map.derive_helpers_in_scope(InFile::new(file_id, id))?;
+ if !helpers.is_empty() {
+ let text_range = attr.syntax().text_range();
+ // remove any other token in this macro input, all their mappings are the
+ // same as this
+ tokens.retain(|t| !text_range.contains_range(t.text_range()));
+ }
+
let mut res = None;
for (.., derive) in
helpers.iter().filter(|(helper, ..)| *helper == attr_name)
@@ -1056,16 +1133,20 @@ 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
+ .cache
+ .expansion_info_cache
+ .entry(macro_file)
+ .or_insert_with(|| macro_file.expansion_info(self.db.upcast()));
+ expansion_info.arg().map(|node| node?.parent()).transpose()
+ })
}
}
})
@@ -1090,7 +1171,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 +1193,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> {
@@ -1275,9 +1356,15 @@ impl<'db> SemanticsImpl<'db> {
}
pub fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option<Macro> {
- let sa = self.analyze(macro_call.syntax())?;
let macro_call = self.find_file(macro_call.syntax()).with_value(macro_call);
- sa.resolve_macro_call(self.db, macro_call)
+ self.with_ctx(|ctx| {
+ ctx.macro_call_to_macro_call(macro_call)
+ .and_then(|call| macro_call_to_macro_id(ctx, call))
+ .map(Into::into)
+ })
+ .or_else(|| {
+ self.analyze(macro_call.value.syntax())?.resolve_macro_call(self.db, macro_call)
+ })
}
pub fn is_proc_macro_call(&self, macro_call: &ast::MacroCall) -> bool {
@@ -1297,19 +1384,24 @@ impl<'db> SemanticsImpl<'db> {
}
pub fn is_unsafe_macro_call(&self, macro_call: &ast::MacroCall) -> bool {
- let sa = match self.analyze(macro_call.syntax()) {
- Some(it) => it,
- None => return false,
- };
+ let Some(mac) = self.resolve_macro_call(macro_call) else { return false };
+ if mac.is_asm_or_global_asm(self.db) {
+ return true;
+ }
+
+ let Some(sa) = self.analyze(macro_call.syntax()) else { return false };
let macro_call = self.find_file(macro_call.syntax()).with_value(macro_call);
- sa.is_unsafe_macro_call(self.db, macro_call)
+ match macro_call.map(|it| it.syntax().parent().and_then(ast::MacroExpr::cast)).transpose() {
+ Some(it) => sa.is_unsafe_macro_call_expr(self.db, it.as_ref()),
+ None => false,
+ }
}
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,18 +1431,17 @@ 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 mut ctx = SourceToDefCtx { db: self.db, cache: &mut self.s2d_cache.borrow_mut() };
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)
}
fn file_to_module_defs(&self, file: FileId) -> impl Iterator<Item = Module> {
- self.with_ctx(|ctx| ctx.file_to_def(file)).into_iter().map(Module::from)
+ self.with_ctx(|ctx| ctx.file_to_def(file).to_owned()).into_iter().map(Module::from)
}
pub fn scope(&self, node: &SyntaxNode) -> Option<SemanticsScope<'db>> {
@@ -1380,6 +1471,7 @@ impl<'db> SemanticsImpl<'db> {
where
Def::Ast: AstNode,
{
+ // FIXME: source call should go through the parse cache
let res = def.source(self.db)?;
self.cache(find_root(res.value.syntax()), res.file_id);
Some(res)
@@ -1409,7 +1501,7 @@ impl<'db> SemanticsImpl<'db> {
offset: Option<TextSize>,
infer_body: bool,
) -> Option<SourceAnalyzer> {
- let _p = tracing::span!(tracing::Level::INFO, "SemanticsImpl::analyze_impl").entered();
+ let _p = tracing::info_span!("SemanticsImpl::analyze_impl").entered();
let node = self.find_file(node);
let container = self.with_ctx(|ctx| ctx.find_container(node))?;
@@ -1438,7 +1530,7 @@ impl<'db> SemanticsImpl<'db> {
assert!(root_node.parent().is_none());
let mut cache = self.root_to_file_cache.borrow_mut();
let prev = cache.insert(root_node, file_id);
- assert!(prev.is_none() || prev == Some(file_id))
+ assert!(prev.is_none() || prev == Some(file_id));
}
pub fn assert_contains_node(&self, node: &SyntaxNode) {
@@ -1613,35 +1705,59 @@ 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.kind {
- hir_expand::MacroDefKind::Declarative(it)
- | hir_expand::MacroDefKind::BuiltIn(_, it)
- | hir_expand::MacroDefKind::BuiltInAttr(_, it)
- | hir_expand::MacroDefKind::BuiltInDerive(_, it)
- | hir_expand::MacroDefKind::BuiltInEager(_, it) => {
- ctx.macro_to_def(InFile::new(it.file_id, it.to_node(db)))
+
+ match loc.def.ast_id() {
+ 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
+ .cache
+ .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))
}
- hir_expand::MacroDefKind::ProcMacro(_, _, it) => {
- ctx.proc_macro_to_def(InFile::new(it.file_id, it.to_node(db)))
+ 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
+ .cache
+ .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)
}
}
@@ -1674,6 +1790,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 {