Skip to content

Commit 7585127

Browse files
bors[bot]Jonas Schievink
andauthored
Merge #11208
11208: feat: expand attribute macros on impl and trait items r=jonas-schievink a=jonas-schievink fixes #11104 bors r+ Co-authored-by: Jonas Schievink <[email protected]>
2 parents c1d3b7a + 3ff68f2 commit 7585127

File tree

9 files changed

+243
-121
lines changed

9 files changed

+243
-121
lines changed

crates/hir_def/src/body.rs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ use cfg::{CfgExpr, CfgOptions};
1212
use drop_bomb::DropBomb;
1313
use either::Either;
1414
use hir_expand::{
15-
ast_id_map::AstIdMap, hygiene::Hygiene, AstId, ExpandResult, HirFileId, InFile, MacroDefId,
15+
ast_id_map::AstIdMap, hygiene::Hygiene, AstId, ExpandError, ExpandResult, HirFileId, InFile,
16+
MacroCallId, MacroDefId,
1617
};
1718
use la_arena::{Arena, ArenaMap};
1819
use limit::Limit;
@@ -124,6 +125,23 @@ impl Expander {
124125
}
125126
};
126127

128+
Ok(self.enter_expand_inner(db, call_id, err))
129+
}
130+
131+
pub fn enter_expand_id<T: ast::AstNode>(
132+
&mut self,
133+
db: &dyn DefDatabase,
134+
call_id: MacroCallId,
135+
) -> ExpandResult<Option<(Mark, T)>> {
136+
self.enter_expand_inner(db, call_id, None)
137+
}
138+
139+
fn enter_expand_inner<T: ast::AstNode>(
140+
&mut self,
141+
db: &dyn DefDatabase,
142+
call_id: MacroCallId,
143+
mut err: Option<ExpandError>,
144+
) -> ExpandResult<Option<(Mark, T)>> {
127145
if err.is_none() {
128146
err = db.macro_expand_error(call_id);
129147
}
@@ -138,17 +156,17 @@ impl Expander {
138156
tracing::warn!("no error despite `parse_or_expand` failing");
139157
}
140158

141-
return Ok(ExpandResult::only_err(err.unwrap_or_else(|| {
159+
return ExpandResult::only_err(err.unwrap_or_else(|| {
142160
mbe::ExpandError::Other("failed to parse macro invocation".into())
143-
})));
161+
}));
144162
}
145163
};
146164

147165
let node = match T::cast(raw_node) {
148166
Some(it) => it,
149167
None => {
150168
// This can happen without being an error, so only forward previous errors.
151-
return Ok(ExpandResult { value: None, err });
169+
return ExpandResult { value: None, err };
152170
}
153171
};
154172

@@ -164,7 +182,7 @@ impl Expander {
164182
self.current_file_id = file_id;
165183
self.ast_id_map = db.ast_id_map(file_id);
166184

167-
Ok(ExpandResult { value: Some((mark, node)), err })
185+
ExpandResult { value: Some((mark, node)), err }
168186
}
169187

