From f916b0474a0443c8ce9915efb59b7465b42e03f8 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sun, 2 Aug 2020 19:52:16 -0400 Subject: [PATCH 1/2] Implement span quoting for proc-macros This PR implements span quoting, allowing proc-macros to produce spans pointing *into their own crate*. This is used by the unstable `proc_macro::quote!` macro, allowing us to get error messages like this: ``` error[E0412]: cannot find type `MissingType` in this scope --> $DIR/auxiliary/span-from-proc-macro.rs:37:20 | LL | pub fn error_from_attribute(_args: TokenStream, _input: TokenStream) -> TokenStream { | ----------------------------------------------------------------------------------- in this expansion of procedural macro `#[error_from_attribute]` ... LL | field: MissingType | ^^^^^^^^^^^ not found in this scope | ::: $DIR/span-from-proc-macro.rs:8:1 | LL | #[error_from_attribute] | ----------------------- in this macro invocation ``` Here, `MissingType` occurs inside the implementation of the proc-macro `#[error_from_attribute]`. Previosuly, this would always result in a span pointing at `#[error_from_attribute]` This will make many proc-macro-related error message much more useful - when a proc-macro generates code containing an error, users will get an error message pointing directly at that code (within the macro definition), instead of always getting a span pointing at the macro invocation site. This is implemented as follows: * When a proc-macro crate is being *compiled*, it causes the `quote!` macro to get run. This saves all of the sapns in the input to `quote!` into the metadata of *the proc-macro-crate* (which we are currently compiling). The `quote!` macro then expands to a call to `proc_macro::Span::recover_proc_macro_span(id)`, where `id` is an opaque identifier for the span in the crate metadata. * When the same proc-macro crate is *run* (e.g. it is loaded from disk and invoked by some consumer crate), the call to `proc_macro::Span::recover_proc_macro_span` causes us to load the span from the proc-macro crate's metadata. The proc-macro then produces a `TokenStream` containing a `Span` pointing into the proc-macro crate itself. The recursive nature of 'quote!' can be difficult to understand at first. The file `src/test/ui/proc-macro/quote-debug.stdout` shows the output of the `quote!` macro, which should make this eaier to understand. This PR also supports custom quoting spans in custom quote macros (e.g. the `quote` crate). All span quoting goes through the `proc_macro::quote_span` method, which can be called by a custom quote macro to perform span quoting. An example of this usage is provided in `src/test/ui/proc-macro/auxiliary/custom-quote.rs` Custom quoting currently has a few limitations: In order to quote a span, we need to generate a call to `proc_macro::Span::recover_proc_macro_span`. However, proc-macros support renaming the `proc_macro` crate, so we can't simply hardcode this path. Previously, the `quote_span` method used the path `crate::Span` - however, this only works when it is called by the builtin `quote!` macro in the same crate. To support being called from arbitrary crates, we need access to the name of the `proc_macro` crate to generate a path. This PR adds an additional argument to `quote_span` to specify the name of the `proc_macro` crate. Howver, this feels kind of hacky, and we may want to change this before stabilizing anything quote-related. Additionally, using `quote_span` currently requires enabling the `proc_macro_internals` feature. The builtin `quote!` macro has an `#[allow_internal_unstable]` attribute, but this won't work for custom quote implementations. This will likely require some additional tricks to apply `allow_internal_unstable` to the span of `proc_macro::Span::recover_proc_macro_span`. --- compiler/rustc_builtin_macros/src/lib.rs | 6 +- compiler/rustc_errors/src/emitter.rs | 21 +++++- compiler/rustc_expand/src/base.rs | 16 +++- compiler/rustc_expand/src/proc_macro.rs | 10 ++- .../rustc_expand/src/proc_macro_server.rs | 74 +++++++++++++++++-- compiler/rustc_lint/src/internal.rs | 19 ++++- compiler/rustc_lint/src/non_fmt_panic.rs | 11 +-- compiler/rustc_metadata/src/rmeta/decoder.rs | 38 +++++++--- .../src/rmeta/decoder/cstore_impl.rs | 11 ++- compiler/rustc_metadata/src/rmeta/encoder.rs | 5 ++ compiler/rustc_metadata/src/rmeta/mod.rs | 7 +- compiler/rustc_middle/src/lint.rs | 2 +- .../rustc_mir/src/transform/coverage/mod.rs | 2 +- .../rustc_mir/src/transform/coverage/spans.rs | 7 +- compiler/rustc_resolve/src/lib.rs | 8 +- compiler/rustc_resolve/src/macros.rs | 12 ++- compiler/rustc_save_analysis/src/lib.rs | 2 +- compiler/rustc_session/src/parse.rs | 14 ++++ compiler/rustc_span/src/hygiene.rs | 15 +++- compiler/rustc_span/src/lib.rs | 5 +- library/proc_macro/src/bridge/mod.rs | 3 + library/proc_macro/src/lib.rs | 16 +++- library/proc_macro/src/quote.rs | 10 ++- .../ui/proc-macro/auxiliary/custom-quote.rs | 31 ++++++++ .../auxiliary/span-from-proc-macro.rs | 49 ++++++++++++ .../ui/proc-macro/meta-macro-hygiene.stdout | 6 +- .../nonterminal-token-hygiene.stdout | 6 +- src/test/ui/proc-macro/quote-debug.rs | 18 +++++ src/test/ui/proc-macro/quote-debug.stdout | 52 +++++++++++++ .../ui/proc-macro/span-from-proc-macro.rs | 17 +++++ .../ui/proc-macro/span-from-proc-macro.stderr | 62 ++++++++++++++++ src/tools/clippy/clippy_lints/src/misc.rs | 2 +- .../clippy_lints/src/unit_types/unit_cmp.rs | 2 +- src/tools/clippy/clippy_utils/src/lib.rs | 4 +- 34 files changed, 494 insertions(+), 69 deletions(-) create mode 100644 src/test/ui/proc-macro/auxiliary/custom-quote.rs create mode 100644 src/test/ui/proc-macro/auxiliary/span-from-proc-macro.rs create mode 100644 src/test/ui/proc-macro/quote-debug.rs create mode 100644 src/test/ui/proc-macro/quote-debug.stdout create mode 100644 src/test/ui/proc-macro/span-from-proc-macro.rs create mode 100644 src/test/ui/proc-macro/span-from-proc-macro.stderr diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index d7926ed0e0b..65a141e1112 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -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; @@ -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 })), + ); } diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index a58caf2667b..61870eede1d 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -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) + } } }); @@ -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 @@ -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(( diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 59505842816..0e966d0bf79 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -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; @@ -810,8 +810,16 @@ impl SyntaxExtension { descr: Symbol, macro_def_id: Option, ) -> 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, @@ -873,6 +881,10 @@ pub trait ResolverExpand { fn take_derive_resolutions(&mut self, expn_id: ExpnId) -> Option; /// Path resolution logic for `#[cfg_accessible(path)]`. fn cfg_accessible(&mut self, expn_id: ExpnId, path: &ast::Path) -> Result; + + /// 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)] diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs index 3f84979ac05..494b3fb61ee 100644 --- a/compiler/rustc_expand/src/proc_macro.rs +++ b/compiler/rustc_expand/src/proc_macro.rs @@ -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 pm::TokenStream>, + pub krate: CrateNum, } impl base::ProcMacro for BangProcMacro { @@ -24,7 +26,7 @@ impl base::ProcMacro for BangProcMacro { span: Span, input: TokenStream, ) -> Result { - 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() { @@ -38,6 +40,7 @@ impl base::ProcMacro for BangProcMacro { pub struct AttrProcMacro { pub client: pm::bridge::client::Client pm::TokenStream>, + pub krate: CrateNum, } impl base::AttrProcMacro for AttrProcMacro { @@ -48,7 +51,7 @@ impl base::AttrProcMacro for AttrProcMacro { annotation: TokenStream, annotated: TokenStream, ) -> Result { - 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| { @@ -64,6 +67,7 @@ impl base::AttrProcMacro for AttrProcMacro { pub struct ProcMacroDerive { pub client: pm::bridge::client::Client pm::TokenStream>, + pub krate: CrateNum, } impl MultiItemModifier for ProcMacroDerive { @@ -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, diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 1ea26b4eab4..f6645448fcc 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -1,4 +1,4 @@ -use crate::base::ExtCtxt; +use crate::base::{ExtCtxt, ResolverExpand}; use rustc_ast as ast; use rustc_ast::token; @@ -7,6 +7,7 @@ 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; @@ -14,6 +15,8 @@ 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}; @@ -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, } 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(), } } @@ -713,6 +728,51 @@ impl server::Span for Rustc<'_> { fn source_text(&mut self, span: Self::Span) -> Option { 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 @@ -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 { diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 9b1a339572e..0398d4a9961 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -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, diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs index 070bc3522a4..c91dc37b374 100644 --- a/compiler/rustc_lint/src/non_fmt_panic.rs +++ b/compiler/rustc_lint/src/non_fmt_panic.rs @@ -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()) } diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index d74fef0045f..4a01731b512 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -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::>(); ( 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), @@ -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> { if self.root.is_proc_macro_crate() { // Proc macro crates do not have any *target* foreign modules. diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index b11ad6c7ff8..cda57f48cae 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -411,7 +411,7 @@ impl CStore { let data = self.get_crate_data(id.krate); if data.root.is_proc_macro_crate() { - return LoadedMacro::ProcMacro(data.load_proc_macro(id.index, sess)); + return LoadedMacro::ProcMacro(data.load_proc_macro(id, sess)); } let span = data.get_span(id.index, sess); @@ -461,6 +461,15 @@ impl CStore { pub fn item_attrs(&self, def_id: DefId, sess: &Session) -> Vec { self.get_crate_data(def_id.krate).get_item_attrs(def_id.index, sess).collect() } + + pub fn get_proc_macro_quoted_span_untracked( + &self, + cnum: CrateNum, + id: usize, + sess: &Session, + ) -> Span { + self.get_crate_data(cnum).get_proc_macro_quoted_span(id, sess) + } } impl CrateStore for CStore { diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index e8f02b8e66f..c61417f9998 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1579,6 +1579,11 @@ impl EncodeContext<'a, 'tcx> { let proc_macro_decls_static = tcx.proc_macro_decls_static(LOCAL_CRATE).unwrap().index; let stability = tcx.lookup_stability(DefId::local(CRATE_DEF_INDEX)).copied(); let macros = self.lazy(hir.krate().proc_macros.iter().map(|p| p.owner.local_def_index)); + let spans = self.tcx.sess.parse_sess.proc_macro_quoted_spans(); + for (i, span) in spans.into_iter().enumerate() { + let span = self.lazy(span); + self.tables.proc_macro_quoted_spans.set(i, span); + } record!(self.tables.def_kind[LOCAL_CRATE.as_def_id()] <- DefKind::Mod); record!(self.tables.span[LOCAL_CRATE.as_def_id()] <- tcx.def_span(LOCAL_CRATE.as_def_id())); diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 9f665d5daaa..6b375045f14 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -258,15 +258,15 @@ crate struct TraitImpls { /// Define `LazyTables` and `TableBuilders` at the same time. macro_rules! define_tables { - ($($name:ident: Table),+ $(,)?) => { + ($($name:ident: Table<$IDX:ty, $T:ty>),+ $(,)?) => { #[derive(MetadataEncodable, MetadataDecodable)] crate struct LazyTables<'tcx> { - $($name: Lazy!(Table)),+ + $($name: Lazy!(Table<$IDX, $T>)),+ } #[derive(Default)] struct TableBuilders<'tcx> { - $($name: TableBuilder),+ + $($name: TableBuilder<$IDX, $T>),+ } impl TableBuilders<'tcx> { @@ -315,6 +315,7 @@ define_tables! { // definitions from any given crate. def_keys: Table>, def_path_hashes: Table>, + proc_macro_quoted_spans: Table>, } #[derive(Copy, Clone, MetadataEncodable, MetadataDecodable)] diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 7adaf54fa23..38d0793a682 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -389,7 +389,7 @@ pub fn in_external_macro(sess: &Session, span: Span) -> bool { false } ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) => true, // well, it's "external" - ExpnKind::Macro(MacroKind::Bang, _) => { + ExpnKind::Macro { kind: MacroKind::Bang, name: _, proc_macro: _ } => { // Dummy span for the `def_site` means it's an external macro. expn_data.def_site.is_dummy() || sess.source_map().is_imported(expn_data.def_site) } diff --git a/compiler/rustc_mir/src/transform/coverage/mod.rs b/compiler/rustc_mir/src/transform/coverage/mod.rs index c1e8f620b30..aca11de4516 100644 --- a/compiler/rustc_mir/src/transform/coverage/mod.rs +++ b/compiler/rustc_mir/src/transform/coverage/mod.rs @@ -128,7 +128,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { if expn_data.is_root() { break; } - if let ExpnKind::Macro(..) = expn_data.kind { + if let ExpnKind::Macro { .. } = expn_data.kind { body_span = expn_data.call_site; } else { break; diff --git a/compiler/rustc_mir/src/transform/coverage/spans.rs b/compiler/rustc_mir/src/transform/coverage/spans.rs index b3fc2a0cb5e..444b4e2ca19 100644 --- a/compiler/rustc_mir/src/transform/coverage/spans.rs +++ b/compiler/rustc_mir/src/transform/coverage/spans.rs @@ -184,8 +184,11 @@ impl CoverageSpan { self.current_macro_or_none .borrow_mut() .get_or_insert_with(|| { - if let ExpnKind::Macro(MacroKind::Bang, current_macro) = - self.expn_span.ctxt().outer_expn_data().kind + if let ExpnKind::Macro { + kind: MacroKind::Bang, + name: current_macro, + proc_macro: _, + } = self.expn_span.ctxt().outer_expn_data().kind { return Some(current_macro); } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 9197f4059ca..300d2c01cb5 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1770,9 +1770,11 @@ impl<'a> Resolver<'a> { let expn_data = expn_id.expn_data(); match expn_data.kind { ExpnKind::Root - | ExpnKind::Macro(MacroKind::Bang | MacroKind::Derive, _) => { - Scope::DeriveHelpersCompat - } + | ExpnKind::Macro { + kind: MacroKind::Bang | MacroKind::Derive, + name: _, + proc_macro: _, + } => Scope::DeriveHelpersCompat, _ => Scope::DeriveHelpers(expn_data.parent), } } diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 10e27f33c29..3f7db2b6962 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -20,7 +20,7 @@ use rustc_expand::compile_declarative_macro; use rustc_expand::expand::{AstFragment, Invocation, InvocationKind, SupportsMacroExpansion}; use rustc_feature::is_builtin_attr_name; use rustc_hir::def::{self, DefKind, NonMacroAttrKind}; -use rustc_hir::def_id; +use rustc_hir::def_id::{self, CrateNum}; use rustc_hir::PrimTy; use rustc_middle::middle::stability; use rustc_middle::ty; @@ -325,7 +325,11 @@ impl<'a> ResolverExpand for Resolver<'a> { let expn_data = expn_id.expn_data(); match expn_data.kind { ExpnKind::Root - | ExpnKind::Macro(MacroKind::Bang | MacroKind::Derive, _) => { + | ExpnKind::Macro { + name: _, + kind: MacroKind::Bang | MacroKind::Derive, + proc_macro: _, + } => { break; } _ => expn_id = expn_data.parent, @@ -462,6 +466,10 @@ impl<'a> ResolverExpand for Resolver<'a> { .emit(); Ok(false) } + + fn get_proc_macro_quoted_span(&self, krate: CrateNum, id: usize) -> Span { + self.crate_loader.cstore().get_proc_macro_quoted_span_untracked(krate, id, self.session) + } } impl<'a> Resolver<'a> { diff --git a/compiler/rustc_save_analysis/src/lib.rs b/compiler/rustc_save_analysis/src/lib.rs index c19c16b88a7..7256e68b87a 100644 --- a/compiler/rustc_save_analysis/src/lib.rs +++ b/compiler/rustc_save_analysis/src/lib.rs @@ -786,7 +786,7 @@ impl<'tcx> SaveContext<'tcx> { let callee = span.source_callee()?; let mac_name = match callee.kind { - ExpnKind::Macro(kind, name) => match kind { + ExpnKind::Macro { kind, name, proc_macro: _ } => match kind { MacroKind::Bang => name, // Ignore attribute macros, their spans are usually mangled diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 65d5d96aba1..7b7878e9c7f 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -137,6 +137,9 @@ pub struct ParseSess { pub type_ascription_path_suggestions: Lock>, /// Whether cfg(version) should treat the current release as incomplete pub assume_incomplete_release: bool, + /// Spans passed to `proc_macro::quote_span`. Each span has a numerical + /// identifier represented by its position in the vector. + pub proc_macro_quoted_spans: Lock>, } impl ParseSess { @@ -164,6 +167,7 @@ impl ParseSess { env_depinfo: Default::default(), type_ascription_path_suggestions: Default::default(), assume_incomplete_release: false, + proc_macro_quoted_spans: Default::default(), } } @@ -236,4 +240,14 @@ impl ParseSess { ); } } + + pub fn save_proc_macro_span(&self, span: Span) -> usize { + let mut spans = self.proc_macro_quoted_spans.lock(); + spans.push(span); + return spans.len() - 1; + } + + pub fn proc_macro_quoted_spans(&self) -> Vec { + self.proc_macro_quoted_spans.lock().clone() + } } diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 8b611626fca..8f3b8cc2d0e 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -144,7 +144,10 @@ impl ExpnId { let expn_data = self.expn_data(); // Stop going up the backtrace once include! is encountered if expn_data.is_root() - || expn_data.kind == ExpnKind::Macro(MacroKind::Bang, sym::include) + || matches!( + expn_data.kind, + ExpnKind::Macro { kind: MacroKind::Bang, name: sym::include, proc_macro: _ } + ) { break; } @@ -839,7 +842,13 @@ pub enum ExpnKind { /// No expansion, aka root expansion. Only `ExpnId::root()` has this kind. Root, /// Expansion produced by a macro. - Macro(MacroKind, Symbol), + Macro { + kind: MacroKind, + name: Symbol, + /// If `true`, this macro is a procedural macro. This + /// flag is only used for diagnostic purposes + proc_macro: bool, + }, /// Transform done by the compiler on the AST. AstPass(AstPass), /// Desugaring done by the compiler during HIR lowering. @@ -852,7 +861,7 @@ impl ExpnKind { pub fn descr(&self) -> String { match *self { ExpnKind::Root => kw::PathRoot.to_string(), - ExpnKind::Macro(macro_kind, name) => match macro_kind { + ExpnKind::Macro { kind, name, proc_macro: _ } => match kind { MacroKind::Bang => format!("{}!", name), MacroKind::Attr => format!("#[{}]", name), MacroKind::Derive => format!("#[derive({})]", name), diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index e0bc7544246..d56b434ca85 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -394,7 +394,10 @@ impl Span { /// Returns `true` if `span` originates in a derive-macro's expansion. pub fn in_derive_expansion(self) -> bool { - matches!(self.ctxt().outer_expn_data().kind, ExpnKind::Macro(MacroKind::Derive, _)) + matches!( + self.ctxt().outer_expn_data().kind, + ExpnKind::Macro { kind: MacroKind::Derive, name: _, proc_macro: _ } + ) } #[inline] diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs index c898d483a8b..355ad1f9f88 100644 --- a/library/proc_macro/src/bridge/mod.rs +++ b/library/proc_macro/src/bridge/mod.rs @@ -162,6 +162,8 @@ macro_rules! with_api { fn join($self: $S::Span, other: $S::Span) -> Option<$S::Span>; fn resolved_at($self: $S::Span, at: $S::Span) -> $S::Span; fn source_text($self: $S::Span) -> Option; + fn save_span($self: $S::Span) -> usize; + fn recover_proc_macro_span(id: usize) -> $S::Span; }, } }; @@ -338,6 +340,7 @@ mark_noop! { &'a [u8], &'a str, String, + usize, Delimiter, Level, LineColumn, diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index c7f58f36154..525fd0fbe34 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -265,7 +265,7 @@ pub mod token_stream { /// Unquoting is done with `$`, and works by taking the single next ident as the unquoted term. /// To quote `$` itself, use `$$`. #[unstable(feature = "proc_macro_quote", issue = "54722")] -#[allow_internal_unstable(proc_macro_def_site)] +#[allow_internal_unstable(proc_macro_def_site, proc_macro_internals)] #[rustc_builtin_macro] pub macro quote($($t:tt)*) { /* compiler built-in */ @@ -394,6 +394,20 @@ impl Span { self.0.source_text() } + // Used by the implementation of `Span::quote` + #[doc(hidden)] + #[unstable(feature = "proc_macro_internals", issue = "27812")] + pub fn save_span(&self) -> usize { + self.0.save_span() + } + + // Used by the implementation of `Span::quote` + #[doc(hidden)] + #[unstable(feature = "proc_macro_internals", issue = "27812")] + pub fn recover_proc_macro_span(id: usize) -> Span { + Span(bridge::client::Span::recover_proc_macro_span(id)) + } + diagnostic_method!(error, Level::Error); diagnostic_method!(warning, Level::Warning); diagnostic_method!(note, Level::Note); diff --git a/library/proc_macro/src/quote.rs b/library/proc_macro/src/quote.rs index 144e2d6bac4..1fd59889709 100644 --- a/library/proc_macro/src/quote.rs +++ b/library/proc_macro/src/quote.rs @@ -65,6 +65,7 @@ pub fn quote(stream: TokenStream) -> TokenStream { if stream.is_empty() { return quote!(crate::TokenStream::new()); } + let proc_macro_crate = quote!(crate); let mut after_dollar = false; let tokens = stream .into_iter() @@ -105,7 +106,7 @@ pub fn quote(stream: TokenStream) -> TokenStream { ))), TokenTree::Ident(tt) => quote!(crate::TokenTree::Ident(crate::Ident::new( (@ TokenTree::from(Literal::string(&tt.to_string()))), - (@ quote_span(tt.span())), + (@ quote_span(proc_macro_crate.clone(), tt.span())), ))), TokenTree::Literal(tt) => quote!(crate::TokenTree::Literal({ let mut iter = (@ TokenTree::from(Literal::string(&tt.to_string()))) @@ -115,7 +116,7 @@ pub fn quote(stream: TokenStream) -> TokenStream { if let (Some(crate::TokenTree::Literal(mut lit)), None) = (iter.next(), iter.next()) { - lit.set_span((@ quote_span(tt.span()))); + lit.set_span((@ quote_span(proc_macro_crate.clone(), tt.span()))); lit } else { unreachable!() @@ -135,6 +136,7 @@ pub fn quote(stream: TokenStream) -> TokenStream { /// Quote a `Span` into a `TokenStream`. /// This is needed to implement a custom quoter. #[unstable(feature = "proc_macro_quote", issue = "54722")] -pub fn quote_span(_: Span) -> TokenStream { - quote!(crate::Span::def_site()) +pub fn quote_span(proc_macro_crate: TokenStream, span: Span) -> TokenStream { + let id = span.save_span(); + quote!((@ proc_macro_crate ) ::Span::recover_proc_macro_span((@ TokenTree::from(Literal::usize_unsuffixed(id))))) } diff --git a/src/test/ui/proc-macro/auxiliary/custom-quote.rs b/src/test/ui/proc-macro/auxiliary/custom-quote.rs new file mode 100644 index 00000000000..714417deee5 --- /dev/null +++ b/src/test/ui/proc-macro/auxiliary/custom-quote.rs @@ -0,0 +1,31 @@ +// force-host +// no-prefer-dynamic +// ignore-tidy-linelength + +#![feature(proc_macro_quote)] +#![crate_type = "proc-macro"] + +extern crate proc_macro; +use std::str::FromStr; +use proc_macro::*; + +#[proc_macro] +pub fn custom_quote(input: TokenStream) -> TokenStream { + let mut tokens: Vec<_> = input.into_iter().collect(); + assert_eq!(tokens.len(), 1, "Unexpected input: {:?}", tokens); + match tokens.pop() { + Some(TokenTree::Ident(ident)) => { + assert_eq!(ident.to_string(), "my_ident"); + + let proc_macro_crate = TokenStream::from_str("::proc_macro").unwrap(); + let quoted_span = proc_macro::quote_span(proc_macro_crate, ident.span()); + let prefix = TokenStream::from_str(r#"let mut ident = proc_macro::Ident::new("my_ident", proc_macro::Span::call_site());"#).unwrap(); + let set_span_method = TokenStream::from_str("ident.set_span").unwrap(); + let set_span_arg = TokenStream::from(TokenTree::Group(Group::new(Delimiter::Parenthesis, quoted_span))); + let suffix = TokenStream::from_str(";proc_macro::TokenStream::from(proc_macro::TokenTree::Ident(ident))").unwrap(); + let full_stream: TokenStream = std::array::IntoIter::new([prefix, set_span_method, set_span_arg, suffix]).collect(); + full_stream + } + _ => unreachable!() + } +} diff --git a/src/test/ui/proc-macro/auxiliary/span-from-proc-macro.rs b/src/test/ui/proc-macro/auxiliary/span-from-proc-macro.rs new file mode 100644 index 00000000000..49292acfea7 --- /dev/null +++ b/src/test/ui/proc-macro/auxiliary/span-from-proc-macro.rs @@ -0,0 +1,49 @@ +// force-host +// no-prefer-dynamic + +#![feature(proc_macro_quote)] +#![feature(proc_macro_internals)] // FIXME - this shouldn't be necessary +#![crate_type = "proc-macro"] + +extern crate proc_macro; +extern crate custom_quote; + +use proc_macro::{quote, TokenStream}; + +macro_rules! expand_to_quote { + () => { + quote! { + let bang_error: bool = 25; + } + } +} + +#[proc_macro] +pub fn error_from_bang(_input: TokenStream) -> TokenStream { + expand_to_quote!() +} + +#[proc_macro] +pub fn other_error_from_bang(_input: TokenStream) -> TokenStream { + custom_quote::custom_quote! { + my_ident + } +} + +#[proc_macro_attribute] +pub fn error_from_attribute(_args: TokenStream, _input: TokenStream) -> TokenStream { + quote! { + struct AttributeError { + field: MissingType + } + } +} + +#[proc_macro_derive(ErrorFromDerive)] +pub fn error_from_derive(_input: TokenStream) -> TokenStream { + quote! { + enum DeriveError { + Variant(OtherMissingType) + } + } +} diff --git a/src/test/ui/proc-macro/meta-macro-hygiene.stdout b/src/test/ui/proc-macro/meta-macro-hygiene.stdout index b7a37ab10ed..d9337fb361b 100644 --- a/src/test/ui/proc-macro/meta-macro-hygiene.stdout +++ b/src/test/ui/proc-macro/meta-macro-hygiene.stdout @@ -45,10 +45,10 @@ fn main /* 0#0 */() { ; } Expansions: 0: parent: ExpnId(0), call_site_ctxt: #0, def_site_ctxt: #0, kind: Root 1: parent: ExpnId(0), call_site_ctxt: #0, def_site_ctxt: #0, kind: AstPass(StdImports) -2: parent: ExpnId(0), call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Bang, "produce_it") +2: parent: ExpnId(0), call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro { kind: Bang, name: "produce_it", proc_macro: false } 3: parent: ExpnId(0), call_site_ctxt: #0, def_site_ctxt: #0, kind: AstPass(StdImports) -4: parent: ExpnId(2), call_site_ctxt: #4, def_site_ctxt: #0, kind: Macro(Bang, "meta_macro::print_def_site") -5: parent: ExpnId(4), call_site_ctxt: #5, def_site_ctxt: #0, kind: Macro(Bang, "$crate::dummy") +4: parent: ExpnId(2), call_site_ctxt: #4, def_site_ctxt: #0, kind: Macro { kind: Bang, name: "meta_macro::print_def_site", proc_macro: true } +5: parent: ExpnId(4), call_site_ctxt: #5, def_site_ctxt: #0, kind: Macro { kind: Bang, name: "$crate::dummy", proc_macro: true } SyntaxContexts: #0: parent: #0, outer_mark: (ExpnId(0), Opaque) diff --git a/src/test/ui/proc-macro/nonterminal-token-hygiene.stdout b/src/test/ui/proc-macro/nonterminal-token-hygiene.stdout index ba3b3ee7827..54dc856bfb0 100644 --- a/src/test/ui/proc-macro/nonterminal-token-hygiene.stdout +++ b/src/test/ui/proc-macro/nonterminal-token-hygiene.stdout @@ -69,10 +69,10 @@ fn main /* 0#0 */() { } Expansions: 0: parent: ExpnId(0), call_site_ctxt: #0, def_site_ctxt: #0, kind: Root 1: parent: ExpnId(0), call_site_ctxt: #0, def_site_ctxt: #0, kind: AstPass(StdImports) -2: parent: ExpnId(0), call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Bang, "outer") +2: parent: ExpnId(0), call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro { kind: Bang, name: "outer", proc_macro: false } 3: parent: ExpnId(0), call_site_ctxt: #0, def_site_ctxt: #0, kind: AstPass(StdImports) -4: parent: ExpnId(2), call_site_ctxt: #4, def_site_ctxt: #4, kind: Macro(Bang, "inner") -5: parent: ExpnId(4), call_site_ctxt: #6, def_site_ctxt: #0, kind: Macro(Bang, "print_bang") +4: parent: ExpnId(2), call_site_ctxt: #4, def_site_ctxt: #4, kind: Macro { kind: Bang, name: "inner", proc_macro: false } +5: parent: ExpnId(4), call_site_ctxt: #6, def_site_ctxt: #0, kind: Macro { kind: Bang, name: "print_bang", proc_macro: true } SyntaxContexts: #0: parent: #0, outer_mark: (ExpnId(0), Opaque) diff --git a/src/test/ui/proc-macro/quote-debug.rs b/src/test/ui/proc-macro/quote-debug.rs new file mode 100644 index 00000000000..e0304a01405 --- /dev/null +++ b/src/test/ui/proc-macro/quote-debug.rs @@ -0,0 +1,18 @@ +// check-pass +// force-host +// no-prefer-dynamic +// compile-flags: -Z unpretty=expanded +// +// This file is not actually used as a proc-macro - instead, +// it's just used to show the output of the `quote!` macro + +#![feature(proc_macro_quote)] +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +fn main() { + proc_macro::quote! { + let hello = "world"; + } +} diff --git a/src/test/ui/proc-macro/quote-debug.stdout b/src/test/ui/proc-macro/quote-debug.stdout new file mode 100644 index 00000000000..4bdc04b9ac4 --- /dev/null +++ b/src/test/ui/proc-macro/quote-debug.stdout @@ -0,0 +1,52 @@ +#![feature(prelude_import)] +#![no_std] +// check-pass +// force-host +// no-prefer-dynamic +// compile-flags: -Z unpretty=expanded +// +// This file is not actually used as a proc-macro - instead, +// it's just used to show the output of the `quote!` macro + +#![feature(proc_macro_quote)] +#![crate_type = "proc-macro"] +#[prelude_import] +use ::std::prelude::rust_2015::*; +#[macro_use] +extern crate std; + +extern crate proc_macro; + +fn main() { + [crate::TokenStream::from(crate::TokenTree::Ident(crate::Ident::new("let", + crate::Span::recover_proc_macro_span(0)))), + crate::TokenStream::from(crate::TokenTree::Ident(crate::Ident::new("hello", + crate::Span::recover_proc_macro_span(1)))), + crate::TokenStream::from(crate::TokenTree::Punct(crate::Punct::new('\u{3d}', + crate::Spacing::Alone))), + crate::TokenStream::from(crate::TokenTree::Literal({ + let mut iter = + "\"world\"".parse::().unwrap().into_iter(); + if let (Some(crate::TokenTree::Literal(mut lit)), + None) = + (iter.next(), + iter.next()) + { + lit.set_span(crate::Span::recover_proc_macro_span(2)); + lit + } else { + { + ::core::panicking::panic("internal error: entered unreachable code") + } + } + })), + crate::TokenStream::from(crate::TokenTree::Punct(crate::Punct::new('\u{3b}', + crate::Spacing::Alone)))].iter().cloned().collect::() +} +const _: () = + { + extern crate proc_macro; + #[rustc_proc_macro_decls] + #[allow(deprecated)] + static _DECLS: &[proc_macro::bridge::client::ProcMacro] = &[]; + }; diff --git a/src/test/ui/proc-macro/span-from-proc-macro.rs b/src/test/ui/proc-macro/span-from-proc-macro.rs new file mode 100644 index 00000000000..ecff2d72587 --- /dev/null +++ b/src/test/ui/proc-macro/span-from-proc-macro.rs @@ -0,0 +1,17 @@ +// aux-build:custom-quote.rs +// aux-build:span-from-proc-macro.rs +// compile-flags: -Z macro-backtrace + +#[macro_use] +extern crate span_from_proc_macro; + +#[error_from_attribute] //~ ERROR cannot find type `MissingType` +struct ShouldBeRemoved; + +#[derive(ErrorFromDerive)] //~ ERROR cannot find type `OtherMissingType` +struct Kept; + +fn main() { + error_from_bang!(); //~ ERROR mismatched types + other_error_from_bang!(); //~ ERROR cannot find value `my_ident` +} diff --git a/src/test/ui/proc-macro/span-from-proc-macro.stderr b/src/test/ui/proc-macro/span-from-proc-macro.stderr new file mode 100644 index 00000000000..2cbe91afacd --- /dev/null +++ b/src/test/ui/proc-macro/span-from-proc-macro.stderr @@ -0,0 +1,62 @@ +error[E0412]: cannot find type `MissingType` in this scope + --> $DIR/auxiliary/span-from-proc-macro.rs:37:20 + | +LL | pub fn error_from_attribute(_args: TokenStream, _input: TokenStream) -> TokenStream { + | ----------------------------------------------------------------------------------- in this expansion of procedural macro `#[error_from_attribute]` +... +LL | field: MissingType + | ^^^^^^^^^^^ not found in this scope + | + ::: $DIR/span-from-proc-macro.rs:8:1 + | +LL | #[error_from_attribute] + | ----------------------- in this macro invocation + +error[E0412]: cannot find type `OtherMissingType` in this scope + --> $DIR/auxiliary/span-from-proc-macro.rs:46:21 + | +LL | pub fn error_from_derive(_input: TokenStream) -> TokenStream { + | ------------------------------------------------------------ in this expansion of procedural macro `#[derive(ErrorFromDerive)]` +... +LL | Variant(OtherMissingType) + | ^^^^^^^^^^^^^^^^ not found in this scope + | + ::: $DIR/span-from-proc-macro.rs:11:10 + | +LL | #[derive(ErrorFromDerive)] + | --------------- in this macro invocation + +error[E0425]: cannot find value `my_ident` in this scope + --> $DIR/auxiliary/span-from-proc-macro.rs:29:9 + | +LL | pub fn other_error_from_bang(_input: TokenStream) -> TokenStream { + | ---------------------------------------------------------------- in this expansion of procedural macro `other_error_from_bang!` +LL | custom_quote::custom_quote! { +LL | my_ident + | ^^^^^^^^ not found in this scope + | + ::: $DIR/span-from-proc-macro.rs:16:5 + | +LL | other_error_from_bang!(); + | ------------------------- in this macro invocation + +error[E0308]: mismatched types + --> $DIR/auxiliary/span-from-proc-macro.rs:16:36 + | +LL | let bang_error: bool = 25; + | ---- ^^ expected `bool`, found integer + | | + | expected due to this +... +LL | pub fn error_from_bang(_input: TokenStream) -> TokenStream { + | ---------------------------------------------------------- in this expansion of procedural macro `error_from_bang!` + | + ::: $DIR/span-from-proc-macro.rs:15:5 + | +LL | error_from_bang!(); + | ------------------- in this macro invocation + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0308, E0412, E0425. +For more information about an error, try `rustc --explain E0308`. diff --git a/src/tools/clippy/clippy_lints/src/misc.rs b/src/tools/clippy/clippy_lints/src/misc.rs index 0b0cd9be46c..6966d798c53 100644 --- a/src/tools/clippy/clippy_lints/src/misc.rs +++ b/src/tools/clippy/clippy_lints/src/misc.rs @@ -660,7 +660,7 @@ fn in_attributes_expansion(expr: &Expr<'_>) -> bool { use rustc_span::hygiene::MacroKind; if expr.span.from_expansion() { let data = expr.span.ctxt().outer_expn_data(); - matches!(data.kind, ExpnKind::Macro(MacroKind::Attr, _)) + matches!(data.kind, ExpnKind::Macro { kind: MacroKind::Attr, name: _, proc_macro: _ }) } else { false } diff --git a/src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs b/src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs index 85257f3113c..d22f7d9a96b 100644 --- a/src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs @@ -8,7 +8,7 @@ use super::UNIT_CMP; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { if expr.span.from_expansion() { if let Some(callee) = expr.span.source_callee() { - if let ExpnKind::Macro(MacroKind::Bang, symbol) = callee.kind { + if let ExpnKind::Macro { kind: MacroKind::Bang, name: symbol, proc_macro: _ } = callee.kind { if let ExprKind::Binary(ref cmp, left, _) = expr.kind { let op = cmp.node; if op.is_comparison() && cx.typeck_results().expr_ty(left).is_unit() { diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index f5ee49c7d5f..9a0b72f06bb 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -947,7 +947,7 @@ pub fn is_expn_of(mut span: Span, name: &str) -> Option { let data = span.ctxt().outer_expn_data(); let new_span = data.call_site; - if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind { + if let ExpnKind::Macro { kind: MacroKind::Bang, name: mac_name, proc_macro: _ } = data.kind { if mac_name.as_str() == name { return Some(new_span); } @@ -975,7 +975,7 @@ pub fn is_direct_expn_of(span: Span, name: &str) -> Option { let data = span.ctxt().outer_expn_data(); let new_span = data.call_site; - if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind { + if let ExpnKind::Macro { kind: MacroKind::Bang, name: mac_name, proc_macro: _ } = data.kind { if mac_name.as_str() == name { return Some(new_span); } From dbf49102aac1515bd56c996fdfcfa20f7ee977ea Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sat, 8 May 2021 21:33:04 -0400 Subject: [PATCH 2/2] Update stderr The spans generated by `quote!` are (intentionally) no longer all the same, so I removed that check entirely. --- src/test/ui/hygiene/unpretty-debug.stdout | 2 +- .../macros/auxiliary/proc_macro_sequence.rs | 19 ------------------- src/test/ui/macros/same-sequence-span.stderr | 12 ++++++++---- 3 files changed, 9 insertions(+), 24 deletions(-) diff --git a/src/test/ui/hygiene/unpretty-debug.stdout b/src/test/ui/hygiene/unpretty-debug.stdout index 96044a89289..e3445a4b80f 100644 --- a/src/test/ui/hygiene/unpretty-debug.stdout +++ b/src/test/ui/hygiene/unpretty-debug.stdout @@ -20,7 +20,7 @@ fn y /* 0#0 */() { } /* Expansions: 0: parent: ExpnId(0), call_site_ctxt: #0, def_site_ctxt: #0, kind: Root -1: parent: ExpnId(0), call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Bang, "foo") +1: parent: ExpnId(0), call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro { kind: Bang, name: "foo", proc_macro: false } SyntaxContexts: #0: parent: #0, outer_mark: (ExpnId(0), Opaque) diff --git a/src/test/ui/macros/auxiliary/proc_macro_sequence.rs b/src/test/ui/macros/auxiliary/proc_macro_sequence.rs index cb8055de6e3..1331480d835 100644 --- a/src/test/ui/macros/auxiliary/proc_macro_sequence.rs +++ b/src/test/ui/macros/auxiliary/proc_macro_sequence.rs @@ -23,24 +23,5 @@ pub fn make_foo(_: TokenStream) -> TokenStream { } }; - // Check that all spans are equal. - // FIXME: `quote!` gives def-site spans to idents and literals, - // but leaves (default) call-site spans on groups and punctuation. - let mut span_call = None; - let mut span_def = None; - for tt in result.clone() { - match tt { - TokenTree::Ident(..) | TokenTree::Literal(..) => match span_def { - None => span_def = Some(tt.span()), - Some(span) => assert_same_span(tt.span(), span), - } - TokenTree::Punct(..) | TokenTree::Group(..) => match span_call { - None => span_call = Some(tt.span()), - Some(span) => assert_same_span(tt.span(), span), - } - } - - } - result } diff --git a/src/test/ui/macros/same-sequence-span.stderr b/src/test/ui/macros/same-sequence-span.stderr index 63b8b29d6ce..911775eb417 100644 --- a/src/test/ui/macros/same-sequence-span.stderr +++ b/src/test/ui/macros/same-sequence-span.stderr @@ -17,11 +17,15 @@ LL | $(= $z:tt)* error: `$x:expr` may be followed by `$y:tt`, which is not allowed for `expr` fragments --> $DIR/same-sequence-span.rs:19:1 | -LL | proc_macro_sequence::make_foo!(); - | ---------------------------------^^^^^^^^^^^^^ +LL | proc_macro_sequence::make_foo!(); + | ^-------------------------------- + | | + | _in this macro invocation | | - | not allowed after `expr` fragments - | in this macro invocation +LL | | +LL | | +LL | | fn main() {} + | |_________________________________^ not allowed after `expr` fragments | = note: allowed there are: `=>`, `,` or `;` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)