1 use crate::base::{self, *};
2 use crate::proc_macro_server;
4 use rustc_span::symbol::sym;
5 use syntax::ast::{self, ItemKind, MetaItemKind, NestedMetaItem};
6 use syntax::errors::{Applicability, FatalError};
8 use syntax::tokenstream::{self, TokenStream};
10 use rustc_data_structures::sync::Lrc;
11 use rustc_span::{Span, DUMMY_SP};
13 const EXEC_STRATEGY: pm::bridge::server::SameThread = pm::bridge::server::SameThread;
15 pub struct BangProcMacro {
16 pub client: pm::bridge::client::Client<fn(pm::TokenStream) -> pm::TokenStream>,
19 impl base::ProcMacro for BangProcMacro {
22 ecx: &'cx mut ExtCtxt<'_>,
26 let server = proc_macro_server::Rustc::new(ecx);
27 match self.client.run(&EXEC_STRATEGY, server, input) {
30 let msg = "proc macro panicked";
31 let mut err = ecx.struct_span_fatal(span, msg);
32 if let Some(s) = e.as_str() {
33 err.help(&format!("message: {}", s));
43 pub struct AttrProcMacro {
44 pub client: pm::bridge::client::Client<fn(pm::TokenStream, pm::TokenStream) -> pm::TokenStream>,
47 impl base::AttrProcMacro for AttrProcMacro {
50 ecx: &'cx mut ExtCtxt<'_>,
52 annotation: TokenStream,
53 annotated: TokenStream,
55 let server = proc_macro_server::Rustc::new(ecx);
56 match self.client.run(&EXEC_STRATEGY, server, annotation, annotated) {
59 let msg = "custom attribute panicked";
60 let mut err = ecx.struct_span_fatal(span, msg);
61 if let Some(s) = e.as_str() {
62 err.help(&format!("message: {}", s));
72 pub struct ProcMacroDerive {
73 pub client: pm::bridge::client::Client<fn(pm::TokenStream) -> pm::TokenStream>,
76 impl MultiItemModifier for ProcMacroDerive {
79 ecx: &mut ExtCtxt<'_>,
81 _meta_item: &ast::MetaItem,
83 ) -> Vec<Annotatable> {
84 let item = match item {
86 | Annotatable::Field(..)
87 | Annotatable::FieldPat(..)
88 | Annotatable::GenericParam(..)
89 | Annotatable::Param(..)
90 | Annotatable::StructField(..)
91 | Annotatable::Variant(..) => panic!("unexpected annotatable"),
92 Annotatable::Item(item) => item,
93 Annotatable::ImplItem(_)
94 | Annotatable::TraitItem(_)
95 | Annotatable::ForeignItem(_)
96 | Annotatable::Stmt(_)
97 | Annotatable::Expr(_) => {
100 "proc-macro derives may only be \
101 applied to a struct, enum, or union",
107 ItemKind::Struct(..) | ItemKind::Enum(..) | ItemKind::Union(..) => {}
111 "proc-macro derives may only be \
112 applied to a struct, enum, or union",
118 let token = token::Interpolated(Lrc::new(token::NtItem(item)));
119 let input = tokenstream::TokenTree::token(token, DUMMY_SP).into();
121 let server = proc_macro_server::Rustc::new(ecx);
122 let stream = match self.client.run(&EXEC_STRATEGY, server, input) {
123 Ok(stream) => stream,
125 let msg = "proc-macro derive panicked";
126 let mut err = ecx.struct_span_fatal(span, msg);
127 if let Some(s) = e.as_str() {
128 err.help(&format!("message: {}", s));
136 let error_count_before = ecx.parse_sess.span_diagnostic.err_count();
137 let msg = "proc-macro derive produced unparseable tokens";
140 rustc_parse::stream_to_parser(ecx.parse_sess, stream, Some("proc-macro derive"));
141 let mut items = vec![];
144 match parser.parse_item() {
146 Ok(Some(item)) => items.push(Annotatable::Item(item)),
148 // FIXME: handle this better
150 ecx.struct_span_fatal(span, msg).emit();
156 // fail if there have been errors emitted
157 if ecx.parse_sess.span_diagnostic.err_count() > error_count_before {
158 ecx.struct_span_fatal(span, msg).emit();
166 crate fn collect_derives(cx: &mut ExtCtxt<'_>, attrs: &mut Vec<ast::Attribute>) -> Vec<ast::Path> {
167 let mut result = Vec::new();
168 attrs.retain(|attr| {
169 if !attr.has_name(sym::derive) {
173 // 1) First let's ensure that it's a meta item.
174 let nmis = match attr.meta_item_list() {
176 cx.struct_span_err(attr.span, "malformed `derive` attribute input")
179 "missing traits to be derived",
180 "#[derive(Trait1, Trait2, ...)]".to_owned(),
181 Applicability::HasPlaceholders,
189 let mut error_reported_filter_map = false;
190 let mut error_reported_map = false;
193 // 2) Moreover, let's ensure we have a path and not `#[derive("foo")]`.
194 .filter_map(|nmi| match nmi {
195 NestedMetaItem::Literal(lit) => {
196 error_reported_filter_map = true;
197 cx.struct_span_err(lit.span, "expected path to a trait, found literal")
198 .help("for example, write `#[derive(Debug)]` for `Debug`")
202 NestedMetaItem::MetaItem(mi) => Some(mi),
204 // 3) Finally, we only accept `#[derive($path_0, $path_1, ..)]`
205 // but not e.g. `#[derive($path_0 = "value", $path_1(abc))]`.
206 // In this case we can still at least determine that the user
207 // wanted this trait to be derived, so let's keep it.
209 let mut traits_dont_accept = |title, action| {
210 error_reported_map = true;
211 let sp = mi.span.with_lo(mi.path.span.hi());
212 cx.struct_span_err(sp, title)
217 Applicability::MachineApplicable,
222 MetaItemKind::List(..) => traits_dont_accept(
223 "traits in `#[derive(...)]` don't accept arguments",
224 "remove the arguments",
226 MetaItemKind::NameValue(..) => traits_dont_accept(
227 "traits in `#[derive(...)]` don't accept values",
230 MetaItemKind::Word => {}
235 result.extend(traits);
236 !error_reported_filter_map && !error_reported_map