Skip to content

Commit e5a2c65

Browse files
Expand procedural attribute macros
1 parent 7f9c4a5 commit e5a2c65

File tree

7 files changed

+159
-14
lines changed

7 files changed

+159
-14
lines changed

crates/hir/src/lib.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,18 @@ impl Module {
534534
Some(derive_name.clone()),
535535
)
536536
}
537+
MacroCallKind::Attr { ast_id, invoc_attr_index, attr_name, .. } => {
538+
let node = ast_id.to_node(db.upcast());
539+
let attr =
540+
node.attrs().nth((*invoc_attr_index) as usize).unwrap_or_else(
541+
|| panic!("cannot find attribute #{}", invoc_attr_index),
542+
);
543+
(
544+
ast_id.file_id,
545+
SyntaxNodePtr::from(AstPtr::new(&attr)),
546+
Some(attr_name.clone()),
547+
)
548+
}
537549
};
538550
sink.push(UnresolvedProcMacro {
539551
file,
@@ -558,7 +570,9 @@ impl Module {
558570
let node = ast_id.to_node(db.upcast());
559571
(ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)))
560572
}
561-
MacroCallKind::Derive { ast_id, .. } => {
573+
MacroCallKind::Derive { ast_id, .. }
574+
| MacroCallKind::Attr { ast_id, .. } => {
575+
// FIXME: point to the attribute instead, this creates very large diagnostics
562576
let node = ast_id.to_node(db.upcast());
563577
(ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)))
564578
}

crates/hir_def/src/lib.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ use std::{
5555
sync::Arc,
5656
};
5757

