1 use crate::base::{self, *};
2 use crate::proc_macro_server;
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};
15 const EXEC_STRATEGY: pm::bridge::server::SameThread = pm::bridge::server::SameThread;
17 pub struct BangProcMacro {
18 pub client: pm::bridge::client::Client<fn(pm::TokenStream) -> pm::TokenStream>,
21 impl base::ProcMacro for BangProcMacro {
24 ecx: &'cx mut ExtCtxt<'_>,
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));
40 pub struct AttrProcMacro {
41 pub client: pm::bridge::client::Client<fn(pm::TokenStream, pm::TokenStream) -> pm::TokenStream>,
44 impl base::AttrProcMacro for AttrProcMacro {
47 ecx: &'cx mut ExtCtxt<'_>,
49 annotation: TokenStream,
50 annotated: TokenStream,
51 ) -> Result<TokenStream, ErrorReported> {
52 let server = proc_macro_server::Rustc::new(ecx);
54 .run(&EXEC_STRATEGY, server, annotation, annotated, ecx.ecfg.proc_macro_backtrace)
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));
66 pub struct ProcMacroDerive {
67 pub client: pm::bridge::client::Client<fn(pm::TokenStream) -> pm::TokenStream>,
70 impl MultiItemModifier for ProcMacroDerive {
73 ecx: &mut ExtCtxt<'_>,
75 _meta_item: &ast::MetaItem,
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) => {
85 assert!(stmt.is_item());
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())
94 let input = if item.pretty_printing_compatibility_hack() {
95 TokenTree::token(token::Interpolated(Lrc::new(item)), DUMMY_SP).into()
97 nt_to_tokenstream(&item, &ecx.sess.parse_sess, CanSynthesizeMissingTokens::Yes)
100 let server = proc_macro_server::Rustc::new(ecx);
102 match self.client.run(&EXEC_STRATEGY, server, input, ecx.ecfg.proc_macro_backtrace) {
103 Ok(stream) => stream,
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));
110 return ExpandResult::Ready(vec![]);
114 let error_count_before = ecx.sess.parse_sess.span_diagnostic.err_count();
116 rustc_parse::stream_to_parser(&ecx.sess.parse_sess, stream, Some("proc-macro derive"));
117 let mut items = vec![];
120 match parser.parse_item() {
124 items.push(Annotatable::Stmt(P(ecx.stmt_item(span, item))));
126 items.push(Annotatable::Item(item));
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();
141 ExpandResult::Ready(items)
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) {
152 // 1) First let's ensure that it's a meta item.
153 let nmis = match attr.meta_item_list() {
155 cx.struct_span_err(attr.span, "malformed `derive` attribute input")
158 "missing traits to be derived",
159 "#[derive(Trait1, Trait2, ...)]".to_owned(),
160 Applicability::HasPlaceholders,
168 let mut error_reported_filter_map = false;
169 let mut error_reported_map = false;
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!(
180 "expected path to a trait, found literal",
182 let token = lit.token.to_string();
183 if token.starts_with('"')
185 && is_ident(&token[1..token.len() - 1])
187 err.help(&format!("try using `#[derive({})]`", &token[1..token.len() - 1]));
189 err.help("for example, write `#[derive(Debug)]` for `Debug`");
194 NestedMetaItem::MetaItem(mi) => Some(mi),
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.
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)
209 Applicability::MachineApplicable,
214 MetaItemKind::List(..) => traits_dont_accept(
215 "traits in `#[derive(...)]` don't accept arguments",
216 "remove the arguments",
218 MetaItemKind::NameValue(..) => traits_dont_accept(
219 "traits in `#[derive(...)]` don't accept values",
222 MetaItemKind::Word => {}
227 result.extend(traits);
228 !error_reported_filter_map && !error_reported_map