]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_expand/src/proc_macro.rs
Auto merge of #80790 - JohnTitor:rollup-js1noez, r=JohnTitor
[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(&item, &ecx.sess.parse_sess, CanSynthesizeMissingTokens::Yes)
98         };
99
100         let server = proc_macro_server::Rustc::new(ecx);
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![]);
111                 }
112             };
113
114         let error_count_before = ecx.sess.parse_sess.span_diagnostic.err_count();
115         let mut parser =
116             rustc_parse::stream_to_parser(&ecx.sess.parse_sess, stream, Some("proc-macro derive"));
117         let mut items = vec![];
118
119         loop {
120             match parser.parse_item() {
121                 Ok(None) => break,
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                 }
129                 Err(mut err) => {
130                     err.emit();
131                     break;
132                 }
133             }
134         }
135
136         // fail if there have been errors emitted
137         if ecx.sess.parse_sess.span_diagnostic.err_count() > error_count_before {
138             ecx.struct_span_err(span, "proc-macro derive produced unparseable tokens").emit();
139         }
140
141         ExpandResult::Ready(items)
142     }
143 }
144
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| {
148         if !attr.has_name(sym::derive) {
149             return true;
150         }
151
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;
164             }
165             Some(x) => x,
166         };
167
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;
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();
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
229     });
230     result
231 }