Skip to content

Commit 0b0531c

Browse files
authored
Rollup merge of rust-lang#39442 - keeperofdakeys:expand-derives, r=jseyfried
Expand derive macros in the MacroExpander This removes the expand_derives function, and sprinkles the functionality throughout the Invocation Collector, Expander and Resolver. r? @jseyfried
2 parents f7af8a5 + b117bee commit 0b0531c

22 files changed

+418
-291
lines changed

src/librustc_metadata/creader.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -577,7 +577,7 @@ impl<'a> CrateLoader<'a> {
577577
use proc_macro::TokenStream;
578578
use proc_macro::__internal::Registry;
579579
use rustc_back::dynamic_lib::DynamicLibrary;
580-
use syntax_ext::deriving::custom::CustomDerive;
580+
use syntax_ext::deriving::custom::ProcMacroDerive;
581581
use syntax_ext::proc_macro_impl::AttrProcMacro;
582582

583583
let path = match dylib {
@@ -609,8 +609,8 @@ impl<'a> CrateLoader<'a> {
609609
expand: fn(TokenStream) -> TokenStream,
610610
attributes: &[&'static str]) {
611611
let attrs = attributes.iter().cloned().map(Symbol::intern).collect();
612-
let derive = SyntaxExtension::CustomDerive(
613-
Box::new(CustomDerive::new(expand, attrs))
612+
let derive = SyntaxExtension::ProcMacroDerive(
613+
Box::new(ProcMacroDerive::new(expand, attrs))
614614
);
615615
self.0.push((Symbol::intern(trait_name), Rc::new(derive)));
616616
}

src/librustc_resolve/build_reduced_graph.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,7 @@ impl<'a> Resolver<'a> {
559559
"an `extern crate` loading macros must be at the crate root");
560560
} else if !self.use_extern_macros && !used &&
561561
self.session.cstore.dep_kind(module.def_id().unwrap().krate).macros_only() {
562-
let msg = "custom derive crates and `#[no_link]` crates have no effect without \
562+
let msg = "proc macro crates and `#[no_link]` crates have no effect without \
563563
`#[macro_use]`";
564564
self.session.span_warn(item.span, msg);
565565
used = true; // Avoid the normal unused extern crate warning

src/librustc_resolve/macros.rs

+26
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,32 @@ impl<'a> base::Resolver for Resolver<'a> {
250250
}
251251
result
252252
}
253+
254+
fn resolve_builtin_macro(&mut self, tname: Name) -> Result<Rc<SyntaxExtension>, Determinacy> {
255+
match self.builtin_macros.get(&tname).cloned() {
256+
Some(binding) => Ok(binding.get_macro(self)),
257+
None => Err(Determinacy::Undetermined),
258+
}
259+
}
260+
261+
fn resolve_derive_macro(&mut self, scope: Mark, path: &ast::Path, force: bool)
262+
-> Result<Rc<SyntaxExtension>, Determinacy> {
263+
let ast::Path { span, .. } = *path;
264+
match self.resolve_macro(scope, path, false) {
265+
Ok(ext) => match *ext {
266+
SyntaxExtension::BuiltinDerive(..) |
267+
SyntaxExtension::ProcMacroDerive(..) => Ok(ext),
268+
_ => Err(Determinacy::Determined),
269+
},
270+
Err(Determinacy::Undetermined) if force => {
271+
let msg = format!("cannot find derive macro `{}` in this scope", path);
272+
let mut err = self.session.struct_span_err(span, &msg);
273+
err.emit();
274+
Err(Determinacy::Determined)
275+
},
276+
Err(err) => Err(err),
277+
}
278+
}
253279
}
254280

255281
impl<'a> Resolver<'a> {

src/libsyntax/ext/base.rs

+22-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
pub use self::SyntaxExtension::{MultiDecorator, MultiModifier, NormalTT, IdentTT};
1212

13-
use ast::{self, Attribute, Name, PatKind};
13+
use ast::{self, Attribute, Name, PatKind, MetaItem};
1414
use attr::HasAttrs;
1515
use codemap::{self, CodeMap, ExpnInfo, Spanned, respan};
1616
use syntax_pos::{Span, ExpnId, NO_EXPANSION};
@@ -471,6 +471,9 @@ impl MacResult for DummyResult {
471471
}
472472
}
473473

474+
pub type BuiltinDeriveFn =
475+
for<'cx> fn(&'cx mut ExtCtxt, Span, &MetaItem, &Annotatable, &mut FnMut(Annotatable));
476+
474477
/// An enum representing the different kinds of syntax extensions.
475478
pub enum SyntaxExtension {
476479
/// A syntax extension that is attached to an item and creates new items
@@ -507,7 +510,14 @@ pub enum SyntaxExtension {
507510
///
508511
IdentTT(Box<IdentMacroExpander>, Option<Span>, bool),
509512

510-
CustomDerive(Box<MultiItemModifier>),
513+
/// An attribute-like procedural macro. TokenStream -> TokenStream.
514+
/// The input is the annotated item.
515+
/// Allows generating code to implement a Trait for a given struct
516+
/// or enum item.
517+
ProcMacroDerive(Box<MultiItemModifier>),
518+
519+
/// An attribute-like procedural macro that derives a builtin trait.
520+
BuiltinDerive(BuiltinDeriveFn),
511521
}
512522

513523
pub type NamedSyntaxExtension = (Name, SyntaxExtension);
@@ -526,6 +536,9 @@ pub trait Resolver {
526536
fn find_attr_invoc(&mut self, attrs: &mut Vec<Attribute>) -> Option<Attribute>;
527537
fn resolve_macro(&mut self, scope: Mark, path: &ast::Path, force: bool)
528538
-> Result<Rc<SyntaxExtension>, Determinacy>;
539+
fn resolve_builtin_macro(&mut self, tname: Name) -> Result<Rc<SyntaxExtension>, Determinacy>;
540+
fn resolve_derive_macro(&mut self, scope: Mark, path: &ast::Path, force: bool)
541+
-> Result<Rc<SyntaxExtension>, Determinacy>;
529542
}
530543

531544
#[derive(Copy, Clone, Debug)]
@@ -552,6 +565,13 @@ impl Resolver for DummyResolver {
552565
-> Result<Rc<SyntaxExtension>, Determinacy> {
553566
Err(Determinacy::Determined)
554567
}
568+
fn resolve_builtin_macro(&mut self, _tname: Name) -> Result<Rc<SyntaxExtension>, Determinacy> {
569+
Err(Determinacy::Determined)
570+
}
571+
fn resolve_derive_macro(&mut self, _scope: Mark, _path: &ast::Path, _force: bool)
572+
-> Result<Rc<SyntaxExtension>, Determinacy> {
573+
Err(Determinacy::Determined)
574+
}
555575
}
556576

557577
#[derive(Clone)]

src/libsyntax/ext/derive.rs

+218
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
// Copyright 2012-2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use ast::Name;
12+
use attr;
13+
use ast::{self, NestedMetaItem}; use ext::base::{ExtCtxt, SyntaxExtension};
14+
use codemap;
15+
use ext::build::AstBuilder;
16+
use feature_gate;
17+
use symbol::Symbol;
18+
use syntax_pos::Span;
19+
20+
pub fn derive_attr_trait<'a>(cx: &mut ExtCtxt, attr: &'a ast::Attribute)
21+
-> Option<&'a NestedMetaItem> {
22+
if attr.name() != "derive" {
23+
return None;
24+
}
25+
if attr.value_str().is_some() {
26+
cx.span_err(attr.span, "unexpected value in `derive`");
27+
return None;
28+
}
29+
30+
let traits = attr.meta_item_list().unwrap_or(&[]);
31+
32+
if traits.is_empty() {
33+
cx.span_warn(attr.span, "empty trait list in `derive`");
34+
return None;
35+
}
36+
37+
return traits.get(0);
38+
}
39+
40+
pub fn verify_derive_attrs(cx: &mut ExtCtxt, attrs: &[ast::Attribute]) {
41+
for attr in attrs {
42+
if attr.name() != "derive" {
43+
continue;
44+
}
45+
46+
if attr.value_str().is_some() {
47+
cx.span_err(attr.span, "unexpected value in `derive`");
48+
}
49+
50+
let traits = attr.meta_item_list().unwrap_or(&[]).to_owned();
51+
52+
if traits.is_empty() {
53+
cx.span_warn(attr.span, "empty trait list in `derive`");
54+
attr::mark_used(&attr);
55+
continue;
56+
}
57+
for titem in traits {
58+
if titem.word().is_none() {
59+
cx.span_err(titem.span, "malformed `derive` entry");
60+
}
61+
}
62+
}
63+
}
64+
65+
#[derive(PartialEq, Debug, Clone, Copy)]
66+
pub enum DeriveType {
67+
Legacy,
68+
ProcMacro,
69+
Builtin
70+
}
71+
72+
impl DeriveType {
73+
// Classify a derive trait name by resolving the macro.
74+
pub fn classify(cx: &mut ExtCtxt, tname: Name) -> DeriveType {
75+
let legacy_derive_name = Symbol::intern(&format!("derive_{}", tname));
76+
77+
if let Ok(_) = cx.resolver.resolve_builtin_macro(legacy_derive_name) {
78+
return DeriveType::Legacy;
79+
}
80+
81+
match cx.resolver.resolve_builtin_macro(tname) {
82+
Ok(ext) => match *ext {
83+
SyntaxExtension::BuiltinDerive(..) => DeriveType::Builtin,
84+
_ => DeriveType::ProcMacro,
85+
},
86+
Err(_) => DeriveType::ProcMacro,
87+
}
88+
}
89+
}
90+
91+
pub fn get_derive_attr(cx: &mut ExtCtxt, attrs: &mut Vec<ast::Attribute>,
92+
derive_type: DeriveType) -> Option<ast::Attribute> {
93+
for i in 0..attrs.len() {
94+
if attrs[i].name() != "derive" {
95+
continue;
96+
}
97+
98+
if attrs[i].value_str().is_some() {
99+
continue;
100+
}
101+
102+
let mut traits = attrs[i].meta_item_list().unwrap_or(&[]).to_owned();
103+
104+
// First, weed out malformed #[derive]
105+
traits.retain(|titem| titem.word().is_some());
106+
107+
let mut titem = None;
108+
109+
// See if we can find a matching trait.
110+
for j in 0..traits.len() {
111+
let tname = match traits[j].name() {
112+
Some(tname) => tname,
113+
_ => continue,
114+
};
115+
116+
if DeriveType::classify(cx, tname) == derive_type {
117+
titem = Some(traits.remove(j));
118+
break;
119+
}
120+
}
121+
122+
// If we find a trait, remove the trait from the attribute.
123+
if let Some(titem) = titem {
124+
if traits.len() == 0 {
125+
attrs.remove(i);
126+
} else {
127+
let derive = Symbol::intern("derive");
128+
let mitem = cx.meta_list(titem.span, derive, traits);
129+
attrs[i] = cx.attribute(titem.span, mitem);
130+
}
131+
let derive = Symbol::intern("derive");
132+
let mitem = cx.meta_list(titem.span, derive, vec![titem]);
133+
return Some(cx.attribute(mitem.span, mitem));
134+
}
135+
}
136+
return None;
137+
}
138+
139+
fn allow_unstable(cx: &mut ExtCtxt, span: Span, attr_name: &str) -> Span {
140+
Span {
141+
expn_id: cx.codemap().record_expansion(codemap::ExpnInfo {
142+
call_site: span,
143+
callee: codemap::NameAndSpan {
144+
format: codemap::MacroAttribute(Symbol::intern(attr_name)),
145+
span: Some(span),
146+
allow_internal_unstable: true,
147+
},
148+
}),
149+
..span
150+
}
151+
}
152+
153+
pub fn add_derived_markers(cx: &mut ExtCtxt, attrs: &mut Vec<ast::Attribute>) {
154+
if attrs.is_empty() {
155+
return;
156+
}
157+
158+
let titems = attrs.iter().filter(|a| {
159+
a.name() == "derive"
160+
}).flat_map(|a| {
161+
a.meta_item_list().unwrap_or(&[]).iter()
162+
}).filter_map(|titem| {
163+
titem.name()
164+
}).collect::<Vec<_>>();
165+
166+
let span = attrs[0].span;
167+
168+
if !attrs.iter().any(|a| a.name() == "structural_match") &&
169+
titems.iter().any(|t| *t == "PartialEq") && titems.iter().any(|t| *t == "Eq") {
170+
let structural_match = Symbol::intern("structural_match");
171+
let span = allow_unstable(cx, span, "derive(PartialEq, Eq)");
172+
let meta = cx.meta_word(span, structural_match);
173+
attrs.push(cx.attribute(span, meta));
174+
}
175+
176+
if !attrs.iter().any(|a| a.name() == "rustc_copy_clone_marker") &&
177+
titems.iter().any(|t| *t == "Copy") && titems.iter().any(|t| *t == "Clone") {
178+
let structural_match = Symbol::intern("rustc_copy_clone_marker");
179+
let span = allow_unstable(cx, span, "derive(Copy, Clone)");
180+
let meta = cx.meta_word(span, structural_match);
181+
attrs.push(cx.attribute(span, meta));
182+
}
183+
}
184+
185+
pub fn find_derive_attr(cx: &mut ExtCtxt, attrs: &mut Vec<ast::Attribute>)
186+
-> Option<ast::Attribute> {
187+
verify_derive_attrs(cx, attrs);
188+
get_derive_attr(cx, attrs, DeriveType::Legacy).and_then(|a| {
189+
let titem = derive_attr_trait(cx, &a);
190+
titem.and_then(|titem| {
191+
let tword = titem.word().unwrap();
192+
let tname = tword.name();
193+
if !cx.ecfg.enable_custom_derive() {
194+
feature_gate::emit_feature_err(
195+
&cx.parse_sess,
196+
"custom_derive",
197+
titem.span,
198+
feature_gate::GateIssue::Language,
199+
feature_gate::EXPLAIN_CUSTOM_DERIVE
200+
);
201+
None
202+
} else {
203+
let name = Symbol::intern(&format!("derive_{}", tname));
204+
if !cx.resolver.is_whitelisted_legacy_custom_derive(name) {
205+
cx.span_warn(titem.span,
206+
feature_gate::EXPLAIN_DEPR_CUSTOM_DERIVE);
207+
}
208+
let mitem = cx.meta_word(titem.span, name);
209+
Some(cx.attribute(mitem.span, mitem))
210+
}
211+
})
212+
}).or_else(|| {
213+
get_derive_attr(cx, attrs, DeriveType::ProcMacro)
214+
}).or_else(|| {
215+
add_derived_markers(cx, attrs);
216+
get_derive_attr(cx, attrs, DeriveType::Builtin)
217+
})
218+
}

0 commit comments

Comments
 (0)