Skip to content

Commit 34dc8d2

Browse files
committed
Add basic custom derive lowering
1 parent e2dd17f commit 34dc8d2

File tree

11 files changed

+152
-28
lines changed

11 files changed

+152
-28
lines changed

Cargo.lock

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/ra_hir_def/src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,9 @@ impl AsMacroCall for AstIdWithPath<ast::ModuleItem> {
475475
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
476476
) -> Option<MacroCallId> {
477477
let def = resolver(self.path.clone())?;
478-
Some(def.as_lazy_macro(db.upcast(), MacroCallKind::Attr(self.ast_id)).into())
478+
Some(
479+
def.as_lazy_macro(db.upcast(), MacroCallKind::Attr(self.ast_id, self.path.to_string()))
480+
.into(),
481+
)
479482
}
480483
}

crates/ra_hir_def/src/nameres/collector.rs

Lines changed: 53 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use hir_expand::{
77
builtin_derive::find_builtin_derive,
88
builtin_macro::find_builtin_macro,
99
name::{name, AsName, Name},
10+
proc_macro::ProcMacroExpander,
1011
HirFileId, MacroCallId, MacroDefId, MacroDefKind,
1112
};
1213
use ra_cfg::CfgOptions;
@@ -238,6 +239,18 @@ impl DefCollector<'_> {
238239
self.def_map.modules[module_id].scope.define_legacy_macro(name, mac);
239240
}
240241

