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