Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement span quoting for proc-macros #84278

Merged
merged 2 commits into from
May 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion compiler/rustc_builtin_macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use crate::deriving::*;

use rustc_expand::base::{MacroExpanderFn, ResolverExpand, SyntaxExtensionKind};
use rustc_expand::proc_macro::BangProcMacro;
use rustc_span::def_id::LOCAL_CRATE;
use rustc_span::symbol::sym;

mod asm;
Expand Down Expand Up @@ -114,5 +115,8 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
}

let client = proc_macro::bridge::client::Client::expand1(proc_macro::quote);
register(sym::quote, SyntaxExtensionKind::Bang(Box::new(BangProcMacro { client })));
register(
sym::quote,
SyntaxExtensionKind::Bang(Box::new(BangProcMacro { client, krate: LOCAL_CRATE })),
);
}
21 changes: 18 additions & 3 deletions compiler/rustc_errors/src/emitter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,9 @@ pub trait Emitter {
// are some which do actually involve macros.
ExpnKind::Inlined | ExpnKind::Desugaring(..) | ExpnKind::AstPass(..) => None,

ExpnKind::Macro(macro_kind, _) => Some(macro_kind),
ExpnKind::Macro { kind: macro_kind, name: _, proc_macro: _ } => {
Some(macro_kind)
}
}
});