170188
pub fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) {

crates/hir_def/src/child_by_source.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ pub trait ChildBySource {
3030
impl ChildBySource for TraitId {
3131
fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
3232
let data = db.trait_data(*self);
33+
// FIXME attribute macros
3334
for (_name, item) in data.items.iter() {
3435
match *item {
3536
AssocItemId::FunctionId(func) => {
@@ -61,6 +62,7 @@ impl ChildBySource for TraitId {
6162
impl ChildBySource for ImplId {
6263
fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
6364
let data = db.impl_data(*self);
65+
// FIXME attribute macros
6466
for &item in data.items.iter() {
6567
match item {
6668
AssocItemId::FunctionId(func) => {

crates/hir_def/src/data.rs

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,20 @@
22
33
use std::sync::Arc;
44

5-
use hir_expand::{name::Name, InFile};
5+
use hir_expand::{name::Name, AstId, ExpandResult, InFile};
66
use syntax::ast;
77

88
use crate::{
99
attr::Attrs,
10-
body::Expander,
10+
body::{Expander, Mark},
1111
db::DefDatabase,
1212
intern::Interned,
1313
item_tree::{self, AssocItem, FnFlags, ItemTreeId, ModItem, Param},
14+
nameres::attr_resolution::ResolvedAttr,
1415
type_ref::{TraitRef, TypeBound, TypeRef},
1516
visibility::RawVisibility,
16-
AssocItemId, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId, Intern,
17-
ItemContainerId, Lookup, ModuleId, StaticId, TraitId, TypeAliasId, TypeAliasLoc,
17+
AssocItemId, AstIdWithPath, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId,
18+
Intern, ItemContainerId, Lookup, ModuleId, StaticId, TraitId, TypeAliasId, TypeAliasLoc,
1819
};
1920

2021
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -348,14 +349,29 @@ fn collect_items(
348349
let item_tree = tree_id.item_tree(db);
349350
let crate_graph = db.crate_graph();
350351
let cfg_options = &crate_graph[module.krate].cfg_options;
352+
let def_map = module.def_map(db);
351353

352354
let mut items = Vec::new();
353-
for item in assoc_items {
355+
'items: for item in assoc_items {
354356
let attrs = item_tree.attrs(db, module.krate, ModItem::from(item).into());
355357
if !attrs.is_cfg_enabled(cfg_options) {
356358
continue;
357359
}
358360

361+
for attr in &*attrs {
362+
let ast_id = AstIdWithPath {
363+
path: (*attr.path).clone(),
364+
ast_id: AstId::new(expander.current_file_id(), item.ast_id(&item_tree).upcast()),
365+
};
366+
if let Ok(ResolvedAttr::Macro(call_id)) =
367+
def_map.resolve_attr_macro(db, module.local_id, ast_id, attr)
368+
{
369+
let res = expander.enter_expand_id(db, call_id);
370+
items.extend(collect_macro_items(db, module, expander, container, limit, res));
371+
continue 'items;
372+
}
373+
}
374+
359375
match item {
360376
AssocItem::Function(id) => {
361377
let item = &item_tree[id];
@@ -385,28 +401,34 @@ fn collect_items(
385401
let res = expander.enter_expand(db, call);
386402

387403
if let Ok(res) = res {
388-
if let Some((mark, mac)) = res.value {
389-
let src: InFile<ast::MacroItems> = expander.to_source(mac);
390-
let tree_id = item_tree::TreeId::new(src.file_id, None);
391-
let item_tree = tree_id.item_tree(db);
392-
let iter =
393-
item_tree.top_level_items().iter().filter_map(ModItem::as_assoc_item);
394-
items.extend(collect_items(
395-
db,
396-
module,
397-
expander,
398-
iter,
399-
tree_id,
400-
container,
401-
limit - 1,
402-
));
403-
404-
expander.exit(db, mark);
405-
}
404+
items.extend(collect_macro_items(db, module, expander, container, limit, res));
406405
}
407406
}
408407
}
409408
}
410409

411410
items
412411
}
412+
413+
fn collect_macro_items(
414+
db: &dyn DefDatabase,
415+
module: ModuleId,
416+
expander: &mut Expander,
417+
container: ItemContainerId,
418+
limit: usize,
419+
res: ExpandResult<Option<(Mark, ast::MacroItems)>>,
420+
) -> Vec<(Name, AssocItemId)> {
421+
if let Some((mark, mac)) = res.value {
422+
let src: InFile<ast::MacroItems> = expander.to_source(mac);
423+
let tree_id = item_tree::TreeId::new(src.file_id, None);
424+
let item_tree = tree_id.item_tree(db);
425+
let iter = item_tree.top_level_items().iter().filter_map(ModItem::as_assoc_item);
426+
let items = collect_items(db, module, expander, iter, tree_id, container, limit - 1);
427+
428+
expander.exit(db, mark);
429+
430+
return items;
431+
}
432+
433+
Vec::new()
434+
}

crates/hir_def/src/item_tree.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -920,6 +920,17 @@ impl From<AssocItem> for ModItem {
920920
}
921921
}
922922

923+
impl AssocItem {
924+
pub fn ast_id(self, tree: &ItemTree) -> FileAstId<ast::AssocItem> {
925+
match self {
926+
AssocItem::Function(id) => tree[id].ast_id.upcast(),
927+
AssocItem::TypeAlias(id) => tree[id].ast_id.upcast(),
928+
AssocItem::Const(id) => tree[id].ast_id.upcast(),
929+
AssocItem::MacroCall(id) => tree[id].ast_id.upcast(),
930+
}
931+
}
932+
}
933+
923934
#[derive(Debug, Eq, PartialEq)]
924935
pub struct Variant {
925936
pub name: Name,

crates/hir_def/src/lib.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -781,12 +781,10 @@ fn attr_macro_as_call_id(
781781
macro_attr: &Attr,
782782
db: &dyn db::DefDatabase,
783783
krate: CrateId,
784-
def: Option<MacroDefId>,
785-
) -> Result<MacroCallId, UnresolvedMacro> {
784+
def: MacroDefId,
785+
) -> MacroCallId {
786786
let attr_path = &item_attr.path;
787-
let def = def.ok_or_else(|| UnresolvedMacro { path: attr_path.clone() })?;
788-
let last_segment =
789-
attr_path.segments().last().ok_or_else(|| UnresolvedMacro { path: attr_path.clone() })?;
787+
let last_segment = attr_path.segments().last().expect("empty attribute path");
790788
let mut arg = match macro_attr.input.as_deref() {
791789
Some(attr::AttrInput::TokenTree(tt, map)) => (tt.clone(), map.clone()),
792790
_ => Default::default(),
@@ -805,5 +803,5 @@ fn attr_macro_as_call_id(
805803
invoc_attr_index: macro_attr.id.ast_index,
806804
},
807805
);
808-
Ok(res)
806+
res
809807
}

crates/hir_def/src/nameres.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
//! path and, upon success, we run macro expansion and "collect module" phase on
4848
//! the result
4949
50+
pub mod attr_resolution;
5051
pub mod diagnostics;
5152
mod collector;
5253
mod mod_resolution;
@@ -64,7 +65,7 @@ use la_arena::Arena;
6465
use profile::Count;
6566
use rustc_hash::FxHashMap;
6667
use stdx::format_to;
67-
use syntax::ast;
68+
use syntax::{ast, SmolStr};
6869

6970
use crate::{
7071
db::DefDatabase,
@@ -107,6 +108,11 @@ pub struct DefMap {
107108
/// (the primary purpose is to resolve derive helpers and fetch a proc-macros name)
108109
exported_proc_macros: FxHashMap<MacroDefId, ProcMacroDef>,
109110

111+
/// Custom attributes registered with `#![register_attr]`.
112+
registered_attrs: Vec<SmolStr>,
113+
/// Custom tool modules registered with `#![register_tool]`.
114+
registered_tools: Vec<SmolStr>,
115+
110116
edition: Edition,
111117
diagnostics: Vec<DefDiagnostic>,
112118
}
@@ -271,6 +277,8 @@ impl DefMap {
271277
prelude: None,
272278
root,
273279
modules,
280+
registered_attrs: Vec::new(),
281+
registered_tools: Vec::new(),
274282
diagnostics: Vec::new(),
275283
}
276284
}
@@ -443,6 +451,8 @@ impl DefMap {
443451
extern_prelude,
444452
diagnostics,
445453
modules,
454+
registered_attrs,
455+
registered_tools,
446456
block: _,
447457
edition: _,
448458
krate: _,
@@ -454,6 +464,8 @@ impl DefMap {
454464
exported_proc_macros.shrink_to_fit();
455465
diagnostics.shrink_to_fit();
456466
modules.shrink_to_fit();
467+
registered_attrs.shrink_to_fit();
468+
registered_tools.shrink_to_fit();
457469
for (_, module) in modules.iter_mut() {
458470
module.children.shrink_to_fit();
459471
module.scope.shrink_to_fit();
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
//! Post-nameres attribute resolution.
2+
3+
use hir_expand::MacroCallId;
4+
use syntax::{ast, SmolStr};
5+
6+
use crate::{
7+
attr::Attr,
8+
attr_macro_as_call_id, builtin_attr,
9+
db::DefDatabase,
10+
item_scope::BuiltinShadowMode,
11+
nameres::path_resolution::ResolveMode,
12+
path::{ModPath, PathKind},
13+
AstIdWithPath, LocalModuleId, UnresolvedMacro,
14+
};
15+
16+
use super::DefMap;
17+
18+
pub enum ResolvedAttr {
19+
/// Attribute resolved to an attribute macro.
20+
Macro(MacroCallId),
21+
/// Attribute resolved to something else that does not require expansion.
22+
Other,
23+
}
24+
25+
impl DefMap {
26+
pub(crate) fn resolve_attr_macro(
27+
&self,
28+
db: &dyn DefDatabase,
29+
original_module: LocalModuleId,
30+
ast_id: AstIdWithPath<ast::Item>,
31+
attr: &Attr,
32+
) -> Result<ResolvedAttr, UnresolvedMacro> {
33+
// NB: does not currently work for derive helpers as they aren't recorded in the `DefMap`
34+
35+
if self.is_builtin_or_registered_attr(&ast_id.path) {
36+
return Ok(ResolvedAttr::Other);
37+
}
38+
39+
let resolved_res = self.resolve_path_fp_with_macro(
40+
db,
41+
ResolveMode::Other,
42+
original_module,
43+
&ast_id.path,
44+
BuiltinShadowMode::Module,
45+
);
46+
let def = match resolved_res.resolved_def.take_macros() {
47+
Some(def) => {
48+
if def.is_attribute() {
49+
def
50+
} else {
51+
return Ok(ResolvedAttr::Other);
52+
}
53+
}
54+
None => return Err(UnresolvedMacro { path: ast_id.path.clone() }),
55+
};
56+
57+
Ok(ResolvedAttr::Macro(attr_macro_as_call_id(&ast_id, attr, db, self.krate, def)))
58+
}
59+
60+
pub(crate) fn is_builtin_or_registered_attr(&self, path: &ModPath) -> bool {
61+
if path.kind != PathKind::Plain {
62+
return false;
63+
}
64+
65+
let segments = path.segments();
66+
67+
if let Some(name) = segments.first() {
68+
let name = name.to_smol_str();
69+
let pred = |n: &_| *n == name;
70+
71+
let registered = self.registered_tools.iter().map(SmolStr::as_str);
72+
let is_tool = builtin_attr::TOOL_MODULES.iter().copied().chain(registered).any(pred);
73+
// FIXME: tool modules can be shadowed by actual modules
74+
if is_tool {
75+
return true;
76+
}
77+
78+
if segments.len() == 1 {
79+
let registered = self.registered_attrs.iter().map(SmolStr::as_str);
80+
let is_inert = builtin_attr::INERT_ATTRIBUTES
81+
.iter()
82+
.map(|it| it.name)
83+
.chain(registered)
84+
.any(pred);
85+
return is_inert;
86+
}
87+
}
88+
false
89+
}
90+
}

0 commit comments

Comments
 (0)