]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_expand/src/proc_macro.rs
New upstream version 1.50.0+dfsg1
[rustc.git] / compiler / rustc_expand / src / proc_macro.rs
CommitLineData
e74abb32
XL
1use crate::base::{self, *};
2use crate::proc_macro_server;
3
fc512014 4use rustc_ast::ptr::P;
74b04a01 5use rustc_ast::token;
f035d41b 6use rustc_ast::tokenstream::{TokenStream, TokenTree};
3dfed10e 7use rustc_ast::{self as ast, *};
dfeec247 8use rustc_data_structures::sync::Lrc;
29967ef6
XL
9use rustc_errors::{struct_span_err, Applicability, ErrorReported};
10use rustc_lexer::is_ident;
f035d41b 11use rustc_parse::nt_to_tokenstream;
dfeec247
XL
12use rustc_span::symbol::sym;
13use rustc_span::{Span, DUMMY_SP};
416331ca 14
e74abb32 15const EXEC_STRATEGY: pm::bridge::server::SameThread = pm::bridge::server::SameThread;
416331ca
XL
16
17pub struct BangProcMacro {
dfeec247 18 pub client: pm::bridge::client::Client<fn(pm::TokenStream) -> pm::TokenStream>,
416331ca
XL
19}
20
21impl 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
40pub struct AttrProcMacro {
e74abb32 41 pub client: pm::bridge::client::Client<fn(pm::TokenStream, pm::TokenStream) -> pm::TokenStream>,
416331ca
XL
42}
43
44impl 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
66pub struct ProcMacroDerive {
e74abb32 67 pub client: pm::bridge::client::Client<fn(pm::TokenStream) -> pm::TokenStream>,
416331ca
XL
68}
69
70impl 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
145crate 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}