blob: 520488c658676bfc07e179951269d44458e4f0a2 [file] [log] [blame]
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<fn(pm::TokenStream, pm::TokenStream) -> 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<fn(pm::TokenStream) -> pm::TokenStream>,
}
impl MultiItemModifier for ProcMacroDerive {
fn expand(&self,
ecx: &mut ExtCtxt<'_>,
span: Span,
_meta_item: &ast::MetaItem,
item: Annotatable)
-> Vec<Annotatable> {
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<ast::Attribute>) -> Vec<ast::Path> {
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
}