rust/compiler/rustc_expand/src/proc_macro.rs

232 lines
8.4 KiB
Rust
Raw Normal View History

use crate::base::{self, *};
use crate::proc_macro_server;
use rustc_ast::ptr::P;
2020-07-01 05:16:49 -05:00
use rustc_ast::token;
use rustc_ast::tokenstream::{TokenStream, TokenTree};
2020-04-27 12:56:11 -05:00
use rustc_ast::{self as ast, *};
use rustc_data_structures::sync::Lrc;
use rustc_errors::{struct_span_err, Applicability, ErrorReported};
2020-10-02 06:52:03 -05:00
use rustc_lexer::is_ident;
use rustc_parse::nt_to_tokenstream;
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,
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);
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 {
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);
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 {
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> {
// We need special handling for statement items
// (e.g. `fn foo() { #[derive(Debug)] struct Bar; }`)
let mut is_stmt = false;
2019-07-18 13:02:34 -05:00
let item = match item {
Annotatable::Item(item) => token::NtItem(item),
Annotatable::Stmt(stmt) => {
is_stmt = true;
assert!(stmt.is_item());
// A proc macro can't observe the fact that we're passing
// them an `NtStmt` - it can only see the underlying tokens
// of the wrapped item
token::NtStmt(stmt.into_inner())
}
_ => unreachable!(),
2019-07-18 13:02:34 -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()
} else {
nt_to_tokenstream(&item, &ecx.sess.parse_sess, DUMMY_SP)
};
2019-07-18 13:02:34 -05:00
let server = proc_macro_server::Rustc::new(ecx);
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
}
};
2019-07-18 13:02:34 -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 =
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,
Ok(Some(item)) => {
if is_stmt {
items.push(Annotatable::Stmt(P(ecx.stmt_item(span, item))));
} else {
items.push(Annotatable::Item(item));
}
}
2019-07-18 13:02:34 -05:00
Err(mut err) => {
err.emit();
break;
2019-07-18 13:02:34 -05:00
}
}
}
// fail if there have been errors emitted
if ecx.sess.parse_sess.span_diagnostic.err_count() > error_count_before {
ecx.struct_span_err(span, "proc-macro derive produced unparseable tokens").emit();
2019-07-18 13:02:34 -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| {
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!(
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
}