rust/compiler/rustc_expand/src/proc_macro.rs

146 lines
5 KiB
Rust
Raw Normal View History

use crate::base::{self, *};
use crate::proc_macro_server;
use rustc_ast as ast;
use rustc_ast::ptr::P;
2020-07-01 13:16:49 +03:00
use rustc_ast::token;
use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, TokenStream, TokenTree};
use rustc_data_structures::sync::Lrc;
use rustc_errors::ErrorGuaranteed;
use rustc_parse::nt_to_tokenstream;
use rustc_parse::parser::ForceCollect;
use rustc_span::{Span, DUMMY_SP};
2019-07-18 21:02:34 +03:00
const EXEC_STRATEGY: pm::bridge::server::SameThread = pm::bridge::server::SameThread;
2019-07-18 21:02:34 +03:00
pub struct BangProcMacro {
2019-12-22 17:42:04 -05:00
pub client: pm::bridge::client::Client<fn(pm::TokenStream) -> pm::TokenStream>,
2019-07-18 21:02:34 +03:00
}
impl base::ProcMacro for BangProcMacro {
2019-12-22 17:42:04 -05:00
fn expand<'cx>(
&self,
ecx: &'cx mut ExtCtxt<'_>,
span: Span,
input: TokenStream,
) -> Result<TokenStream, ErrorGuaranteed> {
let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace;
let server = proc_macro_server::Rustc::new(ecx);
self.client.run(&EXEC_STRATEGY, server, input, proc_macro_backtrace).map_err(|e| {
2020-03-17 10:09:18 +01: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 21:02:34 +03:00
}
2020-03-17 10:09:18 +01:00
err.emit();
ErrorGuaranteed
2020-03-17 10:09:18 +01:00
})
2019-07-18 21:02:34 +03:00
}
}
pub struct AttrProcMacro {
pub client: pm::bridge::client::Client<fn(pm::TokenStream, pm::TokenStream) -> pm::TokenStream>,
2019-07-18 21:02:34 +03:00
}
impl base::AttrProcMacro for AttrProcMacro {
2019-12-22 17:42:04 -05:00
fn expand<'cx>(
&self,
ecx: &'cx mut ExtCtxt<'_>,
span: Span,
annotation: TokenStream,
annotated: TokenStream,
) -> Result<TokenStream, ErrorGuaranteed> {
let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace;
let server = proc_macro_server::Rustc::new(ecx);
self.client
.run(&EXEC_STRATEGY, server, annotation, annotated, 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();
ErrorGuaranteed
})
2019-07-18 21:02:34 +03:00
}
}
pub struct ProcMacroDerive {
pub client: pm::bridge::client::Client<fn(pm::TokenStream) -> pm::TokenStream>,
2019-07-18 21:02:34 +03:00
}
impl MultiItemModifier for ProcMacroDerive {
2019-12-22 17:42:04 -05: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 21:02:34 +03: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 21:02:34 +03:00
};
Extend `proc_macro_back_compat` lint to `procedural-masquerade` We now lint on *any* use of `procedural-masquerade` crate. While this crate still exists, its main reverse dependency (`cssparser`) no longer depends on it. Any crates still depending off should stop doing so, as it only exists to support very old Rust versions. If a crate actually needs to support old versions of rustc via `procedural-masquerade`, then they'll just need to accept the warning until we remove it entirely (at the same time as the back-compat hack). The latest version of `procedural-masquerade` does not work with the latest rustc, but trying to check for the version seems like more trouble than it's worth. While working on this, I realized that the `proc-macro-hack` check was never actually doing anything. The corresponding enum variant in `proc-macro-hack` is named `Value` or `Nested` - it has never been called `Input`. Due to a strange Crater issue, the Crater run that tested adding this did *not* end up testing it - some of the crates that would have failed did not actually have their tests checked, making it seem as though the `proc-macro-hack` check was working. The Crater issue is being discussed at https://rust-lang.zulipchat.com/#narrow/stream/242791-t-infra/topic/Nearly.20identical.20Crater.20runs.20processed.20a.20crate.20differently/near/230406661 Despite the `proc-macro-hack` check not actually doing anything, we haven't gotten any reports from users about their build being broken. I went ahead and removed it entirely, since it's clear that no one is being affected by the `proc-macro-hack` regression in practice.
2021-03-15 15:54:25 -04:00
let input = if crate::base::pretty_printing_compatibility_hack(&item, &ecx.sess.parse_sess)
{
2020-07-01 13:16:49 +03:00
TokenTree::token(token::Interpolated(Lrc::new(item)), DUMMY_SP).into()
} else {
Implement token-based handling of attributes during expansion This PR modifies the macro expansion infrastructure to handle attributes in a fully token-based manner. As a result: * Derives macros no longer lose spans when their input is modified by eager cfg-expansion. This is accomplished by performing eager cfg-expansion on the token stream that we pass to the derive proc-macro * Inner attributes now preserve spans in all cases, including when we have multiple inner attributes in a row. This is accomplished through the following changes: * New structs `AttrAnnotatedTokenStream` and `AttrAnnotatedTokenTree` are introduced. These are very similar to a normal `TokenTree`, but they also track the position of attributes and attribute targets within the stream. They are built when we collect tokens during parsing. An `AttrAnnotatedTokenStream` is converted to a regular `TokenStream` when we invoke a macro. * Token capturing and `LazyTokenStream` are modified to work with `AttrAnnotatedTokenStream`. A new `ReplaceRange` type is introduced, which is created during the parsing of a nested AST node to make the 'outer' AST node aware of the attributes and attribute target stored deeper in the token stream. * When we need to perform eager cfg-expansion (either due to `#[derive]` or `#[cfg_eval]`), we tokenize and reparse our target, capturing additional information about the locations of `#[cfg]` and `#[cfg_attr]` attributes at any depth within the target. This is a performance optimization, allowing us to perform less work in the typical case where captured tokens never have eager cfg-expansion run.
2020-11-28 18:33:17 -05:00
nt_to_tokenstream(&item, &ecx.sess.parse_sess, CanSynthesizeMissingTokens::No)
};
2019-07-18 21:02:34 +03:00
let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace;
let server = proc_macro_server::Rustc::new(ecx);
let stream = match self.client.run(&EXEC_STRATEGY, server, input, 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));
2019-07-18 21:02:34 +03:00
}
err.emit();
return ExpandResult::Ready(vec![]);
}
};
2019-07-18 21:02:34 +03:00
let error_count_before = ecx.sess.parse_sess.span_diagnostic.err_count();
2019-12-22 17:42:04 -05:00
let mut parser =
rustc_parse::stream_to_parser(&ecx.sess.parse_sess, stream, Some("proc-macro derive"));
2019-07-18 21:02:34 +03:00
let mut items = vec![];
loop {
match parser.parse_item(ForceCollect::No) {
2019-07-18 21:02:34 +03:00
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 21:02:34 +03:00
Err(mut err) => {
err.emit();
break;
2019-07-18 21:02:34 +03: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 21:02:34 +03:00
}
ExpandResult::Ready(items)
2019-07-18 21:02:34 +03:00
}
}