1 use crate::base::{self, *};
2 use crate::proc_macro_server;
4 use rustc_ast::ast::{self, ItemKind, MetaItemKind, NestedMetaItem};
6 use rustc_ast::tokenstream::{TokenStream, TokenTree};
7 use rustc_data_structures::sync::Lrc;
8 use rustc_errors::{Applicability, ErrorReported};
9 use rustc_parse::nt_to_tokenstream;
10 use rustc_span::symbol::sym;
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<'_>,
25 ) -> Result<TokenStream, ErrorReported> {
26 let server = proc_macro_server::Rustc::new(ecx);
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));
38 pub struct AttrProcMacro {
39 pub client: pm::bridge::client::Client<fn(pm::TokenStream, pm::TokenStream) -> pm::TokenStream>,
42 impl base::AttrProcMacro for AttrProcMacro {
45 ecx: &'cx mut ExtCtxt<'_>,
47 annotation: TokenStream,
48 annotated: TokenStream,
49 ) -> Result<TokenStream, ErrorReported> {
50 let server = proc_macro_server::Rustc::new(ecx);
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));
62 pub struct ProcMacroDerive {
63 pub client: pm::bridge::client::Client<fn(pm::TokenStream) -> pm::TokenStream>,
66 impl MultiItemModifier for ProcMacroDerive {
69 ecx: &mut ExtCtxt<'_>,
71 _meta_item: &ast::MetaItem,
73 ) -> ExpandResult<Vec<Annotatable>, Annotatable> {
74 let item = match item {
76 | Annotatable::Field(..)
77 | Annotatable::FieldPat(..)
78 | Annotatable::GenericParam(..)
79 | Annotatable::Param(..)
80 | Annotatable::StructField(..)
81 | Annotatable::Variant(..) => panic!("unexpected annotatable"),
82 Annotatable::Item(item) => item,
83 Annotatable::ImplItem(_)
84 | Annotatable::TraitItem(_)
85 | Annotatable::ForeignItem(_)
86 | Annotatable::Stmt(_)
87 | Annotatable::Expr(_) => {
90 "proc-macro derives may only be applied to a struct, enum, or union",
92 return ExpandResult::Ready(Vec::new());
96 ItemKind::Struct(..) | ItemKind::Enum(..) | ItemKind::Union(..) => {}
100 "proc-macro derives may only be applied to a struct, enum, or union",
102 return ExpandResult::Ready(Vec::new());
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()
110 nt_to_tokenstream(&item, &ecx.sess.parse_sess, DUMMY_SP)
113 let server = proc_macro_server::Rustc::new(ecx);
114 let stream = match self.client.run(&EXEC_STRATEGY, server, input) {
115 Ok(stream) => stream,
117 let mut err = ecx.struct_span_err(span, "proc-macro derive panicked");
118 if let Some(s) = e.as_str() {
119 err.help(&format!("message: {}", s));
122 return ExpandResult::Ready(vec![]);
126 let error_count_before = ecx.sess.parse_sess.span_diagnostic.err_count();
128 rustc_parse::stream_to_parser(&ecx.sess.parse_sess, stream, Some("proc-macro derive"));
129 let mut items = vec![];
132 match parser.parse_item() {
134 Ok(Some(item)) => items.push(Annotatable::Item(item)),
142 // fail if there have been errors emitted
143 if ecx.sess.parse_sess.span_diagnostic.err_count() > error_count_before {
144 ecx.struct_span_err(span, "proc-macro derive produced unparseable tokens").emit();
147 ExpandResult::Ready(items)
151 crate fn collect_derives(cx: &mut ExtCtxt<'_>, attrs: &mut Vec<ast::Attribute>) -> Vec<ast::Path> {
152 let mut result = Vec::new();
153 attrs.retain(|attr| {
154 if !attr.has_name(sym::derive) {
158 // 1) First let's ensure that it's a meta item.
159 let nmis = match attr.meta_item_list() {
161 cx.struct_span_err(attr.span, "malformed `derive` attribute input")
164 "missing traits to be derived",
165 "#[derive(Trait1, Trait2, ...)]".to_owned(),
166 Applicability::HasPlaceholders,
174 let mut error_reported_filter_map = false;
175 let mut error_reported_map = false;
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`")
187 NestedMetaItem::MetaItem(mi) => Some(mi),
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.
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)
202 Applicability::MachineApplicable,
207 MetaItemKind::List(..) => traits_dont_accept(
208 "traits in `#[derive(...)]` don't accept arguments",
209 "remove the arguments",
211 MetaItemKind::NameValue(..) => traits_dont_accept(
212 "traits in `#[derive(...)]` don't accept values",
215 MetaItemKind::Word => {}
220 result.extend(traits);
221 !error_reported_filter_map && !error_reported_map