Move derive macro expansion into the MacroExpander · rust-lang/rust@b117bee (original) (raw)
``
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 Vecast::Attribute,
`
``
92
`+
derive_type: DeriveType) -> Optionast::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 Vecast::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 Vecast::Attribute)
`
``
186
`+
-> Optionast::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
`+
}
`