Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-expand/src/db.rs')
-rw-r--r--crates/hir-expand/src/db.rs181
1 files changed, 121 insertions, 60 deletions
diff --git a/crates/hir-expand/src/db.rs b/crates/hir-expand/src/db.rs
index 6b5aa39fa6..888c1405a6 100644
--- a/crates/hir-expand/src/db.rs
+++ b/crates/hir-expand/src/db.rs
@@ -1,9 +1,11 @@
//! Defines database & queries for macro expansion.
use base_db::{Crate, RootQueryDb};
+use either::Either;
use mbe::MatchedArmIndex;
+use rustc_hash::FxHashSet;
use span::{AstIdMap, Edition, Span, SyntaxContext};
-use syntax::{AstNode, Parse, SyntaxError, SyntaxNode, SyntaxToken, T, ast};
+use syntax::{AstNode, Parse, SyntaxElement, SyntaxError, SyntaxNode, SyntaxToken, T, ast};
use syntax_bridge::{DocCommentDesugarMode, syntax_node_to_token_tree};
use triomphe::Arc;
@@ -11,9 +13,9 @@ use crate::{
AstId, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallInfo,
EagerExpander, EditionedFileId, ExpandError, ExpandResult, ExpandTo, HirFileId, MacroCallId,
MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind,
- attrs::Meta,
+ attrs::{AttrId, AttrInput, RawAttrs, collect_attrs},
builtin::pseudo_derive_attr_expansion,
- cfg_process::attr_macro_input_to_token_tree,
+ cfg_process,
declarative::DeclarativeMacroExpander,
fixup::{self, SyntaxFixupUndoInfo},
hygiene::{span_with_call_site_ctxt, span_with_def_site_ctxt, span_with_mixed_site_ctxt},
@@ -175,7 +177,7 @@ pub fn expand_speculative(
let span_map = SpanMapRef::RealSpanMap(&span_map);
// Build the subtree and token mapping for the speculative args
- let (mut tt, undo_info) = match &loc.kind {
+ let (mut tt, undo_info) = match loc.kind {
MacroCallKind::FnLike { .. } => (
syntax_bridge::syntax_node_to_token_tree(
speculative_args,
@@ -198,35 +200,48 @@ pub fn expand_speculative(
),
SyntaxFixupUndoInfo::NONE,
),
- MacroCallKind::Derive { derive_macro_id, .. } => {
- let MacroCallKind::Attr { censored_attr_ids: attr_ids, .. } =
- &derive_macro_id.loc(db).kind
- else {
- unreachable!("`derive_macro_id` should be `MacroCallKind::Attr`");
+ MacroCallKind::Derive { derive_attr_index: index, .. }
+ | MacroCallKind::Attr { invoc_attr_index: index, .. } => {
+ let censor = if let MacroCallKind::Derive { .. } = loc.kind {
+ censor_derive_input(index, &ast::Adt::cast(speculative_args.clone())?)
+ } else {
+ attr_source(index, &ast::Item::cast(speculative_args.clone())?)
+ .into_iter()
+ .map(|it| it.syntax().clone().into())
+ .collect()
};
- attr_macro_input_to_token_tree(
- db,
- speculative_args,
+
+ let censor_cfg =
+ cfg_process::process_cfg_attrs(db, speculative_args, &loc).unwrap_or_default();
+ let mut fixups = fixup::fixup_syntax(
span_map,
+ speculative_args,
span,
- true,
- attr_ids,
- loc.krate,
+ DocCommentDesugarMode::ProcMacro,
+ );
+ fixups.append.retain(|it, _| match it {
+ syntax::NodeOrToken::Token(_) => true,
+ it => !censor.contains(it) && !censor_cfg.contains(it),
+ });
+ fixups.remove.extend(censor);
+ fixups.remove.extend(censor_cfg);
+
+ (
+ syntax_bridge::syntax_node_to_token_tree_modified(
+ speculative_args,
+ span_map,
+ fixups.append,
+ fixups.remove,
+ span,
+ DocCommentDesugarMode::ProcMacro,
+ ),
+ fixups.undo_info,
)
}
- MacroCallKind::Attr { censored_attr_ids: attr_ids, .. } => attr_macro_input_to_token_tree(
- db,
- speculative_args,
- span_map,
- span,
- false,
- attr_ids,
- loc.krate,
- ),
};
- let attr_arg = match &loc.kind {
- MacroCallKind::Attr { censored_attr_ids: attr_ids, .. } => {
+ let attr_arg = match loc.kind {
+ MacroCallKind::Attr { invoc_attr_index, .. } => {
if loc.def.is_attribute_derive() {
// for pseudo-derive expansion we actually pass the attribute itself only
ast::Attr::cast(speculative_args.clone()).and_then(|attr| attr.token_tree()).map(
@@ -245,21 +260,18 @@ pub fn expand_speculative(
// Attributes may have an input token tree, build the subtree and map for this as well
// then try finding a token id for our token if it is inside this input subtree.
let item = ast::Item::cast(speculative_args.clone())?;
- let (_, _, _, meta) =
- attr_ids.invoc_attr().find_attr_range_with_source(db, loc.krate, &item);
- match meta {
- Meta::TokenTree { tt, .. } => {
- let mut attr_arg = syntax_bridge::syntax_node_to_token_tree(
- tt.syntax(),
- span_map,
- span,
- DocCommentDesugarMode::ProcMacro,
- );
- attr_arg.top_subtree_delimiter_mut().kind = tt::DelimiterKind::Invisible;
- Some(attr_arg)
+ let attrs = RawAttrs::new_expanded(db, &item, span_map, loc.krate.cfg_options(db));
+ attrs.iter().find(|attr| attr.id == invoc_attr_index).and_then(|attr| {
+ match attr.input.as_deref()? {
+ AttrInput::TokenTree(tt) => {
+ let mut attr_arg = tt.clone();
+ attr_arg.top_subtree_delimiter_mut().kind =
+ tt::DelimiterKind::Invisible;
+ Some(attr_arg)
+ }
+ AttrInput::Literal(_) => None,
}
- _ => None,
- }
+ })
}
}
_ => None,
@@ -421,7 +433,7 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult {
let (parse, map) = parse_with_map(db, loc.kind.file_id());
let root = parse.syntax_node();
- let (is_derive, censor_item_tree_attr_ids, item_node, span) = match &loc.kind {
+ let (censor, item_node, span) = match loc.kind {
MacroCallKind::FnLike { ast_id, .. } => {
let node = &ast_id.to_ptr(db).to_node(&root);
let path_range = node
@@ -489,29 +501,53 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult {
MacroCallKind::Derive { .. } => {
unreachable!("`ExpandDatabase::macro_arg` called with `MacroCallKind::Derive`")
}
- MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => {
+ MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => {
let node = ast_id.to_ptr(db).to_node(&root);
- let range = attr_ids
- .invoc_attr()
- .find_attr_range_with_source(db, loc.krate, &node)
- .3
- .path_range();
- let span = map.span_for_range(range);
-
- let is_derive = matches!(loc.def.kind, MacroDefKind::BuiltInAttr(_, expander) if expander.is_derive());
- (is_derive, &**attr_ids, node, span)
+ let attr_source = attr_source(invoc_attr_index, &node);
+
+ let span = map.span_for_range(
+ attr_source
+ .as_ref()
+ .and_then(|it| it.path())
+ .map_or_else(|| node.syntax().text_range(), |it| it.syntax().text_range()),
+ );
+ // If derive attribute we need to censor the derive input
+ if matches!(loc.def.kind, MacroDefKind::BuiltInAttr(_, expander) if expander.is_derive())
+ && ast::Adt::can_cast(node.syntax().kind())
+ {
+ let adt = ast::Adt::cast(node.syntax().clone()).unwrap();
+ let censor_derive_input = censor_derive_input(invoc_attr_index, &adt);
+ (censor_derive_input, node, span)
+ } else {
+ (attr_source.into_iter().map(|it| it.syntax().clone().into()).collect(), node, span)
+ }
}
};
- let (mut tt, undo_info) = attr_macro_input_to_token_tree(
- db,
- item_node.syntax(),
- map.as_ref(),
- span,
- is_derive,
- censor_item_tree_attr_ids,
- loc.krate,
- );
+ let (mut tt, undo_info) = {
+ let syntax = item_node.syntax();
+ let censor_cfg = cfg_process::process_cfg_attrs(db, syntax, &loc).unwrap_or_default();
+ let mut fixups =
+ fixup::fixup_syntax(map.as_ref(), syntax, span, DocCommentDesugarMode::ProcMacro);
+ fixups.append.retain(|it, _| match it {
+ syntax::NodeOrToken::Token(_) => true,
+ it => !censor.contains(it) && !censor_cfg.contains(it),
+ });
+ fixups.remove.extend(censor);
+ fixups.remove.extend(censor_cfg);
+
+ (
+ syntax_bridge::syntax_node_to_token_tree_modified(
+ syntax,
+ map,
+ fixups.append,
+ fixups.remove,
+ span,
+ DocCommentDesugarMode::ProcMacro,
+ ),
+ fixups.undo_info,
+ )
+ };
if loc.def.is_proc_macro() {
// proc macros expect their inputs without parentheses, MBEs expect it with them included
@@ -521,6 +557,31 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult {
(Arc::new(tt), undo_info, span)
}
+// FIXME: Censoring info should be calculated by the caller! Namely by name resolution
+/// Derives expect all `#[derive(..)]` invocations up to (and including) the currently invoked one to be stripped
+fn censor_derive_input(derive_attr_index: AttrId, node: &ast::Adt) -> FxHashSet<SyntaxElement> {
+ // FIXME: handle `cfg_attr`
+ cov_mark::hit!(derive_censoring);
+ collect_attrs(node)
+ .take(derive_attr_index.ast_index() + 1)
+ .filter_map(|(_, attr)| Either::left(attr))
+ // FIXME, this resolution should not be done syntactically
+ // derive is a proper macro now, no longer builtin
+ // But we do not have resolution at this stage, this means
+ // we need to know about all macro calls for the given ast item here
+ // so we require some kind of mapping...
+ .filter(|attr| attr.simple_name().as_deref() == Some("derive"))
+ .map(|it| it.syntax().clone().into())
+ .collect()
+}
+
+/// Attributes expect the invoking attribute to be stripped
+fn attr_source(invoc_attr_index: AttrId, node: &ast::Item) -> Option<ast::Attr> {
+ // FIXME: handle `cfg_attr`
+ cov_mark::hit!(attribute_macro_attr_censoring);
+ collect_attrs(node).nth(invoc_attr_index.ast_index()).and_then(|(_, attr)| Either::left(attr))
+}
+
impl TokenExpander {
fn macro_expander(db: &dyn ExpandDatabase, id: MacroDefId) -> TokenExpander {
match id.kind {