2019-10-16 03:59:30 -05:00
|
|
|
use crate::base::{self, *};
|
|
|
|
use crate::proc_macro_server;
|
|
|
|
|
2020-07-01 05:16:49 -05:00
|
|
|
use rustc_ast::token;
|
2020-06-14 06:30:42 -05:00
|
|
|
use rustc_ast::tokenstream::{TokenStream, TokenTree};
|
2020-04-27 12:56:11 -05:00
|
|
|
use rustc_ast::{self as ast, *};
|
2020-01-09 04:18:47 -06:00
|
|
|
use rustc_data_structures::sync::Lrc;
|
2020-10-01 11:39:47 -05:00
|
|
|
use rustc_errors::{struct_span_err, Applicability, ErrorReported};
|
2020-10-02 06:52:03 -05:00
|
|
|
use rustc_lexer::is_ident;
|
2020-06-14 06:30:42 -05:00
|
|
|
use rustc_parse::nt_to_tokenstream;
|
2020-01-01 12:30:57 -06:00
|
|
|
use rustc_span::symbol::sym;
|
2020-01-09 04:18:47 -06:00
|
|
|
use rustc_span::{Span, DUMMY_SP};
|
2019-07-18 13:02:34 -05:00
|
|
|
|
2019-10-16 03:59:30 -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,
|
2020-03-17 04:09:18 -05:00
|
|
|
) -> Result<TokenStream, ErrorReported> {
|
2019-07-18 13:02:34 -05:00
|
|
|
let server = proc_macro_server::Rustc::new(ecx);
|
2020-08-30 21:17:24 -05:00
|
|
|
self.client.run(&EXEC_STRATEGY, server, input, ecx.ecfg.proc_macro_backtrace).map_err(|e| {
|
2020-03-17 04:09:18 -05:00
|
|
|
let mut err = ecx.struct_span_err(span, "proc macro panicked");
|
|
|
|
if let Some(s) = e.as_str() {
|
|
|
|
err.help(&format!("message: {}", s));
|
2019-07-18 13:02:34 -05:00
|
|
|
}
|
2020-03-17 04:09:18 -05:00
|
|
|
err.emit();
|
|
|
|
ErrorReported
|
|
|
|
})
|
2019-07-18 13:02:34 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct AttrProcMacro {
|
2019-10-16 03:59:30 -05:00
|
|
|
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,
|
2020-03-17 04:56:00 -05:00
|
|
|
) -> Result<TokenStream, ErrorReported> {
|
2019-07-18 13:02:34 -05:00
|
|
|
let server = proc_macro_server::Rustc::new(ecx);
|
2020-08-30 21:17:24 -05:00
|
|
|
self.client
|
|
|
|
.run(&EXEC_STRATEGY, server, annotation, annotated, ecx.ecfg.proc_macro_backtrace)
|
|
|
|
.map_err(|e| {
|
|
|
|
let mut err = ecx.struct_span_err(span, "custom attribute panicked");
|
|
|
|
if let Some(s) = e.as_str() {
|
|
|
|
err.help(&format!("message: {}", s));
|
|
|
|
}
|
|
|
|
err.emit();
|
|
|
|
ErrorReported
|
|
|
|
})
|
2019-07-18 13:02:34 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct ProcMacroDerive {
|
2019-10-16 03:59:30 -05:00
|
|
|
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,
|
2020-03-09 12:50:12 -05:00
|
|
|
) -> ExpandResult<Vec<Annotatable>, Annotatable> {
|
2019-07-18 13:02:34 -05:00
|
|
|
let item = match item {
|
2020-11-18 16:55:59 -06:00
|
|
|
Annotatable::Item(item) => token::NtItem(item),
|
|
|
|
_ => unreachable!(),
|
2019-07-18 13:02:34 -05:00
|
|
|
};
|
2020-06-14 06:30:42 -05:00
|
|
|
let input = if item.pretty_printing_compatibility_hack() {
|
2020-07-01 05:16:49 -05:00
|
|
|
TokenTree::token(token::Interpolated(Lrc::new(item)), DUMMY_SP).into()
|
2020-06-14 06:30:42 -05:00
|
|
|
} else {
|
2020-07-29 20:27:50 -05:00
|
|
|
nt_to_tokenstream(&item, &ecx.sess.parse_sess, DUMMY_SP)
|
2020-06-14 06:30:42 -05:00
|
|
|
};
|
2019-07-18 13:02:34 -05:00
|
|
|
|
|
|
|
let server = proc_macro_server::Rustc::new(ecx);
|
2020-08-30 21:17:24 -05:00
|
|
|
let stream =
|
|
|
|
match self.client.run(&EXEC_STRATEGY, server, input, ecx.ecfg.proc_macro_backtrace) {
|
|
|
|
Ok(stream) => stream,
|
|
|
|
Err(e) => {
|
|
|
|
let mut err = ecx.struct_span_err(span, "proc-macro derive panicked");
|
|
|
|
if let Some(s) = e.as_str() {
|
|
|
|
err.help(&format!("message: {}", s));
|
|
|
|
}
|
|
|
|
err.emit();
|
|
|
|
return ExpandResult::Ready(vec![]);
|
2019-07-18 13:02:34 -05:00
|
|
|
}
|
2020-08-30 21:17:24 -05:00
|
|
|
};
|
2019-07-18 13:02:34 -05:00
|
|
|
|
2020-07-29 20:27:50 -05:00
|
|
|
let error_count_before = ecx.sess.parse_sess.span_diagnostic.err_count();
|
2019-12-22 16:42:04 -06:00
|
|
|
let mut parser =
|
2020-07-29 20:27:50 -05:00
|
|
|
rustc_parse::stream_to_parser(&ecx.sess.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) => {
|
2020-03-17 05:30:53 -05:00
|
|
|
err.emit();
|
|
|
|
break;
|
2019-07-18 13:02:34 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// fail if there have been errors emitted
|
2020-07-29 20:27:50 -05:00
|
|
|
if ecx.sess.parse_sess.span_diagnostic.err_count() > error_count_before {
|
2020-03-17 05:30:53 -05:00
|
|
|
ecx.struct_span_err(span, "proc-macro derive produced unparseable tokens").emit();
|
2019-07-18 13:02:34 -05:00
|
|
|
}
|
|
|
|
|
2020-03-09 12:50:12 -05:00
|
|
|
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| {
|
2019-10-23 14:33:12 -05:00
|
|
|
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;
|
2020-10-02 06:52:03 -05:00
|
|
|
let mut err = struct_span_err!(
|
2020-10-01 11:39:47 -05:00
|
|
|
cx.sess,
|
|
|
|
lit.span,
|
|
|
|
E0777,
|
|
|
|
"expected path to a trait, found literal",
|
2020-10-02 06:52:03 -05:00
|
|
|
);
|
|
|
|
let token = lit.token.to_string();
|
|
|
|
if token.starts_with('"')
|
|
|
|
&& token.len() > 2
|
|
|
|
&& is_ident(&token[1..token.len() - 1])
|
|
|
|
{
|
|
|
|
err.help(&format!("try using `#[derive({})]`", &token[1..token.len() - 1]));
|
|
|
|
} else {
|
|
|
|
err.help("for example, write `#[derive(Debug)]` for `Debug`");
|
|
|
|
}
|
|
|
|
err.emit();
|
2019-12-05 06:53:56 -06:00
|
|
|
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
|
|
|
|
}
|