58+
use attr::Attr;
5859
use base_db::{impl_intern_key, salsa, CrateId};
5960
use hir_expand::{
6061
ast_id_map::FileAstId,
@@ -768,3 +769,42 @@ fn derive_macro_as_call_id(
768769
.into();
769770
Ok(res)
770771
}
772+
773+
fn attr_macro_as_call_id(
774+
item_attr: &AstIdWithPath<ast::Item>,
775+
macro_attr: &Attr,
776+
db: &dyn db::DefDatabase,
777+
krate: CrateId,
778+
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
779+
) -> Result<MacroCallId, UnresolvedMacro> {
780+
let def: MacroDefId = resolver(item_attr.path.clone())
781+
.ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?;
782+
let last_segment = item_attr
783+
.path
784+
.segments()
785+
.last()
786+
.ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?;
787+
let mut arg = match &macro_attr.input {
788+
Some(input) => match &**input {
789+
attr::AttrInput::Literal(_) => tt::Subtree::default(),
790+
attr::AttrInput::TokenTree(tt) => tt.clone(),
791+
},
792+
None => tt::Subtree::default(),
793+
};
794+
// The parentheses are always disposed here.
795+
arg.delimiter = None;
796+
797+
let res = def
798+
.as_lazy_macro(
799+
db.upcast(),
800+
krate,
801+
MacroCallKind::Attr {
802+
ast_id: item_attr.ast_id,
803+
attr_name: last_segment.to_string(),
804+
attr_args: arg,
805+
invoc_attr_index: macro_attr.id.ast_index,
806+
},
807+
)
808+
.into();
809+
Ok(res)
810+
}

crates/hir_def/src/nameres/collector.rs

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use syntax::ast;
2323

2424
use crate::{
2525
attr::{Attr, AttrId, AttrInput, Attrs},
26-
builtin_attr,
26+
attr_macro_as_call_id, builtin_attr,
2727
db::DefDatabase,
2828
derive_macro_as_call_id,
2929
intern::Interned,
@@ -223,7 +223,7 @@ struct MacroDirective {
223223
enum MacroDirectiveKind {
224224
FnLike { ast_id: AstIdWithPath<ast::MacroCall>, fragment: FragmentKind },
225225
Derive { ast_id: AstIdWithPath<ast::Item>, derive_attr: AttrId },
226-
Attr { ast_id: AstIdWithPath<ast::Item>, attr: AttrId, mod_item: ModItem },
226+
Attr { ast_id: AstIdWithPath<ast::Item>, attr: Attr, mod_item: ModItem },
227227
}
228228

229229
struct DefData<'a> {
@@ -419,7 +419,7 @@ impl DefCollector<'_> {
419419
let mut unresolved_macros = std::mem::replace(&mut self.unresolved_macros, Vec::new());
420420
let pos = unresolved_macros.iter().position(|directive| {
421421
if let MacroDirectiveKind::Attr { ast_id, mod_item, attr } = &directive.kind {
422-
self.skip_attrs.insert(ast_id.ast_id.with_value(*mod_item), *attr);
422+
self.skip_attrs.insert(ast_id.ast_id.with_value(*mod_item), attr.id);
423423

424424
let file_id = ast_id.ast_id.file_id;
425425
let item_tree = self.db.file_item_tree(file_id);
@@ -1050,7 +1050,7 @@ impl DefCollector<'_> {
10501050
let file_id = ast_id.ast_id.file_id;
10511051
let item_tree = self.db.file_item_tree(file_id);
10521052
let mod_dir = self.mod_dirs[&directive.module_id].clone();
1053-
self.skip_attrs.insert(InFile::new(file_id, *mod_item), *attr);
1053+
self.skip_attrs.insert(InFile::new(file_id, *mod_item), attr.id);
10541054
ModCollector {
10551055
def_collector: &mut *self,
10561056
macro_depth: directive.depth,
@@ -1068,7 +1068,51 @@ impl DefCollector<'_> {
10681068
}
10691069

10701070
// Not resolved to a derive helper, so try to resolve as a macro.
1071-
// FIXME: not yet :)
1071+
match attr_macro_as_call_id(
1072+
ast_id,
1073+
attr,
1074+
self.db,
1075+
self.def_map.krate,
1076+
&resolver,
1077+
) {
1078+
Ok(call_id) => {
1079+
let loc: MacroCallLoc = self.db.lookup_intern_macro(call_id);
1080+
if let MacroDefKind::ProcMacro(exp, ..) = &loc.def.kind {
1081+
if exp.is_dummy() {
1082+
// Proc macros that cannot be expanded are treated as not
1083+
// resolved, in order to fall back later.
1084+
self.def_map.diagnostics.push(
1085+
DefDiagnostic::unresolved_proc_macro(
1086+
directive.module_id,
1087+
loc.kind,
1088+
),
1089+
);
1090+
1091+
let file_id = ast_id.ast_id.file_id;
1092+
let item_tree = self.db.file_item_tree(file_id);
1093+
let mod_dir = self.mod_dirs[&directive.module_id].clone();
1094+
self.skip_attrs
1095+
.insert(InFile::new(file_id, *mod_item), attr.id);
1096+
ModCollector {
1097+
def_collector: &mut *self,
1098+
macro_depth: directive.depth,
1099+
module_id: directive.module_id,
1100+
file_id,
1101+
item_tree: &item_tree,
1102+
mod_dir,
1103+
}
1104+
.collect(&[*mod_item]);
1105+
1106+
// Remove the macro directive.
1107+
return false;
1108+
}
1109+
}
1110+
resolved.push((directive.module_id, call_id, directive.depth));
1111+
res = ReachedFixedPoint::No;
1112+
return false;
1113+
}
1114+
Err(UnresolvedMacro { .. }) => (),
1115+
}
10721116
}
10731117
}
10741118

@@ -1628,7 +1672,7 @@ impl ModCollector<'_, '_> {
16281672
self.def_collector.unresolved_macros.push(MacroDirective {
16291673
module_id: self.module_id,
16301674
depth: self.macro_depth + 1,
1631-
kind: MacroDirectiveKind::Attr { ast_id, attr: attr.id, mod_item },
1675+
kind: MacroDirectiveKind::Attr { ast_id, attr: attr.clone(), mod_item },
16321676
});
16331677

16341678
return Err(());

crates/hir_expand/src/db.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ use syntax::{
1313

1414
use crate::{
1515
ast_id_map::AstIdMap, hygiene::HygieneFrame, input::process_macro_input, BuiltinDeriveExpander,
16-
BuiltinFnLikeExpander, HirFileId, HirFileIdRepr, MacroCallId, MacroCallLoc, MacroDefId,
17-
MacroDefKind, MacroFile, ProcMacroExpander,
16+
BuiltinFnLikeExpander, HirFileId, HirFileIdRepr, MacroCallId, MacroCallKind, MacroCallLoc,
17+
MacroDefId, MacroDefKind, MacroFile, ProcMacroExpander,
1818
};
1919

2020
/// Total limit on the number of tokens produced by any macro invocation.
@@ -377,7 +377,12 @@ fn expand_proc_macro(
377377
_ => unreachable!(),
378378
};
379379

380-
expander.expand(db, loc.krate, &macro_arg.0)
380+
let attr_arg = match &loc.kind {
381+
MacroCallKind::Attr { attr_args, .. } => Some(attr_args),
382+
_ => None,
383+
};
384+
385+
expander.expand(db, loc.krate, &macro_arg.0, attr_arg)
381386
}
382387

383388
fn is_self_replicating(from: &SyntaxNode, to: &SyntaxNode) -> bool {

crates/hir_expand/src/input.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@ pub(crate) fn process_macro_input(
2828

2929
remove_derives_up_to(item, derive_attr_index as usize).syntax().clone()
3030
}
31+
MacroCallKind::Attr { invoc_attr_index, .. } => {
32+
let item = match ast::Item::cast(node.clone()) {
33+
Some(item) => item,
34+
None => return node,
35+
};
36+
37+
remove_attr_invoc(item, invoc_attr_index as usize).syntax().clone()
38+
}
3139
}
3240
}
3341

@@ -46,6 +54,17 @@ fn remove_derives_up_to(item: ast::Item, attr_index: usize) -> ast::Item {
4654
item
4755
}
4856

57+
/// Removes the attribute invoking an attribute macro from `item`.
58+
fn remove_attr_invoc(item: ast::Item, attr_index: usize) -> ast::Item {
59+
let item = item.clone_for_update();
60+
let attr = item
61+
.attrs()
62+
.nth(attr_index)
63+
.unwrap_or_else(|| panic!("cannot find attribute #{}", attr_index));
64+
attr.syntax().detach();
65+
item
66+
}
67+
4968
#[cfg(test)]
5069
mod tests {
5170
use base_db::fixture::WithFixture;

crates/hir_expand/src/lib.rs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -258,14 +258,29 @@ pub enum MacroCallKind {
258258
/// out-of-line modules, which may have attributes spread across 2 files!
259259
derive_attr_index: u32,
260260
},
261+
Attr {
262+
ast_id: AstId<ast::Item>,
263+
attr_name: String,
264+
attr_args: tt::Subtree,
265+
/// Syntactical index of the invoking `#[attribute]`.
266+
///
267+
/// Outer attributes are counted first, then inner attributes. This does not support
268+
/// out-of-line modules, which may have attributes spread across 2 files!
269+
invoc_attr_index: u32,
270+
},
261271
}
262272

273+
// FIXME: attribute indices do not account for `cfg_attr`, which means that we'll strip the whole
274+
// `cfg_attr` instead of just one of the attributes it expands to
275+
263276
impl MacroCallKind {
264277
/// Returns the file containing the macro invocation.
265278
fn file_id(&self) -> HirFileId {
266279
match self {
267280
MacroCallKind::FnLike { ast_id, .. } => ast_id.file_id,
268-
MacroCallKind::Derive { ast_id, .. } => ast_id.file_id,
281+
MacroCallKind::Derive { ast_id, .. } | MacroCallKind::Attr { ast_id, .. } => {
282+
ast_id.file_id
283+
}
269284
}
270285
}
271286

@@ -274,7 +289,7 @@ impl MacroCallKind {
274289
MacroCallKind::FnLike { ast_id, .. } => {
275290
ast_id.with_value(ast_id.to_node(db).syntax().clone())
276291
}
277-
MacroCallKind::Derive { ast_id, .. } => {
292+
MacroCallKind::Derive { ast_id, .. } | MacroCallKind::Attr { ast_id, .. } => {
278293
ast_id.with_value(ast_id.to_node(db).syntax().clone())
279294
}
280295
}
@@ -285,14 +300,17 @@ impl MacroCallKind {
285300
MacroCallKind::FnLike { ast_id, .. } => {
286301
Some(ast_id.to_node(db).token_tree()?.syntax().clone())
287302
}
288-
MacroCallKind::Derive { ast_id, .. } => Some(ast_id.to_node(db).syntax().clone()),
303+
MacroCallKind::Derive { ast_id, .. } | MacroCallKind::Attr { ast_id, .. } => {
304+
Some(ast_id.to_node(db).syntax().clone())
305+
}
289306
}
290307
}
291308

292309
fn fragment_kind(&self) -> FragmentKind {
293310
match self {
294311
MacroCallKind::FnLike { fragment, .. } => *fragment,
295312
MacroCallKind::Derive { .. } => FragmentKind::Items,
313+
MacroCallKind::Attr { .. } => FragmentKind::Items, // is this always correct?
296314
}
297315
}
298316
}

crates/hir_expand/src/proc_macro.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,16 @@ impl ProcMacroExpander {
2828
Self { krate, proc_macro_id: None }
2929
}
3030

31+
pub fn is_dummy(&self) -> bool {
32+
self.proc_macro_id.is_none()
33+
}
34+
3135
pub fn expand(
3236
self,
3337
db: &dyn AstDatabase,
3438
calling_crate: CrateId,
3539
tt: &tt::Subtree,
40+
attr_arg: Option<&tt::Subtree>,
3641
) -> Result<tt::Subtree, mbe::ExpandError> {
3742
match self.proc_macro_id {
3843
Some(id) => {
@@ -46,7 +51,7 @@ impl ProcMacroExpander {
4651
// Proc macros have access to the environment variables of the invoking crate.
4752
let env = &krate_graph[calling_crate].env;
4853

49-
proc_macro.expander.expand(&tt, None, &env).map_err(mbe::ExpandError::from)
54+
proc_macro.expander.expand(&tt, attr_arg, &env).map_err(mbe::ExpandError::from)
5055
}
5156
None => Err(mbe::ExpandError::UnresolvedProcMacro),
5257
}

0 commit comments

Comments
 (0)