]>
Commit | Line | Data |
---|---|---|
e74abb32 XL |
1 | use crate::base::{self, *}; |
2 | use crate::proc_macro_server; | |
3 | ||
74b04a01 | 4 | use rustc_ast::token; |
f035d41b | 5 | use rustc_ast::tokenstream::{TokenStream, TokenTree}; |
3dfed10e | 6 | use rustc_ast::{self as ast, *}; |
dfeec247 | 7 | use rustc_data_structures::sync::Lrc; |
ba9703b0 | 8 | use rustc_errors::{Applicability, ErrorReported}; |
f035d41b | 9 | use rustc_parse::nt_to_tokenstream; |
dfeec247 XL |
10 | use rustc_span::symbol::sym; |
11 | use rustc_span::{Span, DUMMY_SP}; | |
416331ca | 12 | |
e74abb32 | 13 | const EXEC_STRATEGY: pm::bridge::server::SameThread = pm::bridge::server::SameThread; |
416331ca XL |
14 | |
15 | pub struct BangProcMacro { | |
dfeec247 | 16 | pub client: pm::bridge::client::Client<fn(pm::TokenStream) -> pm::TokenStream>, |
416331ca XL |
17 | } |
18 | ||
19 | impl base::ProcMacro for BangProcMacro { | |
dfeec247 XL |
20 | fn expand<'cx>( |
21 | &self, | |
22 | ecx: &'cx mut ExtCtxt<'_>, | |
23 | span: Span, | |
24 | input: TokenStream, | |
ba9703b0 | 25 | ) -> Result<TokenStream, ErrorReported> { |
416331ca | 26 | let server = proc_macro_server::Rustc::new(ecx); |
ba9703b0 XL |
27 | self.client.run(&EXEC_STRATEGY, server, input).map_err(|e| { |
28 | let mut err = ecx.struct_span_err(span, "proc macro panicked"); | |
29 | if let Some(s) = e.as_str() { | |
30 | err.help(&format!("message: {}", s)); | |
416331ca | 31 | } |
ba9703b0 XL |
32 | err.emit(); |
33 | ErrorReported | |
34 | }) | |
416331ca XL |
35 | } |
36 | } | |
37 | ||
38 | pub struct AttrProcMacro { | |
e74abb32 | 39 | pub client: pm::bridge::client::Client<fn(pm::TokenStream, pm::TokenStream) -> pm::TokenStream>, |
416331ca XL |
40 | } |
41 | ||
42 | impl base::AttrProcMacro for AttrProcMacro { | |
dfeec247 XL |
43 | fn expand<'cx>( |
44 | &self, | |
45 | ecx: &'cx mut ExtCtxt<'_>, | |
46 | span: Span, | |
47 | annotation: TokenStream, | |
48 | annotated: TokenStream, | |
ba9703b0 | 49 | ) -> Result<TokenStream, ErrorReported> { |
416331ca | 50 | let server = proc_macro_server::Rustc::new(ecx); |
ba9703b0 XL |
51 | self.client.run(&EXEC_STRATEGY, server, annotation, annotated).map_err(|e| { |
52 | let mut err = ecx.struct_span_err(span, "custom attribute panicked"); | |
53 | if let Some(s) = e.as_str() { | |
54 | err.help(&format!("message: {}", s)); | |
416331ca | 55 | } |
ba9703b0 XL |
56 | err.emit(); |
57 | ErrorReported | |
58 | }) | |
416331ca XL |
59 | } |
60 | } | |
61 | ||
62 | pub struct ProcMacroDerive { | |
e74abb32 | 63 | pub client: pm::bridge::client::Client<fn(pm::TokenStream) -> pm::TokenStream>, |
416331ca XL |
64 | } |
65 | ||
66 | impl MultiItemModifier for ProcMacroDerive { | |
dfeec247 XL |
67 | fn expand( |
68 | &self, | |
69 | ecx: &mut ExtCtxt<'_>, | |
70 | span: Span, | |
71 | _meta_item: &ast::MetaItem, | |
72 | item: Annotatable, | |
ba9703b0 | 73 | ) -> ExpandResult<Vec<Annotatable>, Annotatable> { |
416331ca | 74 | let item = match item { |
dfeec247 XL |
75 | Annotatable::Arm(..) |
76 | | Annotatable::Field(..) | |
77 | | Annotatable::FieldPat(..) | |
78 | | Annotatable::GenericParam(..) | |
79 | | Annotatable::Param(..) | |
80 | | Annotatable::StructField(..) | |
81 | | Annotatable::Variant(..) => panic!("unexpected annotatable"), | |
416331ca | 82 | Annotatable::Item(item) => item, |
dfeec247 XL |
83 | Annotatable::ImplItem(_) |
84 | | Annotatable::TraitItem(_) | |
85 | | Annotatable::ForeignItem(_) | |
86 | | Annotatable::Stmt(_) | |
87 | | Annotatable::Expr(_) => { | |
88 | ecx.span_err( | |
89 | span, | |
ba9703b0 | 90 | "proc-macro derives may only be applied to a struct, enum, or union", |
dfeec247 | 91 | ); |
ba9703b0 | 92 | return ExpandResult::Ready(Vec::new()); |
416331ca XL |
93 | } |
94 | }; | |
e74abb32 | 95 | match item.kind { |
dfeec247 | 96 | ItemKind::Struct(..) | ItemKind::Enum(..) | ItemKind::Union(..) => {} |
416331ca | 97 | _ => { |
dfeec247 XL |
98 | ecx.span_err( |
99 | span, | |
ba9703b0 | 100 | "proc-macro derives may only be applied to a struct, enum, or union", |
dfeec247 | 101 | ); |
ba9703b0 | 102 | return ExpandResult::Ready(Vec::new()); |
416331ca XL |
103 | } |
104 | } | |
105 | ||
f035d41b XL |
106 | let item = token::NtItem(item); |
107 | let input = if item.pretty_printing_compatibility_hack() { | |
108 | TokenTree::token(token::Interpolated(Lrc::new(item)), DUMMY_SP).into() | |
109 | } else { | |
3dfed10e | 110 | nt_to_tokenstream(&item, &ecx.sess.parse_sess, DUMMY_SP) |
f035d41b | 111 | }; |
416331ca XL |
112 | |
113 | let server = proc_macro_server::Rustc::new(ecx); | |
114 | let stream = match self.client.run(&EXEC_STRATEGY, server, input) { | |
115 | Ok(stream) => stream, | |
116 | Err(e) => { | |
ba9703b0 | 117 | let mut err = ecx.struct_span_err(span, "proc-macro derive panicked"); |
416331ca XL |
118 | if let Some(s) = e.as_str() { |
119 | err.help(&format!("message: {}", s)); | |
120 | } | |
416331ca | 121 | err.emit(); |
ba9703b0 | 122 | return ExpandResult::Ready(vec![]); |
416331ca XL |
123 | } |
124 | }; | |
125 | ||
3dfed10e | 126 | let error_count_before = ecx.sess.parse_sess.span_diagnostic.err_count(); |
dfeec247 | 127 | let mut parser = |
3dfed10e | 128 | rustc_parse::stream_to_parser(&ecx.sess.parse_sess, stream, Some("proc-macro derive")); |
416331ca XL |
129 | let mut items = vec![]; |
130 | ||
131 | loop { | |
132 | match parser.parse_item() { | |
133 | Ok(None) => break, | |
dfeec247 | 134 | Ok(Some(item)) => items.push(Annotatable::Item(item)), |
416331ca | 135 | Err(mut err) => { |
ba9703b0 XL |
136 | err.emit(); |
137 | break; | |
416331ca XL |
138 | } |
139 | } | |
140 | } | |
141 | ||
416331ca | 142 | // fail if there have been errors emitted |
3dfed10e | 143 | if ecx.sess.parse_sess.span_diagnostic.err_count() > error_count_before { |
ba9703b0 | 144 | ecx.struct_span_err(span, "proc-macro derive produced unparseable tokens").emit(); |
416331ca XL |
145 | } |
146 | ||
ba9703b0 | 147 | ExpandResult::Ready(items) |
416331ca XL |
148 | } |
149 | } | |
150 | ||
416331ca XL |
151 | crate fn collect_derives(cx: &mut ExtCtxt<'_>, attrs: &mut Vec<ast::Attribute>) -> Vec<ast::Path> { |
152 | let mut result = Vec::new(); | |
153 | attrs.retain(|attr| { | |
60c5eb7d | 154 | if !attr.has_name(sym::derive) { |
416331ca XL |
155 | return true; |
156 | } | |
416331ca | 157 | |
60c5eb7d XL |
158 | // 1) First let's ensure that it's a meta item. |
159 | let nmis = match attr.meta_item_list() { | |
160 | None => { | |
161 | cx.struct_span_err(attr.span, "malformed `derive` attribute input") | |
162 | .span_suggestion( | |
163 | attr.span, | |
164 | "missing traits to be derived", | |
165 | "#[derive(Trait1, Trait2, ...)]".to_owned(), | |
166 | Applicability::HasPlaceholders, | |
167 | ) | |
168 | .emit(); | |
169 | return false; | |
e74abb32 | 170 | } |
60c5eb7d | 171 | Some(x) => x, |
e74abb32 XL |
172 | }; |
173 | ||
60c5eb7d XL |
174 | let mut error_reported_filter_map = false; |
175 | let mut error_reported_map = false; | |
176 | let traits = nmis | |
177 | .into_iter() | |
178 | // 2) Moreover, let's ensure we have a path and not `#[derive("foo")]`. | |
179 | .filter_map(|nmi| match nmi { | |
180 | NestedMetaItem::Literal(lit) => { | |
181 | error_reported_filter_map = true; | |
182 | cx.struct_span_err(lit.span, "expected path to a trait, found literal") | |
183 | .help("for example, write `#[derive(Debug)]` for `Debug`") | |
184 | .emit(); | |
185 | None | |
186 | } | |
187 | NestedMetaItem::MetaItem(mi) => Some(mi), | |
188 | }) | |
189 | // 3) Finally, we only accept `#[derive($path_0, $path_1, ..)]` | |
190 | // but not e.g. `#[derive($path_0 = "value", $path_1(abc))]`. | |
191 | // In this case we can still at least determine that the user | |
192 | // wanted this trait to be derived, so let's keep it. | |
193 | .map(|mi| { | |
194 | let mut traits_dont_accept = |title, action| { | |
195 | error_reported_map = true; | |
196 | let sp = mi.span.with_lo(mi.path.span.hi()); | |
197 | cx.struct_span_err(sp, title) | |
198 | .span_suggestion( | |
199 | sp, | |
200 | action, | |
201 | String::new(), | |
202 | Applicability::MachineApplicable, | |
203 | ) | |
204 | .emit(); | |
205 | }; | |
206 | match &mi.kind { | |
207 | MetaItemKind::List(..) => traits_dont_accept( | |
208 | "traits in `#[derive(...)]` don't accept arguments", | |
209 | "remove the arguments", | |
210 | ), | |
211 | MetaItemKind::NameValue(..) => traits_dont_accept( | |
212 | "traits in `#[derive(...)]` don't accept values", | |
213 | "remove the value", | |
214 | ), | |
215 | MetaItemKind::Word => {} | |
216 | } | |
217 | mi.path | |
218 | }); | |
219 | ||
220 | result.extend(traits); | |
221 | !error_reported_filter_map && !error_reported_map | |
416331ca XL |
222 | }); |
223 | result | |
224 | } |