Skip to content

Commit 12146d5

Browse files
committed
Add expansion infrastructure for derive macros
1 parent 4c0bd06 commit 12146d5

File tree

18 files changed

+335
-77
lines changed

18 files changed

+335
-77
lines changed

crates/ra_hir/src/code_model/src.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,10 @@ impl HasSource for TypeAlias {
105105
impl HasSource for MacroDef {
106106
type Ast = ast::MacroCall;
107107
fn source(self, db: &impl DefDatabase) -> InFile<ast::MacroCall> {
108-
InFile { file_id: self.id.ast_id.file_id, value: self.id.ast_id.to_node(db) }
108+
InFile {
109+
file_id: self.id.ast_id.expect("MacroDef without ast_id").file_id,
110+
value: self.id.ast_id.expect("MacroDef without ast_id").to_node(db),
111+
}
109112
}
110113
}
111114
impl HasSource for ImplBlock {

crates/ra_hir/src/from_source.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -152,9 +152,9 @@ impl FromSource for MacroDef {
152152

153153
let module_src = ModuleSource::from_child_node(db, src.as_ref().map(|it| it.syntax()));
154154
let module = Module::from_definition(db, InFile::new(src.file_id, module_src))?;
155-
let krate = module.krate().crate_id();
155+
let krate = Some(module.krate().crate_id());
156156

157-
let ast_id = AstId::new(src.file_id, db.ast_id_map(src.file_id).ast_id(&src.value));
157+
let ast_id = Some(AstId::new(src.file_id, db.ast_id_map(src.file_id).ast_id(&src.value)));
158158

159159
let id: MacroDefId = MacroDefId { krate, ast_id, kind };
160160
Some(MacroDef { id })

crates/ra_hir/src/source_binder.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ use hir_def::{
2020
AssocItemId, DefWithBodyId,
2121
};
2222
use hir_expand::{
23-
hygiene::Hygiene, name::AsName, AstId, HirFileId, InFile, MacroCallId, MacroFileKind,
23+
hygiene::Hygiene, name::AsName, AstId, HirFileId, InFile, MacroCallId, MacroCallKind,
24+
MacroFileKind,
2425
};
2526
use ra_syntax::{
2627
ast::{self, AstNode},
@@ -456,7 +457,7 @@ impl SourceAnalyzer {
456457
db.ast_id_map(macro_call.file_id).ast_id(macro_call.value),
457458
);
458459
Some(Expansion {
459-
macro_call_id: def.as_call_id(db, ast_id),
460+
macro_call_id: def.as_call_id(db, MacroCallKind::FnLike(ast_id)),
460461
macro_file_kind: to_macro_file_kind(macro_call.value),
461462
})
462463
}

crates/ra_hir_def/src/attr.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,9 @@ impl Attrs {
6161
AdtId::UnionId(it) => attrs_from_ast(it.lookup_intern(db).ast_id, db),
6262
},
6363
AttrDefId::TraitId(it) => attrs_from_ast(it.lookup_intern(db).ast_id, db),
64-
AttrDefId::MacroDefId(it) => attrs_from_ast(it.ast_id, db),
64+
AttrDefId::MacroDefId(it) => {
65+
it.ast_id.map_or_else(Attrs::empty, |ast_id| attrs_from_ast(ast_id, db))
66+
}
6567
AttrDefId::ImplId(it) => attrs_from_ast(it.lookup_intern(db).ast_id, db),
6668
AttrDefId::ConstId(it) => attrs_from_loc(it.lookup(db), db),
6769
AttrDefId::StaticId(it) => attrs_from_loc(it.lookup(db), db),
@@ -86,6 +88,10 @@ impl Attrs {
8688
Attrs { entries }
8789
}
8890

91+
pub(crate) fn empty() -> Attrs {
92+
Attrs { entries: None }
93+
}
94+
8995
pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> {
9096
AttrQuery { attrs: self, key }
9197
}

crates/ra_hir_def/src/body.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ pub mod scope;
66
use std::{ops::Index, sync::Arc};
77

88
use either::Either;
9-
use hir_expand::{hygiene::Hygiene, AstId, HirFileId, InFile, MacroDefId, MacroFileKind};
9+
use hir_expand::{
10+
hygiene::Hygiene, AstId, HirFileId, InFile, MacroCallKind, MacroDefId, MacroFileKind,
11+
};
1012
use ra_arena::{map::ArenaMap, Arena};
1113
use ra_syntax::{ast, AstNode, AstPtr};
1214
use rustc_hash::FxHashMap;
@@ -46,7 +48,7 @@ impl Expander {
4648

4749
if let Some(path) = macro_call.path().and_then(|path| self.parse_path(path)) {
4850
if let Some(def) = self.resolve_path_as_macro(db, &path) {
49-
let call_id = def.as_call_id(db, ast_id);
51+
let call_id = def.as_call_id(db, MacroCallKind::FnLike(ast_id));
5052
let file_id = call_id.as_file(MacroFileKind::Expr);
5153
if let Some(node) = db.parse_or_expand(file_id) {
5254
if let Some(expr) = ast::Expr::cast(node) {

crates/ra_hir_def/src/docs.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ impl Documentation {
6060
docs_from_ast(&src.value[it.local_id])
6161
}
6262
AttrDefId::TraitId(it) => docs_from_ast(&it.source(db).value),
63-
AttrDefId::MacroDefId(it) => docs_from_ast(&it.ast_id.to_node(db)),
63+
AttrDefId::MacroDefId(it) => docs_from_ast(&it.ast_id?.to_node(db)),
6464
AttrDefId::ConstId(it) => docs_from_ast(&it.lookup(db).source(db).value),
6565
AttrDefId::StaticId(it) => docs_from_ast(&it.lookup(db).source(db).value),
6666
AttrDefId::FunctionId(it) => docs_from_ast(&it.lookup(db).source(db).value),

crates/ra_hir_def/src/nameres/collector.rs

+48-5
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44
//! resolves imports and expands macros.
55
66
use hir_expand::{
7+
builtin_derive::find_builtin_derive,
78
builtin_macro::find_builtin_macro,
89
name::{self, AsName, Name},
9-
HirFileId, MacroCallId, MacroDefId, MacroDefKind, MacroFileKind,
10+
HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, MacroFileKind,
1011
};
1112
use ra_cfg::CfgOptions;
1213
use ra_db::{CrateId, FileId};
@@ -58,6 +59,7 @@ pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> C
5859
glob_imports: FxHashMap::default(),
5960
unresolved_imports: Vec::new(),
6061
unexpanded_macros: Vec::new(),
62+
unexpanded_attribute_macros: Vec::new(),
6163
mod_dirs: FxHashMap::default(),
6264
macro_stack_monitor: MacroStackMonitor::default(),
6365
poison_macros: FxHashSet::default(),
@@ -102,6 +104,7 @@ struct DefCollector<'a, DB> {
102104
glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, LocalImportId)>>,
103105
unresolved_imports: Vec<(LocalModuleId, LocalImportId, raw::ImportData)>,
104106
unexpanded_macros: Vec<(LocalModuleId, AstId<ast::MacroCall>, Path)>,
107+
unexpanded_attribute_macros: Vec<(LocalModuleId, AstId<ast::ModuleItem>, Path)>,
105108
mod_dirs: FxHashMap<LocalModuleId, ModDir>,
106109

107110
/// Some macro use `$tt:tt which mean we have to handle the macro perfectly
@@ -470,6 +473,8 @@ where
470473

471474
fn resolve_macros(&mut self) -> ReachedFixedPoint {
472475
let mut macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new());
476+
let mut attribute_macros =
477+
std::mem::replace(&mut self.unexpanded_attribute_macros, Vec::new());
473478
let mut resolved = Vec::new();
474479
let mut res = ReachedFixedPoint::Yes;
475480
macros.retain(|(module_id, ast_id, path)| {
@@ -482,7 +487,19 @@ where
482487
);
483488

484489
if let Some(def) = resolved_res.resolved_def.take_macros() {
485-
let call_id = def.as_call_id(self.db, *ast_id);
490+
let call_id = def.as_call_id(self.db, MacroCallKind::FnLike(*ast_id));
491+
resolved.push((*module_id, call_id, def));
492+
res = ReachedFixedPoint::No;
493+
return false;
494+
}
495+
496+
true
497+
});
498+
attribute_macros.retain(|(module_id, ast_id, path)| {
499+
let resolved_res = self.resolve_attribute_macro(path);
500+
501+
if let Some(def) = resolved_res {
502+
let call_id = def.as_call_id(self.db, MacroCallKind::Attr(*ast_id));
486503
resolved.push((*module_id, call_id, def));
487504
res = ReachedFixedPoint::No;
488505
return false;
@@ -492,6 +509,7 @@ where
492509
});
493510

494511
self.unexpanded_macros = macros;
512+
self.unexpanded_attribute_macros = attribute_macros;
495513

496514
for (module_id, macro_call_id, macro_def_id) in resolved {
497515
self.collect_macro_expansion(module_id, macro_call_id, macro_def_id);
@@ -500,6 +518,20 @@ where
500518
res
501519
}
502520

521+
fn resolve_attribute_macro(&self, path: &Path) -> Option<MacroDefId> {
522+
// FIXME this is currently super hacky, just enough to support the
523+
// built-in derives
524+
if let Some(name) = path.as_ident() {
525+
// FIXME this should actually be handled with the normal name
526+
// resolution; the std lib defines built-in stubs for the derives,
527+
// but these are new-style `macro`s, which we don't support yet
528+
if let Some(def_id) = find_builtin_derive(name) {
529+
return Some(def_id);
530+
}
531+
}
532+
None
533+
}
534+
503535
fn collect_macro_expansion(
504536
&mut self,
505537
module_id: LocalModuleId,
@@ -589,6 +621,9 @@ where
589621
.push((self.module_id, import_id, self.raw_items[import_id].clone())),
590622
raw::RawItemKind::Def(def) => self.define_def(&self.raw_items[def]),
591623
raw::RawItemKind::Macro(mac) => self.collect_macro(&self.raw_items[mac]),
624+
raw::RawItemKind::AttributeMacro(att) => {
625+
self.collect_attribute_macro(&self.raw_items[att])
626+
}
592627
raw::RawItemKind::Impl(imp) => {
593628
let module = ModuleId {
594629
krate: self.def_collector.def_map.krate,
@@ -759,8 +794,8 @@ where
759794
if is_macro_rules(&mac.path) {
760795
if let Some(name) = &mac.name {
761796
let macro_id = MacroDefId {
762-
ast_id,
763-
krate: self.def_collector.def_map.krate,
797+
ast_id: Some(ast_id),
798+
krate: Some(self.def_collector.def_map.krate),
764799
kind: MacroDefKind::Declarative,
765800
};
766801
self.def_collector.define_macro(self.module_id, name.clone(), macro_id, mac.export);
@@ -773,7 +808,8 @@ where
773808
if let Some(macro_def) = mac.path.as_ident().and_then(|name| {
774809
self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name)
775810
}) {
776-
let macro_call_id = macro_def.as_call_id(self.def_collector.db, ast_id);
811+
let macro_call_id =
812+
macro_def.as_call_id(self.def_collector.db, MacroCallKind::FnLike(ast_id));
777813

778814
self.def_collector.collect_macro_expansion(self.module_id, macro_call_id, macro_def);
779815
return;
@@ -788,6 +824,12 @@ where
788824
self.def_collector.unexpanded_macros.push((self.module_id, ast_id, path));
789825
}
790826

827+
fn collect_attribute_macro(&mut self, data: &raw::AttributeMacroData) {
828+
let ast_id = AstId::new(self.file_id, data.ast_id);
829+
let path = data.path.clone();
830+
self.def_collector.unexpanded_attribute_macros.push((self.module_id, ast_id, path));
831+
}
832+
791833
fn import_all_legacy_macros(&mut self, module_id: LocalModuleId) {
792834
let macros = self.def_collector.def_map[module_id].scope.legacy_macros.clone();
793835
for (name, macro_) in macros {
@@ -829,6 +871,7 @@ mod tests {
829871
glob_imports: FxHashMap::default(),
830872
unresolved_imports: Vec::new(),
831873
unexpanded_macros: Vec::new(),
874+
unexpanded_attribute_macros: Vec::new(),
832875
mod_dirs: FxHashMap::default(),
833876
macro_stack_monitor: monitor,
834877
poison_macros: FxHashSet::default(),

crates/ra_hir_def/src/nameres/raw.rs

+55
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ pub struct RawItems {
3636
imports: Arena<LocalImportId, ImportData>,
3737
defs: Arena<Def, DefData>,
3838
macros: Arena<Macro, MacroData>,
39+
attribute_macros: Arena<AttributeMacro, AttributeMacroData>,
3940
impls: Arena<Impl, ImplData>,
4041
/// items for top-level module
4142
items: Vec<RawItem>,
@@ -120,6 +121,13 @@ impl Index<Macro> for RawItems {
120121
}
121122
}
122123

124+
impl Index<AttributeMacro> for RawItems {
125+
type Output = AttributeMacroData;
126+
fn index(&self, idx: AttributeMacro) -> &AttributeMacroData {
127+
&self.attribute_macros[idx]
128+
}
129+
}
130+
123131
impl Index<Impl> for RawItems {
124132
type Output = ImplData;
125133
fn index(&self, idx: Impl) -> &ImplData {
@@ -139,6 +147,7 @@ pub(super) enum RawItemKind {
139147
Import(LocalImportId),
140148
Def(Def),
141149
Macro(Macro),
150+
AttributeMacro(AttributeMacro),
142151
Impl(Impl),
143152
}
144153

@@ -197,6 +206,18 @@ pub(super) struct MacroData {
197206
pub(super) builtin: bool,
198207
}
199208

209+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
210+
pub(super) struct AttributeMacro(RawId);
211+
impl_arena_id!(AttributeMacro);
212+
213+
#[derive(Debug, PartialEq, Eq)]
214+
pub(super) struct AttributeMacroData {
215+
pub(super) ast_id: FileAstId<ast::ModuleItem>,
216+
pub(super) path: Path,
217+
/// Whether this is a derive invocation. If so, `path` is just the trait path.
218+
pub(super) derive: bool,
219+
}
220+
200221
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
201222
pub(super) struct Impl(RawId);
202223
impl_arena_id!(Impl);
@@ -226,6 +247,12 @@ impl RawItemsCollector {
226247

227248
fn add_item(&mut self, current_module: Option<Module>, item: ast::ModuleItem) {
228249
let attrs = self.parse_attrs(&item);
250+
// FIXME: check attrs to see if this is an attribute macro invocation;
251+
// in which case we don't add the invocation, just a single attribute
252+
// macro invocation
253+
254+
// check for derives
255+
self.add_derives(current_module, &attrs, &item);
229256
let (kind, name) = match item {
230257
ast::ModuleItem::Module(module) => {
231258
self.add_module(current_module, module);
@@ -279,6 +306,34 @@ impl RawItemsCollector {
279306
}
280307
}
281308

309+
fn add_derives(
310+
&mut self,
311+
current_module: Option<Module>,
312+
attrs: &Attrs,
313+
item: &ast::ModuleItem,
314+
) {
315+
for derive_subtree in attrs.by_key("derive").tt_values() {
316+
// for #[derive(Copy, Clone)], `derive_subtree` is the `(Copy, Clone)` subtree
317+
for tt in &derive_subtree.token_trees {
318+
let ident = match &tt {
319+
tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => ident,
320+
tt::TokenTree::Leaf(tt::Leaf::Punct(_)) => continue, // , is ok
321+
_ => continue, // anything else would be an error (which we currently ignore)
322+
};
323+
let path = Path::from_tt_ident(ident);
324+
325+
let ast_id = self.source_ast_id_map.ast_id(item);
326+
327+
let m = self.raw_items.attribute_macros.alloc(AttributeMacroData {
328+
ast_id,
329+
derive: true,
330+
path,
331+
});
332+
self.push_item(current_module, attrs.clone(), RawItemKind::AttributeMacro(m));
333+
}
334+
}
335+
}
336+
282337
fn add_module(&mut self, current_module: Option<Module>, module: ast::Module) {
283338
let name = match module.name() {
284339
Some(it) => it.as_name(),

crates/ra_hir_def/src/nameres/tests/macros.rs

+24
Original file line numberDiff line numberDiff line change
@@ -600,3 +600,27 @@ fn macro_dollar_crate_is_correct_in_indirect_deps() {
600600
⋮bar: t v
601601
"###);
602602
}
603+
604+
#[test]
605+
fn expand_derive() {
606+
let map = compute_crate_def_map(
607+
"
608+
//- /main.rs
609+
#[derive(Clone)]
610+
struct Foo;
611+
",
612+
);
613+
assert_eq!(map.modules[map.root].impls.len(), 1);
614+
}
615+
616+
#[test]
617+
fn expand_multiple_derive() {
618+
let map = compute_crate_def_map(
619+
"
620+
//- /main.rs
621+
#[derive(Copy, Clone)]
622+
struct Foo;
623+
",
624+
);
625+
assert_eq!(map.modules[map.root].impls.len(), 2);
626+
}

crates/ra_hir_def/src/path.rs

+5
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,11 @@ impl Path {
199199
name_ref.as_name().into()
200200
}
201201

202+
/// Converts an `tt::Ident` into a single-identifier `Path`.
203+
pub(crate) fn from_tt_ident(ident: &tt::Ident) -> Path {
204+
ident.as_name().into()
205+
}
206+
202207
/// `true` is this path is a single identifier, like `foo`
203208
pub fn is_ident(&self) -> bool {
204209
self.kind == PathKind::Plain && self.segments.len() == 1

crates/ra_hir_expand/src/ast_id_map.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ impl AstIdMap {
5353
pub(crate) fn from_source(node: &SyntaxNode) -> AstIdMap {
5454
assert!(node.parent().is_none());
5555
let mut res = AstIdMap { arena: Arena::default() };
56-
// By walking the tree in bread-first order we make sure that parents
56+
// By walking the tree in breadth-first order we make sure that parents
5757
// get lower ids then children. That is, adding a new child does not
5858
// change parent's id. This means that, say, adding a new function to a
5959
// trait does not change ids of top-level items, which helps caching.

0 commit comments

Comments
 (0)