rust/src/librustc_expand/proc_macro.rs

239 lines
8.3 KiB
Rust
Raw Normal View History

use crate::base::{self, *};
use crate::proc_macro_server;
use rustc_ast::ast::{self, ItemKind, MetaItemKind, NestedMetaItem};
use rustc_ast::token;
use rustc_ast::tokenstream::{self, TokenStream};
use rustc_data_structures::sync::Lrc;
use rustc_errors::{Applicability, FatalError};
2020-01-01 12:30:57 -06:00
use rustc_span::symbol::sym;
use rustc_span::{Span, DUMMY_SP};
2019-07-18 13:02:34 -05:00
const EXEC_STRATEGY: pm::bridge::server::SameThread = pm::bridge::server::SameThread;
2019-07-18 13:02:34 -05:00
pub struct BangProcMacro {
2019-12-22 16:42:04 -06:00
pub client: pm::bridge::client::Client<fn(pm::TokenStream) -> pm::TokenStream>,
2019-07-18 13:02:34 -05:00
}
impl base::ProcMacro for BangProcMacro {
2019-12-22 16:42:04 -06:00
fn expand<'cx>(
&self,
ecx: &'cx mut ExtCtxt<'_>,
span: Span,
input: TokenStream,
) -> TokenStream {
2019-07-18 13:02:34 -05:00
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>,
2019-07-18 13:02:34 -05:00
}
impl base::AttrProcMacro for AttrProcMacro {
2019-12-22 16:42:04 -06:00
fn expand<'cx>(
&self,
ecx: &'cx mut ExtCtxt<'_>,
span: Span,
annotation: TokenStream,
annotated: TokenStream,
) -> TokenStream {
2019-07-18 13:02:34 -05:00
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>,
2019-07-18 13:02:34 -05:00
}
impl MultiItemModifier for ProcMacroDerive {
2019-12-22 16:42:04 -06:00
fn expand(
&self,
ecx: &mut ExtCtxt<'_>,
span: Span,
_meta_item: &ast::MetaItem,
item: Annotatable,
) -> ExpandResult<Vec<Annotatable>, Annotatable> {
2019-07-18 13:02:34 -05:00
let item = match item {
2019-12-22 16:42:04 -06:00
Annotatable::Arm(..)
| Annotatable::Field(..)
| Annotatable::FieldPat(..)
| Annotatable::GenericParam(..)
| Annotatable::Param(..)
| Annotatable::StructField(..)
| Annotatable::Variant(..) => panic!("unexpected annotatable"),
2019-07-18 13:02:34 -05:00
Annotatable::Item(item) => item,
2019-12-22 16:42:04 -06:00
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 ExpandResult::Ready(Vec::new());
2019-07-18 13:02:34 -05:00
}
};
2019-09-26 11:51:36 -05:00
match item.kind {
2019-12-22 16:42:04 -06:00
ItemKind::Struct(..) | ItemKind::Enum(..) | ItemKind::Union(..) => {}
2019-07-18 13:02:34 -05:00
_ => {
2019-12-22 16:42:04 -06:00
ecx.span_err(
span,
"proc-macro derives may only be \
applied to a struct, enum, or union",
);
return ExpandResult::Ready(Vec::new());
2019-07-18 13:02:34 -05:00
}
}
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";
2019-12-22 16:42:04 -06:00
let mut parser =
rustc_parse::stream_to_parser(ecx.parse_sess, stream, Some("proc-macro derive"));
2019-07-18 13:02:34 -05:00
let mut items = vec![];
loop {
match parser.parse_item() {
Ok(None) => break,
2019-12-22 16:42:04 -06:00
Ok(Some(item)) => items.push(Annotatable::Item(item)),
2019-07-18 13:02:34 -05:00
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();
}
ExpandResult::Ready(items)
2019-07-18 13:02:34 -05:00
}
}
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) {
2019-07-18 13:02:34 -05:00
return true;
}
2019-12-05 06:53:56 -06:00
// 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;
2019-10-09 06:19:15 -05:00
}
2019-12-05 06:53:56 -06:00
Some(x) => x,
2019-10-09 06:19:15 -05:00
};
2019-12-05 07:19:00 -06:00
let mut error_reported_filter_map = false;
let mut error_reported_map = false;
2019-12-05 06:53:56 -06:00
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) => {
2019-12-05 07:19:00 -06:00
error_reported_filter_map = true;
2019-12-05 06:53:56 -06:00
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| {
2019-12-05 07:19:00 -06:00
error_reported_map = true;
2019-12-05 06:53:56 -06:00
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);
2019-12-05 07:19:00 -06:00
!error_reported_filter_map && !error_reported_map
2019-07-18 13:02:34 -05:00
});
result
}