]> git.proxmox.com Git - rustc.git/blame - src/librustc_expand/proc_macro.rs
New upstream version 1.47.0+dfsg1
[rustc.git] / src / librustc_expand / proc_macro.rs
CommitLineData
e74abb32
XL
1use crate::base::{self, *};
2use crate::proc_macro_server;
3
74b04a01 4use rustc_ast::token;
f035d41b 5use rustc_ast::tokenstream::{TokenStream, TokenTree};
3dfed10e 6use rustc_ast::{self as ast, *};
dfeec247 7use rustc_data_structures::sync::Lrc;
ba9703b0 8use rustc_errors::{Applicability, ErrorReported};
f035d41b 9use rustc_parse::nt_to_tokenstream;
dfeec247
XL
10use rustc_span::symbol::sym;
11use rustc_span::{Span, DUMMY_SP};
416331ca 12
e74abb32 13const EXEC_STRATEGY: pm::bridge::server::SameThread = pm::bridge::server::SameThread;
416331ca
XL
14
15pub struct BangProcMacro {
dfeec247 16 pub client: pm::bridge::client::Client<fn(pm::TokenStream) -> pm::TokenStream>,
416331ca
XL
17}
18
19impl 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
38pub struct AttrProcMacro {
e74abb32 39 pub client: pm::bridge::client::Client<fn(pm::TokenStream, pm::TokenStream) -> pm::TokenStream>,
416331ca
XL
40}
41
42impl 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
62pub struct ProcMacroDerive {
e74abb32 63 pub client: pm::bridge::client::Client<fn(pm::TokenStream) -> pm::TokenStream>,
416331ca
XL
64}
65
66impl 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
151crate 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}