242+
/// Define a proc macro
243+
///
244+
/// A proc macro is similar to normal macro scope, but it would not visiable in legacy textual scoped.
245+
/// And unconditionally exported.
246+
fn define_proc_macro(&mut self, name: Name, macro_: MacroDefId) {
247+
self.update(
248+
self.def_map.root,
249+
&[(name, PerNs::macros(macro_, Visibility::Public))],
250+
Visibility::Public,
251+
);
252+
}
253+
241254
/// Import macros from `#[macro_use] extern crate`.
242255
fn import_macros_from_extern_crate(
243256
&mut self,
@@ -537,8 +550,9 @@ impl DefCollector<'_> {
537550
true
538551
});
539552
attribute_macros.retain(|directive| {
540-
if let Some(call_id) =
541-
directive.ast_id.as_call_id(self.db, |path| self.resolve_attribute_macro(&path))
553+
if let Some(call_id) = directive
554+
.ast_id
555+
.as_call_id(self.db, |path| self.resolve_attribute_macro(&directive, &path))
542556
{
543557
resolved.push((directive.module_id, call_id, 0));
544558
res = ReachedFixedPoint::No;
@@ -562,9 +576,11 @@ impl DefCollector<'_> {
562576
res
563577
}
564578

565-
fn resolve_attribute_macro(&self, path: &ModPath) -> Option<MacroDefId> {
566-
// FIXME this is currently super hacky, just enough to support the
567-
// built-in derives
579+
fn resolve_attribute_macro(
580+
&self,
581+
directive: &DeriveDirective,
582+
path: &ModPath,
583+
) -> Option<MacroDefId> {
568584
if let Some(name) = path.as_ident() {
569585
// FIXME this should actually be handled with the normal name
570586
// resolution; the std lib defines built-in stubs for the derives,
@@ -573,7 +589,15 @@ impl DefCollector<'_> {
573589
return Some(def_id);
574590
}
575591
}
576-
None
592+
let resolved_res = self.def_map.resolve_path_fp_with_macro(
593+
self.db,
594+
ResolveMode::Other,
595+
directive.module_id,
596+
&path,
597+
BuiltinShadowMode::Module,
598+
);
599+
600+
resolved_res.resolved_def.take_macros()
577601
}
578602

579603
fn collect_macro_expansion(
@@ -776,8 +800,8 @@ impl ModCollector<'_, '_> {
776800
// FIXME: check attrs to see if this is an attribute macro invocation;
777801
// in which case we don't add the invocation, just a single attribute
778802
// macro invocation
779-
780803
self.collect_derives(attrs, def);
804+
self.collect_proc_macro(attrs);
781805

782806
let name = def.name.clone();
783807
let container = ContainerId::ModuleId(module);
@@ -854,6 +878,28 @@ impl ModCollector<'_, '_> {
854878
}
855879
}
856880

881+
fn collect_proc_macro(&mut self, attrs: &Attrs) {
882+
if let Some(derive_subtree) = attrs.by_key("proc_macro_derive").tt_values().next() {
883+
if let Some(tt) = derive_subtree.token_trees.get(0) {
884+
let ident = match &tt {
885+
tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => ident,
886+
_ => return, // anything else would be an error (which we currently ignore)
887+
};
888+
let name = ident.as_name();
889+
let krate = self.def_collector.def_map.krate;
890+
let expander = ProcMacroExpander::new(krate);
891+
892+
let macro_id = MacroDefId {
893+
ast_id: None,
894+
krate: Some(krate),
895+
kind: MacroDefKind::ProcMacro(expander),
896+
};
897+
898+
self.def_collector.define_proc_macro(name.clone(), macro_id);
899+
}
900+
}
901+
}
902+
857903
fn collect_macro(&mut self, mac: &raw::MacroData) {
858904
let mut ast_id = AstIdWithPath::new(self.file_id, mac.ast_id, mac.path.clone());
859905

crates/ra_hir_expand/src/builtin_derive.rs

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -229,24 +229,22 @@ fn partial_ord_expand(
229229
mod tests {
230230
use super::*;
231231
use crate::{test_db::TestDB, AstId, MacroCallId, MacroCallKind, MacroCallLoc};
232+
use name::Name;
232233
use ra_db::{fixture::WithFixture, SourceDatabase};
233234

234-
fn expand_builtin_derive(s: &str, expander: BuiltinDeriveExpander) -> String {
235+
fn expand_builtin_derive(s: &str, name: Name) -> String {
236+
let def = find_builtin_derive(&name).unwrap();
237+
235238
let (db, file_id) = TestDB::with_single_file(&s);
236239
let parsed = db.parse(file_id);
237240
let items: Vec<_> =
238241
parsed.syntax_node().descendants().filter_map(ast::ModuleItem::cast).collect();
239242

240243
let ast_id_map = db.ast_id_map(file_id.into());
241244

242-
// the first one should be a macro_rules
243-
let def =
244-
MacroDefId { krate: None, ast_id: None, kind: MacroDefKind::BuiltInDerive(expander) };
245+
let attr_id = AstId::new(file_id.into(), ast_id_map.ast_id(&items[0]));
245246

246-
let loc = MacroCallLoc {
247-
def,
248-
kind: MacroCallKind::Attr(AstId::new(file_id.into(), ast_id_map.ast_id(&items[0]))),
249-
};
247+
let loc = MacroCallLoc { def, kind: MacroCallKind::Attr(attr_id, name.to_string()) };
250248

251249
let id: MacroCallId = db.intern_macro(loc).into();
252250
let parsed = db.parse_or_expand(id.as_file()).unwrap();
@@ -263,7 +261,7 @@ mod tests {
263261
#[derive(Copy)]
264262
struct Foo;
265263
"#,
266-
BuiltinDeriveExpander::Copy,
264+
name::known::Copy,
267265
);
268266

269267
assert_eq!(expanded, "impl< >std::marker::CopyforFoo< >{}");
@@ -276,7 +274,7 @@ mod tests {
276274
#[derive(Copy)]
277275
struct Foo<A, B>;
278276
"#,
279-
BuiltinDeriveExpander::Copy,
277+
name::known::Copy,
280278
);
281279

282280
assert_eq!(
@@ -292,7 +290,7 @@ mod tests {
292290
#[derive(Copy)]
293291
struct Foo<A, B, 'a, 'b>;
294292
"#,
295-
BuiltinDeriveExpander::Copy,
293+
name::known::Copy,
296294
);
297295

298296
// We currently just ignore lifetimes
@@ -310,7 +308,7 @@ mod tests {
310308
#[derive(Clone)]
311309
struct Foo<A, B>;
312310
"#,
313-
BuiltinDeriveExpander::Clone,
311+
name::known::Clone,
314312
);
315313

316314
assert_eq!(

crates/ra_hir_expand/src/db.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,15 @@ use ra_syntax::{algo::diff, AstNode, Parse, SyntaxKind::*, SyntaxNode};
1111
use crate::{
1212
ast_id_map::AstIdMap, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallLoc, EagerMacroId,
1313
HirFileId, HirFileIdRepr, LazyMacroId, MacroCallId, MacroCallLoc, MacroDefId, MacroDefKind,
14-
MacroFile,
14+
MacroFile, ProcMacroExpander,
1515
};
1616

1717
#[derive(Debug, Clone, Eq, PartialEq)]
1818
pub enum TokenExpander {
1919
MacroRules(mbe::MacroRules),
2020
Builtin(BuiltinFnLikeExpander),
2121
BuiltinDerive(BuiltinDeriveExpander),
22+
ProcMacro(ProcMacroExpander),
2223
}
2324

2425
impl TokenExpander {
@@ -33,6 +34,7 @@ impl TokenExpander {
3334
// FIXME switch these to ExpandResult as well
3435
TokenExpander::Builtin(it) => it.expand(db, id, tt).into(),
3536
TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(),
37+
TokenExpander::ProcMacro(it) => it.expand(db, id, tt).into(),
3638
}
3739
}
3840

@@ -41,6 +43,7 @@ impl TokenExpander {
4143
TokenExpander::MacroRules(it) => it.map_id_down(id),
4244
TokenExpander::Builtin(..) => id,
4345
TokenExpander::BuiltinDerive(..) => id,
46+
TokenExpander::ProcMacro(..) => id,
4447
}
4548
}
4649

@@ -49,6 +52,7 @@ impl TokenExpander {
4952
TokenExpander::MacroRules(it) => it.map_id_up(id),
5053
TokenExpander::Builtin(..) => (id, mbe::Origin::Call),
5154
TokenExpander::BuiltinDerive(..) => (id, mbe::Origin::Call),
55+
TokenExpander::ProcMacro(..) => (id, mbe::Origin::Call),
5256
}
5357
}
5458
}
@@ -130,7 +134,10 @@ pub(crate) fn macro_def(
130134
MacroDefKind::BuiltInDerive(expander) => {
131135
Some(Arc::new((TokenExpander::BuiltinDerive(expander), mbe::TokenMap::default())))
132136
}
133-
MacroDefKind::BuiltInEager(_expander) => None,
137+
MacroDefKind::BuiltInEager(_) => None,
138+
MacroDefKind::ProcMacro(expander) => {
139+
Some(Arc::new((TokenExpander::ProcMacro(expander), mbe::TokenMap::default())))
140+
}
134141
}
135142
}
136143

crates/ra_hir_expand/src/eager.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@ fn eager_macro_recur(
112112
}
113113
MacroDefKind::Declarative
114114
| MacroDefKind::BuiltIn(_)
115-
| MacroDefKind::BuiltInDerive(_) => {
115+
| MacroDefKind::BuiltInDerive(_)
116+
| MacroDefKind::ProcMacro(_) => {
116117
let expanded = lazy_expand(db, &def, curr.with_value(child.clone()))?;
117118
// replace macro inside
118119
eager_macro_recur(db, expanded, macro_resolver)?

crates/ra_hir_expand/src/hygiene.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ impl Hygiene {
3030
MacroDefKind::BuiltIn(_) => None,
3131
MacroDefKind::BuiltInDerive(_) => None,
3232
MacroDefKind::BuiltInEager(_) => None,
33+
MacroDefKind::ProcMacro(_) => None,
3334
}
3435
}
3536
MacroCallId::EagerMacro(_id) => None,

crates/ra_hir_expand/src/lib.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ pub mod hygiene;
1111
pub mod diagnostics;
1212
pub mod builtin_derive;
1313
pub mod builtin_macro;
14+
pub mod proc_macro;
1415
pub mod quote;
1516
pub mod eager;
1617

@@ -27,6 +28,7 @@ use ra_syntax::{
2728
use crate::ast_id_map::FileAstId;
2829
use crate::builtin_derive::BuiltinDeriveExpander;
2930
use crate::builtin_macro::{BuiltinFnLikeExpander, EagerExpander};
31+
use crate::proc_macro::ProcMacroExpander;
3032

3133
#[cfg(test)]
3234
mod test_db;
@@ -217,6 +219,7 @@ pub enum MacroDefKind {
217219
// FIXME: maybe just Builtin and rename BuiltinFnLikeExpander to BuiltinExpander
218220
BuiltInDerive(BuiltinDeriveExpander),
219221
BuiltInEager(EagerExpander),
222+
ProcMacro(ProcMacroExpander),
220223
}
221224

222225
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -228,21 +231,23 @@ pub struct MacroCallLoc {
228231
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
229232
pub enum MacroCallKind {
230233
FnLike(AstId<ast::MacroCall>),
231-
Attr(AstId<ast::ModuleItem>),
234+
Attr(AstId<ast::ModuleItem>, String),
232235
}
233236

234237
impl MacroCallKind {
235238
pub fn file_id(&self) -> HirFileId {
236239
match self {
237240
MacroCallKind::FnLike(ast_id) => ast_id.file_id,
238-
MacroCallKind::Attr(ast_id) => ast_id.file_id,
241+
MacroCallKind::Attr(ast_id, _) => ast_id.file_id,
239242
}
240243
}
241244

242245
pub fn node(&self, db: &dyn db::AstDatabase) -> InFile<SyntaxNode> {
243246
match self {
244247
MacroCallKind::FnLike(ast_id) => ast_id.with_value(ast_id.to_node(db).syntax().clone()),
245-
MacroCallKind::Attr(ast_id) => ast_id.with_value(ast_id.to_node(db).syntax().clone()),
248+
MacroCallKind::Attr(ast_id, _) => {
249+
ast_id.with_value(ast_id.to_node(db).syntax().clone())
250+
}
246251
}
247252
}
248253

@@ -251,7 +256,7 @@ impl MacroCallKind {
251256
MacroCallKind::FnLike(ast_id) => {
252257
Some(ast_id.to_node(db).token_tree()?.syntax().clone())
253258
}
254-
MacroCallKind::Attr(ast_id) => Some(ast_id.to_node(db).syntax().clone()),
259+
MacroCallKind::Attr(ast_id, _) => Some(ast_id.to_node(db).syntax().clone()),
255260
}
256261
}
257262
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//! Proc Macro Expander stub
2+
3+
use crate::{db::AstDatabase, LazyMacroId, MacroCallKind, MacroCallLoc};
4+
use ra_db::CrateId;
5+
6+
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
7+
pub struct ProcMacroExpander {
8+
krate: CrateId,
9+
}
10+
11+
impl ProcMacroExpander {
12+
pub fn new(krate: CrateId) -> ProcMacroExpander {
13+
ProcMacroExpander { krate }
14+
}
15+
16+
pub fn expand(
17+
&self,
18+
db: &dyn AstDatabase,
19+
id: LazyMacroId,
20+
_tt: &tt::Subtree,
21+
) -> Result<tt::Subtree, mbe::ExpandError> {
22+
let loc: MacroCallLoc = db.lookup_intern_macro(id);
23+
let name = match loc.kind {
24+
MacroCallKind::FnLike(_) => return Err(mbe::ExpandError::ConversionError),
25+
MacroCallKind::Attr(_, name) => name,
26+
};
27+
28+
dbg!(name);
29+
30+
unimplemented!()
31+
}
32+
}

crates/ra_hir_ty/src/tests/macros.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -639,3 +639,26 @@ mod clone {
639639
);
640640
assert_eq!("(Wrapper<S>, {unknown})", type_at_pos(&db, pos));
641641
}
642+
643+
#[test]
644+
fn infer_custom_derive_simple() {
645+
let (db, pos) = TestDB::with_position(
646+
r#"
647+
//- /main.rs crate:main deps:foo
648+
use foo::Foo;
649+
650+
#[derive(Foo)]
651+
struct S{}
652+
653+
fn test() {
654+
S{}<|>;
655+
}
656+
657+
//- /lib.rs crate:foo
658+
#[proc_macro_derive(Foo)]
659+
pub fn derive_foo(_item: TokenStream) -> TokenStream {
660+
}
661+
"#,
662+
);
663+
assert_eq!("S", type_at_pos(&db, pos));
664+
}

0 commit comments

Comments
 (0)