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