Skip to content

Commit df07c8f

Browse files
committed
Auto merge of #13730 - lowr:feat/builtin-macro-helper-attr, r=Veykril
Support builtin derive macro helper attributes Closes #13244 It's a bit wasteful for `Macro2Data` to have `helpers` field currently just for `Default` derive macro, but I tend to think it's okay for the time being given how rare macro2's are used.
2 parents f9bd487 + 051c659 commit df07c8f

File tree

6 files changed

+113
-40
lines changed

6 files changed

+113
-40
lines changed

crates/hir-def/src/data.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ use crate::{
1313
intern::Interned,
1414
item_tree::{self, AssocItem, FnFlags, ItemTree, ItemTreeId, ModItem, Param, TreeId},
1515
nameres::{
16-
attr_resolution::ResolvedAttr, diagnostics::DefDiagnostic, proc_macro::ProcMacroKind,
16+
attr_resolution::ResolvedAttr,
17+
diagnostics::DefDiagnostic,
18+
proc_macro::{parse_macro_name_and_helper_attrs, ProcMacroKind},
1719
DefMap,
1820
},
1921
type_ref::{TraitRef, TypeBound, TypeRef},
@@ -348,6 +350,10 @@ impl ImplData {
348350
pub struct Macro2Data {
349351
pub name: Name,
350352
pub visibility: RawVisibility,
353+
// It's a bit wasteful as currently this is only for builtin `Default` derive macro, but macro2
354+
// are rarely used in practice so I think it's okay for now.
355+
/// Derive helpers, if this is a derive rustc_builtin_macro
356+
pub helpers: Option<Box<[Name]>>,
351357
}
352358

353359
impl Macro2Data {
@@ -356,9 +362,18 @@ impl Macro2Data {
356362
let item_tree = loc.id.item_tree(db);
357363
let makro = &item_tree[loc.id.value];
358364

365+
let helpers = item_tree
366+
.attrs(db, loc.container.krate(), ModItem::from(loc.id.value).into())
367+
.by_key("rustc_builtin_macro")
368+
.tt_values()
369+
.next()
370+
.and_then(|attr| parse_macro_name_and_helper_attrs(&attr.token_trees))
371+
.map(|(_, helpers)| helpers);
372+
359373
Arc::new(Macro2Data {
360374
name: makro.name.clone(),
361375
visibility: item_tree[makro.visibility].clone(),
376+
helpers,
362377
})
363378
}
364379
}

crates/hir-def/src/nameres/collector.rs

+27-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ use crate::{
4040
diagnostics::DefDiagnostic,
4141
mod_resolution::ModDir,
4242
path_resolution::ReachedFixedPoint,
43-
proc_macro::{ProcMacroDef, ProcMacroKind},
43+
proc_macro::{parse_macro_name_and_helper_attrs, ProcMacroDef, ProcMacroKind},
4444
BuiltinShadowMode, DefMap, ModuleData, ModuleOrigin, ResolveMode,
4545
},
4646
path::{ImportAlias, ModPath, PathKind},
@@ -2005,6 +2005,7 @@ impl ModCollector<'_, '_> {
20052005
let ast_id = InFile::new(self.file_id(), mac.ast_id.upcast());
20062006

20072007
// Case 1: builtin macros
2008+
let mut helpers_opt = None;
20082009
let attrs = self.item_tree.attrs(self.def_collector.db, krate, ModItem::from(id).into());
20092010
let expander = if attrs.by_key("rustc_builtin_macro").exists() {
20102011
if let Some(expander) = find_builtin_macro(&mac.name) {
@@ -2013,6 +2014,25 @@ impl ModCollector<'_, '_> {
20132014
Either::Right(it) => MacroExpander::BuiltInEager(it),
20142015
}
20152016
} else if let Some(expander) = find_builtin_derive(&mac.name) {
2017+
if let Some(attr) = attrs.by_key("rustc_builtin_macro").tt_values().next() {
2018+
// NOTE: The item *may* have both `#[rustc_builtin_macro]` and `#[proc_macro_derive]`,
2019+
// in which case rustc ignores the helper attributes from the latter, but it
2020+
// "doesn't make sense in practice" (see rust-lang/rust#87027).
2021+
if let Some((name, helpers)) =
2022+
parse_macro_name_and_helper_attrs(&attr.token_trees)
2023+
{
2024+
// NOTE: rustc overrides the name if the macro name if it's different from the
2025+
// macro name, but we assume it isn't as there's no such case yet. FIXME if
2026+
// the following assertion fails.
2027+
stdx::always!(
2028+
name == mac.name,
2029+
"built-in macro {} has #[rustc_builtin_macro] which declares different name {}",
2030+
mac.name,
2031+
name
2032+
);
2033+
helpers_opt = Some(helpers);
2034+
}
2035+
}
20162036
MacroExpander::BuiltInDerive(expander)
20172037
} else if let Some(expander) = find_builtin_attr(&mac.name) {
20182038
MacroExpander::BuiltInAttr(expander)
@@ -2037,6 +2057,12 @@ impl ModCollector<'_, '_> {
20372057
macro_id,
20382058
&self.item_tree[mac.visibility],
20392059
);
2060+
if let Some(helpers) = helpers_opt {
2061+
self.def_collector
2062+
.def_map
2063+
.exported_derives
2064+
.insert(macro_id_to_def_id(self.def_collector.db, macro_id.into()), helpers);
2065+
}
20402066
}
20412067

20422068
fn collect_macro_call(&mut self, mac: &MacroCall, container: ItemContainerId) {

crates/hir-def/src/nameres/proc_macro.rs

+43-35
Original file line numberDiff line numberDiff line change
@@ -37,45 +37,53 @@ impl Attrs {
3737
Some(ProcMacroDef { name: func_name.clone(), kind: ProcMacroKind::Attr })
3838
} else if self.by_key("proc_macro_derive").exists() {
3939
let derive = self.by_key("proc_macro_derive").tt_values().next()?;
40+
let def = parse_macro_name_and_helper_attrs(&derive.token_trees)
41+
.map(|(name, helpers)| ProcMacroDef { name, kind: ProcMacroKind::CustomDerive { helpers } });
4042

41-
match &*derive.token_trees {
42-
// `#[proc_macro_derive(Trait)]`
43-
[TokenTree::Leaf(Leaf::Ident(trait_name))] => Some(ProcMacroDef {
44-
name: trait_name.as_name(),
45-
kind: ProcMacroKind::CustomDerive { helpers: Box::new([]) },
46-
}),
47-
48-
// `#[proc_macro_derive(Trait, attributes(helper1, helper2, ...))]`
49-
[
50-
TokenTree::Leaf(Leaf::Ident(trait_name)),
51-
TokenTree::Leaf(Leaf::Punct(comma)),
52-
TokenTree::Leaf(Leaf::Ident(attributes)),
53-
TokenTree::Subtree(helpers)
54-
] if comma.char == ',' && attributes.text == "attributes" =>
55-
{
56-
let helpers = helpers.token_trees.iter()
57-
.filter(|tt| !matches!(tt, TokenTree::Leaf(Leaf::Punct(comma)) if comma.char == ','))
58-
.map(|tt| {
59-
match tt {
60-
TokenTree::Leaf(Leaf::Ident(helper)) => Some(helper.as_name()),
61-
_ => None
62-
}
63-
})
64-
.collect::<Option<Box<[_]>>>()?;
65-
66-
Some(ProcMacroDef {
67-
name: trait_name.as_name(),
68-
kind: ProcMacroKind::CustomDerive { helpers },
69-
})
70-
}
71-
72-
_ => {
73-
tracing::trace!("malformed `#[proc_macro_derive]`: {}", derive);
74-
None
75-
}
43+
if def.is_none() {
44+
tracing::trace!("malformed `#[proc_macro_derive]`: {}", derive);
7645
}
46+
47+
def
7748
} else {
7849
None
7950
}
8051
}
8152
}
53+
54+
// This fn is intended for `#[proc_macro_derive(..)]` and `#[rustc_builtin_macro(..)]`, which have
55+
// the same strucuture.
56+
#[rustfmt::skip]
57+
pub(crate) fn parse_macro_name_and_helper_attrs(tt: &[TokenTree]) -> Option<(Name, Box<[Name]>)> {
58+
match tt {
59+
// `#[proc_macro_derive(Trait)]`
60+
// `#[rustc_builtin_macro(Trait)]`
61+
[TokenTree::Leaf(Leaf::Ident(trait_name))] => Some((trait_name.as_name(), Box::new([]))),
62+
63+
// `#[proc_macro_derive(Trait, attributes(helper1, helper2, ...))]`
64+
// `#[rustc_builtin_macro(Trait, attributes(helper1, helper2, ...))]`
65+
[
66+
TokenTree::Leaf(Leaf::Ident(trait_name)),
67+
TokenTree::Leaf(Leaf::Punct(comma)),
68+
TokenTree::Leaf(Leaf::Ident(attributes)),
69+
TokenTree::Subtree(helpers)
70+
] if comma.char == ',' && attributes.text == "attributes" =>
71+
{
72+
let helpers = helpers
73+
.token_trees
74+
.iter()
75+
.filter(
76+
|tt| !matches!(tt, TokenTree::Leaf(Leaf::Punct(comma)) if comma.char == ','),
77+
)
78+
.map(|tt| match tt {
79+
TokenTree::Leaf(Leaf::Ident(helper)) => Some(helper.as_name()),
80+
_ => None,
81+
})
82+
.collect::<Option<Box<[_]>>>()?;
83+
84+
Some((trait_name.as_name(), helpers))
85+
}
86+
87+
_ => None,
88+
}
89+
}

crates/hir-def/src/nameres/tests/macros.rs

+22
Original file line numberDiff line numberDiff line change
@@ -822,6 +822,28 @@ fn derive() {}
822822
);
823823
}
824824

825+
#[test]
826+
fn resolves_derive_helper_rustc_builtin_macro() {
827+
cov_mark::check!(resolved_derive_helper);
828+
// This is NOT the correct usage of `default` helper attribute, but we don't resolve helper
829+
// attributes on non mod items in hir nameres.
830+
check(
831+
r#"
832+
//- minicore: derive, default
833+
#[derive(Default)]
834+
#[default]
835+
enum E {
836+
A,
837+
B,
838+
}
839+
"#,
840+
expect![[r#"
841+
crate
842+
E: t
843+
"#]],
844+
);
845+
}
846+
825847
#[test]
826848
fn unresolved_attr_with_cfg_attr_hang() {
827849
// Another regression test for https://github.com/rust-lang/rust-analyzer/issues/8905

crates/hir/src/lib.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -2349,12 +2349,14 @@ impl DeriveHelper {
23492349

23502350
pub fn name(&self, db: &dyn HirDatabase) -> Name {
23512351
match self.derive {
2352-
MacroId::Macro2Id(_) => None,
2352+
MacroId::Macro2Id(it) => {
2353+
db.macro2_data(it).helpers.as_deref().and_then(|it| it.get(self.idx)).cloned()
2354+
}
23532355
MacroId::MacroRulesId(_) => None,
23542356
MacroId::ProcMacroId(proc_macro) => db
23552357
.proc_macro_data(proc_macro)
23562358
.helpers
2357-
.as_ref()
2359+
.as_deref()
23582360
.and_then(|it| it.get(self.idx))
23592361
.cloned(),
23602362
}

crates/test-utils/src/minicore.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ pub mod default {
112112
fn default() -> Self;
113113
}
114114
// region:derive
115-
#[rustc_builtin_macro]
115+
#[rustc_builtin_macro(Default, attributes(default))]
116116
pub macro Default($item:item) {}
117117
// endregion:derive
118118
}

0 commit comments

Comments
 (0)