use crate::base::{self, *}; use crate::proc_macro_server; use syntax::ast::{self, ItemKind, MetaItemKind, NestedMetaItem}; use syntax::errors::{Applicability, FatalError}; use syntax::symbol::sym; use syntax::token; use syntax::tokenstream::{self, TokenStream}; use rustc_data_structures::sync::Lrc; use syntax_pos::{Span, DUMMY_SP}; const EXEC_STRATEGY: pm::bridge::server::SameThread = pm::bridge::server::SameThread; pub struct BangProcMacro { pub client: pm::bridge::client::Client< fn(pm::TokenStream) -> pm::TokenStream, >, } impl base::ProcMacro for BangProcMacro { fn expand<'cx>(&self, ecx: &'cx mut ExtCtxt<'_>, span: Span, input: TokenStream) -> TokenStream { let server = proc_macro_server::Rustc::new(ecx); match self.client.run(&EXEC_STRATEGY, server, input) { Ok(stream) => stream, Err(e) => { let msg = "proc macro panicked"; let mut err = ecx.struct_span_fatal(span, msg); if let Some(s) = e.as_str() { err.help(&format!("message: {}", s)); } err.emit(); FatalError.raise(); } } } } pub struct AttrProcMacro { pub client: pm::bridge::client::Client pm::TokenStream>, } impl base::AttrProcMacro for AttrProcMacro { fn expand<'cx>(&self, ecx: &'cx mut ExtCtxt<'_>, span: Span, annotation: TokenStream, annotated: TokenStream) -> TokenStream { let server = proc_macro_server::Rustc::new(ecx); match self.client.run(&EXEC_STRATEGY, server, annotation, annotated) { Ok(stream) => stream, Err(e) => { let msg = "custom attribute panicked"; let mut err = ecx.struct_span_fatal(span, msg); if let Some(s) = e.as_str() { err.help(&format!("message: {}", s)); } err.emit(); FatalError.raise(); } } } } pub struct ProcMacroDerive { pub client: pm::bridge::client::Client pm::TokenStream>, } impl MultiItemModifier for ProcMacroDerive { fn expand(&self, ecx: &mut ExtCtxt<'_>, span: Span, _meta_item: &ast::MetaItem, item: Annotatable) -> Vec { let item = match item { Annotatable::Arm(..) | Annotatable::Field(..) | Annotatable::FieldPat(..) | Annotatable::GenericParam(..) | Annotatable::Param(..) | Annotatable::StructField(..) | Annotatable::Variant(..) => panic!("unexpected annotatable"), Annotatable::Item(item) => item, Annotatable::ImplItem(_) | Annotatable::TraitItem(_) | Annotatable::ForeignItem(_) | Annotatable::Stmt(_) | Annotatable::Expr(_) => { ecx.span_err(span, "proc-macro derives may only be \ applied to a struct, enum, or union"); return Vec::new() } }; match item.kind { ItemKind::Struct(..) | ItemKind::Enum(..) | ItemKind::Union(..) => {}, _ => { ecx.span_err(span, "proc-macro derives may only be \ applied to a struct, enum, or union"); return Vec::new() } } let token = token::Interpolated(Lrc::new(token::NtItem(item))); let input = tokenstream::TokenTree::token(token, DUMMY_SP).into(); let server = proc_macro_server::Rustc::new(ecx); let stream = match self.client.run(&EXEC_STRATEGY, server, input) { Ok(stream) => stream, Err(e) => { let msg = "proc-macro derive panicked"; let mut err = ecx.struct_span_fatal(span, msg); if let Some(s) = e.as_str() { err.help(&format!("message: {}", s)); } err.emit(); FatalError.raise(); } }; let error_count_before = ecx.parse_sess.span_diagnostic.err_count(); let msg = "proc-macro derive produced unparseable tokens"; let mut parser = rustc_parse::stream_to_parser( ecx.parse_sess, stream, Some("proc-macro derive"), ); let mut items = vec![]; loop { match parser.parse_item() { Ok(None) => break, Ok(Some(item)) => { items.push(Annotatable::Item(item)) } Err(mut err) => { // FIXME: handle this better err.cancel(); ecx.struct_span_fatal(span, msg).emit(); FatalError.raise(); } } } // fail if there have been errors emitted if ecx.parse_sess.span_diagnostic.err_count() > error_count_before { ecx.struct_span_fatal(span, msg).emit(); FatalError.raise(); } items } } crate fn collect_derives(cx: &mut ExtCtxt<'_>, attrs: &mut Vec) -> Vec { let mut result = Vec::new(); attrs.retain(|attr| { if !attr.has_name(sym::derive) { return true; } // 1) First let's ensure that it's a meta item. let nmis = match attr.meta_item_list() { None => { cx.struct_span_err(attr.span, "malformed `derive` attribute input") .span_suggestion( attr.span, "missing traits to be derived", "#[derive(Trait1, Trait2, ...)]".to_owned(), Applicability::HasPlaceholders, ) .emit(); return false; } Some(x) => x, }; let mut error_reported_filter_map = false; let mut error_reported_map = false; let traits = nmis .into_iter() // 2) Moreover, let's ensure we have a path and not `#[derive("foo")]`. .filter_map(|nmi| match nmi { NestedMetaItem::Literal(lit) => { error_reported_filter_map = true; cx.struct_span_err(lit.span, "expected path to a trait, found literal") .help("for example, write `#[derive(Debug)]` for `Debug`") .emit(); None } NestedMetaItem::MetaItem(mi) => Some(mi), }) // 3) Finally, we only accept `#[derive($path_0, $path_1, ..)]` // but not e.g. `#[derive($path_0 = "value", $path_1(abc))]`. // In this case we can still at least determine that the user // wanted this trait to be derived, so let's keep it. .map(|mi| { let mut traits_dont_accept = |title, action| { error_reported_map = true; let sp = mi.span.with_lo(mi.path.span.hi()); cx.struct_span_err(sp, title) .span_suggestion( sp, action, String::new(), Applicability::MachineApplicable, ) .emit(); }; match &mi.kind { MetaItemKind::List(..) => traits_dont_accept( "traits in `#[derive(...)]` don't accept arguments", "remove the arguments", ), MetaItemKind::NameValue(..) => traits_dont_accept( "traits in `#[derive(...)]` don't accept values", "remove the value", ), MetaItemKind::Word => {} } mi.path }); result.extend(traits); !error_reported_filter_map && !error_reported_map }); result }