Expand Down Expand Up @@ -371,10 +373,19 @@ pub trait Emitter {
new_labels
.push((trace.call_site, "in the inlined copy of this code".to_string()));
} else if always_backtrace {
let proc_macro = if let ExpnKind::Macro { kind: _, name: _, proc_macro: true } =
trace.kind
{
"procedural macro "
} else {
""
};

new_labels.push((
trace.def_site,
format!(
"in this expansion of `{}`{}",
"in this expansion of {}`{}`{}",
proc_macro,
trace.kind.descr(),
if macro_backtrace.len() > 1 {
// if macro_backtrace.len() == 1 it'll be
Expand All @@ -400,7 +411,11 @@ pub trait Emitter {
// and it needs an "in this macro invocation" label to match that.
let redundant_span = trace.call_site.contains(sp);

if !redundant_span && matches!(trace.kind, ExpnKind::Macro(MacroKind::Bang, _))
if !redundant_span
&& matches!(
trace.kind,
ExpnKind::Macro { kind: MacroKind::Bang, name: _, proc_macro: _ }
)
|| always_backtrace
{
new_labels.push((
Expand Down
16 changes: 14 additions & 2 deletions compiler/rustc_expand/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT;
use rustc_lint_defs::BuiltinLintDiagnostics;
use rustc_parse::{self, nt_to_tokenstream, parser, MACRO_ARGUMENTS};
use rustc_session::{parse::ParseSess, Limit, Session};
use rustc_span::def_id::DefId;
use rustc_span::def_id::{CrateNum, DefId};
use rustc_span::edition::Edition;
use rustc_span::hygiene::{AstPass, ExpnData, ExpnId, ExpnKind};
use rustc_span::source_map::SourceMap;
Expand Down Expand Up @@ -810,8 +810,16 @@ impl SyntaxExtension {
descr: Symbol,
macro_def_id: Option<DefId>,
) -> ExpnData {
use SyntaxExtensionKind::*;
let proc_macro = match self.kind {
// User-defined proc macro
Bang(..) | Attr(..) | Derive(..) => true,
// Consider everthing else to be not a proc
// macro for diagnostic purposes
LegacyBang(..) | LegacyAttr(..) | NonMacroAttr { .. } | LegacyDerive(..) => false,
};
ExpnData::new(
ExpnKind::Macro(self.macro_kind(), descr),
ExpnKind::Macro { kind: self.macro_kind(), name: descr, proc_macro },
parent,
call_site,
self.span,
Expand Down Expand Up @@ -873,6 +881,10 @@ pub trait ResolverExpand {
fn take_derive_resolutions(&mut self, expn_id: ExpnId) -> Option<DeriveResolutions>;
/// Path resolution logic for `#[cfg_accessible(path)]`.
fn cfg_accessible(&mut self, expn_id: ExpnId, path: &ast::Path) -> Result<bool, Indeterminate>;

/// Decodes the proc-macro quoted span in the specified crate, with the specified id.
/// No caching is performed.
fn get_proc_macro_quoted_span(&self, krate: CrateNum, id: usize) -> Span;
}

#[derive(Clone, Default)]
Expand Down
10 changes: 7 additions & 3 deletions compiler/rustc_expand/src/proc_macro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ use rustc_data_structures::sync::Lrc;
use rustc_errors::ErrorReported;
use rustc_parse::nt_to_tokenstream;
use rustc_parse::parser::ForceCollect;
use rustc_span::def_id::CrateNum;
use rustc_span::{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>,
pub krate: CrateNum,
}

impl base::ProcMacro for BangProcMacro {
Expand All @@ -24,7 +26,7 @@ impl base::ProcMacro for BangProcMacro {
span: Span,
input: TokenStream,
) -> Result<TokenStream, ErrorReported> {
let server = proc_macro_server::Rustc::new(ecx);
let server = proc_macro_server::Rustc::new(ecx, self.krate);
self.client.run(&EXEC_STRATEGY, server, input, ecx.ecfg.proc_macro_backtrace).map_err(|e| {
let mut err = ecx.struct_span_err(span, "proc macro panicked");
if let Some(s) = e.as_str() {
Expand All @@ -38,6 +40,7 @@ impl base::ProcMacro for BangProcMacro {

pub struct AttrProcMacro {
pub client: pm::bridge::client::Client<fn(pm::TokenStream, pm::TokenStream) -> pm::TokenStream>,
pub krate: CrateNum,
}

impl base::AttrProcMacro for AttrProcMacro {
Expand All @@ -48,7 +51,7 @@ impl base::AttrProcMacro for AttrProcMacro {
annotation: TokenStream,
annotated: TokenStream,
) -> Result<TokenStream, ErrorReported> {
let server = proc_macro_server::Rustc::new(ecx);
let server = proc_macro_server::Rustc::new(ecx, self.krate);
self.client
.run(&EXEC_STRATEGY, server, annotation, annotated, ecx.ecfg.proc_macro_backtrace)
.map_err(|e| {
Expand All @@ -64,6 +67,7 @@ impl base::AttrProcMacro for AttrProcMacro {

pub struct ProcMacroDerive {
pub client: pm::bridge::client::Client<fn(pm::TokenStream) -> pm::TokenStream>,
pub krate: CrateNum,
}

impl MultiItemModifier for ProcMacroDerive {
Expand Down Expand Up @@ -97,7 +101,7 @@ impl MultiItemModifier for ProcMacroDerive {
nt_to_tokenstream(&item, &ecx.sess.parse_sess, CanSynthesizeMissingTokens::No)
};

let server = proc_macro_server::Rustc::new(ecx);
let server = proc_macro_server::Rustc::new(ecx, self.krate);
let stream =
match self.client.run(&EXEC_STRATEGY, server, input, ecx.ecfg.proc_macro_backtrace) {
Ok(stream) => stream,
Expand Down
74 changes: 67 additions & 7 deletions compiler/rustc_expand/src/proc_macro_server.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::base::ExtCtxt;
use crate::base::{ExtCtxt, ResolverExpand};

use rustc_ast as ast;
use rustc_ast::token;
Expand All @@ -7,13 +7,16 @@ use rustc_ast::token::NtIdent;
use rustc_ast::tokenstream::{self, CanSynthesizeMissingTokens};
use rustc_ast::tokenstream::{DelimSpan, Spacing::*, TokenStream, TreeAndSpacing};
use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync::Lrc;
use rustc_errors::Diagnostic;
use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT;
use rustc_lint_defs::BuiltinLintDiagnostics;
use rustc_parse::lexer::nfc_normalize;
use rustc_parse::{nt_to_tokenstream, parse_stream_from_source_str};
use rustc_session::parse::ParseSess;
use rustc_span::def_id::CrateNum;
use rustc_span::hygiene::ExpnId;
use rustc_span::hygiene::ExpnKind;
use rustc_span::symbol::{self, kw, sym, Symbol};
use rustc_span::{BytePos, FileName, MultiSpan, Pos, RealFileName, SourceFile, Span};
Expand Down Expand Up @@ -355,22 +358,34 @@ pub struct Literal {
}

pub(crate) struct Rustc<'a> {
resolver: &'a dyn ResolverExpand,
sess: &'a ParseSess,
def_site: Span,
call_site: Span,
mixed_site: Span,
span_debug: bool,
krate: CrateNum,
expn_id: ExpnId,
rebased_spans: FxHashMap<usize, Span>,
}

impl<'a> Rustc<'a> {
pub fn new(cx: &'a ExtCtxt<'_>) -> Self {
pub fn new(cx: &'a ExtCtxt<'_>, krate: CrateNum) -> Self {
let expn_data = cx.current_expansion.id.expn_data();
let def_site = cx.with_def_site_ctxt(expn_data.def_site);
let call_site = cx.with_call_site_ctxt(expn_data.call_site);
let mixed_site = cx.with_mixed_site_ctxt(expn_data.call_site);
let sess = cx.parse_sess();
Rustc {
sess: &cx.sess.parse_sess,
def_site: cx.with_def_site_ctxt(expn_data.def_site),
call_site: cx.with_call_site_ctxt(expn_data.call_site),
mixed_site: cx.with_mixed_site_ctxt(expn_data.call_site),
resolver: cx.resolver,
sess,
def_site,
call_site,
mixed_site,
span_debug: cx.ecfg.span_debug,
krate,
expn_id: cx.current_expansion.id,
rebased_spans: FxHashMap::default(),
}
}

Expand Down Expand Up @@ -713,6 +728,51 @@ impl server::Span for Rustc<'_> {
fn source_text(&mut self, span: Self::Span) -> Option<String> {
self.sess.source_map().span_to_snippet(span).ok()
}
/// Saves the provided span into the metadata of
/// *the crate we are currently compiling*, which must
/// be a proc-macro crate. This id can be passed to
/// `recover_proc_macro_span` when our current crate
/// is *run* as a proc-macro.
///
/// Let's suppose that we have two crates - `my_client`
/// and `my_proc_macro`. The `my_proc_macro` crate
/// contains a procedural macro `my_macro`, which
/// is implemented as: `quote! { "hello" }`
///
/// When we *compile* `my_proc_macro`, we will execute
/// the `quote` proc-macro. This will save the span of
/// "hello" into the metadata of `my_proc_macro`. As a result,
/// the body of `my_proc_macro` (after expansion) will end
/// up containg a call that looks like this:
/// `proc_macro::Ident::new("hello", proc_macro::Span::recover_proc_macro_span(0))`
///
/// where `0` is the id returned by this function.
/// When `my_proc_macro` *executes* (during the compilation of `my_client`),
/// the call to `recover_proc_macro_span` will load the corresponding
/// span from the metadata of `my_proc_macro` (which we have access to,
/// since we've loaded `my_proc_macro` from disk in order to execute it).
/// In this way, we have obtained a span pointing into `my_proc_macro`
fn save_span(&mut self, mut span: Self::Span) -> usize {
// Throw away the `SyntaxContext`, since we currently
// skip serializing `SyntaxContext`s for proc-macro crates
span = span.with_ctxt(rustc_span::SyntaxContext::root());
self.sess.save_proc_macro_span(span)
}
fn recover_proc_macro_span(&mut self, id: usize) -> Self::Span {
let resolver = self.resolver;
let krate = self.krate;
let expn_id = self.expn_id;
*self.rebased_spans.entry(id).or_insert_with(|| {
let raw_span = resolver.get_proc_macro_quoted_span(krate, id);
// Ignore the deserialized `SyntaxContext` entirely.
// FIXME: Preserve the macro backtrace from the serialized span
// For example, if a proc-macro crate has code like
// `macro_one!() -> macro_two!() -> quote!()`, we might
// want to 'concatenate' this backtrace with the backtrace from
// our current call site.
raw_span.with_def_site_ctxt(expn_id)
})
}
}

// See issue #74616 for details
Expand All @@ -722,7 +782,7 @@ fn ident_name_compatibility_hack(
rustc: &mut Rustc<'_>,
) -> Option<(rustc_span::symbol::Ident, bool)> {
if let NtIdent(ident, is_raw) = nt {
if let ExpnKind::Macro(_, macro_name) = orig_span.ctxt().outer_expn_data().kind {
if let ExpnKind::Macro { name: macro_name, .. } = orig_span.ctxt().outer_expn_data().kind {
let source_map = rustc.sess.source_map();
let filename = source_map.span_to_filename(orig_span);
if let FileName::Real(RealFileName::Named(path)) = filename {
Expand Down
19 changes: 15 additions & 4 deletions compiler/rustc_lint/src/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,10 +248,21 @@ impl EarlyLintPass for LintPassImpl {
if last.ident.name == sym::LintPass {
let expn_data = lint_pass.path.span.ctxt().outer_expn_data();
let call_site = expn_data.call_site;
if expn_data.kind != ExpnKind::Macro(MacroKind::Bang, sym::impl_lint_pass)
&& call_site.ctxt().outer_expn_data().kind
!= ExpnKind::Macro(MacroKind::Bang, sym::declare_lint_pass)
{
if !matches!(
expn_data.kind,
ExpnKind::Macro {
kind: MacroKind::Bang,
name: sym::impl_lint_pass,
proc_macro: _
}
) && !matches!(
call_site.ctxt().outer_expn_data().kind,
ExpnKind::Macro {
kind: MacroKind::Bang,
name: sym::declare_lint_pass,
proc_macro: _
}
) {
cx.struct_span_lint(
LINT_PASS_IMPL_WITHOUT_MACRO,
lint_pass.path.span,
Expand Down
11 changes: 6 additions & 5 deletions compiler/rustc_lint/src/non_fmt_panic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,10 +248,11 @@ fn panic_call<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>) -> (Span,
}
}

let macro_symbol = if let hygiene::ExpnKind::Macro(_, symbol) = expn.kind {
symbol
} else {
Symbol::intern("panic")
};
let macro_symbol =
if let hygiene::ExpnKind::Macro { kind: _, name: symbol, proc_macro: _ } = expn.kind {
symbol
} else {
Symbol::intern("panic")
};
(expn.call_site, panic_macro, macro_symbol.as_str())
}
38 changes: 27 additions & 11 deletions compiler/rustc_metadata/src/rmeta/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -716,30 +716,37 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
.decode((self, sess))
}

fn load_proc_macro(&self, id: DefIndex, sess: &Session) -> SyntaxExtension {
let (name, kind, helper_attrs) = match *self.raw_proc_macro(id) {
fn load_proc_macro(&self, def_id: DefId, sess: &Session) -> SyntaxExtension {
let (name, kind, helper_attrs) = match *self.raw_proc_macro(def_id.index) {
ProcMacro::CustomDerive { trait_name, attributes, client } => {
let helper_attrs =
attributes.iter().cloned().map(Symbol::intern).collect::<Vec<_>>();
(
trait_name,
SyntaxExtensionKind::Derive(Box::new(ProcMacroDerive { client })),
SyntaxExtensionKind::Derive(Box::new(ProcMacroDerive {
client,
krate: def_id.krate,
})),
helper_attrs,
)
}
ProcMacro::Attr { name, client } => {
(name, SyntaxExtensionKind::Attr(Box::new(AttrProcMacro { client })), Vec::new())
}
ProcMacro::Bang { name, client } => {
(name, SyntaxExtensionKind::Bang(Box::new(BangProcMacro { client })), Vec::new())
}
ProcMacro::Attr { name, client } => (
name,
SyntaxExtensionKind::Attr(Box::new(AttrProcMacro { client, krate: def_id.krate })),
Vec::new(),
),
ProcMacro::Bang { name, client } => (
name,
SyntaxExtensionKind::Bang(Box::new(BangProcMacro { client, krate: def_id.krate })),
Vec::new(),
),
};

let attrs: Vec<_> = self.get_item_attrs(id, sess).collect();
let attrs: Vec<_> = self.get_item_attrs(def_id.index, sess).collect();
SyntaxExtension::new(
sess,
kind,
self.get_span(id, sess),
self.get_span(def_id.index, sess),
helper_attrs,
self.root.edition,
Symbol::intern(name),
Expand Down Expand Up @@ -1379,6 +1386,15 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
}
}

fn get_proc_macro_quoted_span(&self, index: usize, sess: &Session) -> Span {
self.root
.tables
.proc_macro_quoted_spans
.get(self, index)
.unwrap_or_else(|| panic!("Missing proc macro quoted span: {:?}", index))
.decode((self, sess))
}

fn get_foreign_modules(&self, tcx: TyCtxt<'tcx>) -> Lrc<FxHashMap<DefId, ForeignModule>> {
if self.root.is_proc_macro_crate() {
// Proc macro crates do not have any *target* foreign modules.
Expand Down
Loading