Skip to content

Commit 18f6a99

Browse files
committed
Add expansion infrastructure for derive macros
1 parent 4c0bd06 commit 18f6a99

File tree

18 files changed

+319
-79
lines changed

18 files changed

+319
-79
lines changed

crates/ra_hir/src/code_model/src.rs

Lines changed: 4 additions & 1 deletion
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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 3 additions & 2 deletions
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

Lines changed: 3 additions & 1 deletion
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(Default::default, |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),

crates/ra_hir_def/src/body.rs

Lines changed: 4 additions & 2 deletions
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 66 additions & 7 deletions
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,
@@ -587,7 +619,9 @@ where
587619
.def_collector
588620
.unresolved_imports
589621
.push((self.module_id, import_id, self.raw_items[import_id].clone())),
590-
raw::RawItemKind::Def(def) => self.define_def(&self.raw_items[def]),
622+
raw::RawItemKind::Def(def) => {
623+
self.define_def(&self.raw_items[def], &item.attrs)
624+
}
591625
raw::RawItemKind::Macro(mac) => self.collect_macro(&self.raw_items[mac]),
592626
raw::RawItemKind::Impl(imp) => {
593627
let module = ModuleId {
@@ -682,10 +716,16 @@ where
682716
res
683717
}
684718

685-
fn define_def(&mut self, def: &raw::DefData) {
719+
fn define_def(&mut self, def: &raw::DefData, attrs: &Attrs) {
686720
let module = ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id };
687721
let ctx = LocationCtx::new(self.def_collector.db, module, self.file_id);
688722

723+
// FIXME: check attrs to see if this is an attribute macro invocation;
724+
// in which case we don't add the invocation, just a single attribute
725+
// macro invocation
726+
727+
self.collect_derives(attrs, def);
728+
689729
let name = def.name.clone();
690730
let def: PerNs = match def.kind {
691731
raw::DefKind::Function(ast_id) => {
@@ -736,6 +776,23 @@ where
736776
self.def_collector.update(self.module_id, None, &[(name, resolution)])
737777
}
738778

779+
fn collect_derives(&mut self, attrs: &Attrs, def: &raw::DefData) {
780+
for derive_subtree in attrs.by_key("derive").tt_values() {
781+
// for #[derive(Copy, Clone)], `derive_subtree` is the `(Copy, Clone)` subtree
782+
for tt in &derive_subtree.token_trees {
783+
let ident = match &tt {
784+
tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => ident,
785+
tt::TokenTree::Leaf(tt::Leaf::Punct(_)) => continue, // , is ok
786+
_ => continue, // anything else would be an error (which we currently ignore)
787+
};
788+
let path = Path::from_tt_ident(ident);
789+
790+
let ast_id = AstId::new(self.file_id, def.kind.ast_id());
791+
self.def_collector.unexpanded_attribute_macros.push((self.module_id, ast_id, path));
792+
}
793+
}
794+
}
795+
739796
fn collect_macro(&mut self, mac: &raw::MacroData) {
740797
let ast_id = AstId::new(self.file_id, mac.ast_id);
741798

@@ -759,8 +816,8 @@ where
759816
if is_macro_rules(&mac.path) {
760817
if let Some(name) = &mac.name {
761818
let macro_id = MacroDefId {
762-
ast_id,
763-
krate: self.def_collector.def_map.krate,
819+
ast_id: Some(ast_id),
820+
krate: Some(self.def_collector.def_map.krate),
764821
kind: MacroDefKind::Declarative,
765822
};
766823
self.def_collector.define_macro(self.module_id, name.clone(), macro_id, mac.export);
@@ -773,7 +830,8 @@ where
773830
if let Some(macro_def) = mac.path.as_ident().and_then(|name| {
774831
self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name)
775832
}) {
776-
let macro_call_id = macro_def.as_call_id(self.def_collector.db, ast_id);
833+
let macro_call_id =
834+
macro_def.as_call_id(self.def_collector.db, MacroCallKind::FnLike(ast_id));
777835

778836
self.def_collector.collect_macro_expansion(self.module_id, macro_call_id, macro_def);
779837
return;
@@ -829,6 +887,7 @@ mod tests {
829887
glob_imports: FxHashMap::default(),
830888
unresolved_imports: Vec::new(),
831889
unexpanded_macros: Vec::new(),
890+
unexpanded_attribute_macros: Vec::new(),
832891
mod_dirs: FxHashMap::default(),
833892
macro_stack_monitor: monitor,
834893
poison_macros: FxHashSet::default(),

crates/ra_hir_def/src/nameres/raw.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,21 @@ pub(super) enum DefKind {
184184
TypeAlias(FileAstId<ast::TypeAliasDef>),
185185
}
186186

187+
impl DefKind {
188+
pub fn ast_id(&self) -> FileAstId<ast::ModuleItem> {
189+
match self {
190+
DefKind::Function(it) => it.upcast(),
191+
DefKind::Struct(it) => it.upcast(),
192+
DefKind::Union(it) => it.upcast(),
193+
DefKind::Enum(it) => it.upcast(),
194+
DefKind::Const(it) => it.upcast(),
195+
DefKind::Static(it) => it.upcast(),
196+
DefKind::Trait(it) => it.upcast(),
197+
DefKind::TypeAlias(it) => it.upcast(),
198+
}
199+
}
200+
}
201+
187202
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
188203
pub(super) struct Macro(RawId);
189204
impl_arena_id!(Macro);

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

Lines changed: 24 additions & 0 deletions
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

Lines changed: 5 additions & 0 deletions
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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,16 @@ impl<N: AstNode> Hash for FileAstId<N> {
3939
}
4040
}
4141

42+
impl<N: AstNode> FileAstId<N> {
43+
// Can't make this a From implementation because of coherence
44+
pub fn upcast<M: AstNode>(self) -> FileAstId<M>
45+
where
46+
M: From<N>,
47+
{
48+
FileAstId { raw: self.raw, _ty: PhantomData }
49+
}
50+
}
51+
4252
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
4353
struct ErasedFileAstId(RawId);
4454
impl_arena_id!(ErasedFileAstId);
@@ -53,7 +63,7 @@ impl AstIdMap {
5363
pub(crate) fn from_source(node: &SyntaxNode) -> AstIdMap {
5464
assert!(node.parent().is_none());
5565
let mut res = AstIdMap { arena: Arena::default() };
56-
// By walking the tree in bread-first order we make sure that parents
66+
// By walking the tree in breadth-first order we make sure that parents
5767
// get lower ids then children. That is, adding a new child does not
5868
// change parent's id. This means that, say, adding a new function to a
5969
// trait does not change ids of top-level items, which helps caching.

0 commit comments

Comments
 (0)