about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAaron Hill <aa1ronham@gmail.com>2020-08-02 19:52:16 -0400
committerAaron Hill <aa1ronham@gmail.com>2021-05-12 00:51:31 -0400
commitf916b0474a0443c8ce9915efb59b7465b42e03f8 (patch)
treeb48772ffbcf5e25a93beb0d21ff5bad37ff1c663
parentea3068efe44f11d379a28a812d4a78ab73a80137 (diff)
downloadrust-f916b0474a0443c8ce9915efb59b7465b42e03f8.tar.gz
rust-f916b0474a0443c8ce9915efb59b7465b42e03f8.zip
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`.
-rw-r--r--compiler/rustc_builtin_macros/src/lib.rs6
-rw-r--r--compiler/rustc_errors/src/emitter.rs21
-rw-r--r--compiler/rustc_expand/src/base.rs16
-rw-r--r--compiler/rustc_expand/src/proc_macro.rs10
-rw-r--r--compiler/rustc_expand/src/proc_macro_server.rs74
-rw-r--r--compiler/rustc_lint/src/internal.rs19
-rw-r--r--compiler/rustc_lint/src/non_fmt_panic.rs11
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder.rs38
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs11
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs5
-rw-r--r--compiler/rustc_metadata/src/rmeta/mod.rs7
-rw-r--r--compiler/rustc_middle/src/lint.rs2
-rw-r--r--compiler/rustc_mir/src/transform/coverage/mod.rs2
-rw-r--r--compiler/rustc_mir/src/transform/coverage/spans.rs7
-rw-r--r--compiler/rustc_resolve/src/lib.rs8
-rw-r--r--compiler/rustc_resolve/src/macros.rs12
-rw-r--r--compiler/rustc_save_analysis/src/lib.rs2
-rw-r--r--compiler/rustc_session/src/parse.rs14
-rw-r--r--compiler/rustc_span/src/hygiene.rs15
-rw-r--r--compiler/rustc_span/src/lib.rs5
-rw-r--r--library/proc_macro/src/bridge/mod.rs3
-rw-r--r--library/proc_macro/src/lib.rs16
-rw-r--r--library/proc_macro/src/quote.rs10
-rw-r--r--src/test/ui/proc-macro/auxiliary/custom-quote.rs31
-rw-r--r--src/test/ui/proc-macro/auxiliary/span-from-proc-macro.rs49
-rw-r--r--src/test/ui/proc-macro/meta-macro-hygiene.stdout6
-rw-r--r--src/test/ui/proc-macro/nonterminal-token-hygiene.stdout6
-rw-r--r--src/test/ui/proc-macro/quote-debug.rs18
-rw-r--r--src/test/ui/proc-macro/quote-debug.stdout52
-rw-r--r--src/test/ui/proc-macro/span-from-proc-macro.rs17
-rw-r--r--src/test/ui/proc-macro/span-from-proc-macro.stderr62
-rw-r--r--src/tools/clippy/clippy_lints/src/misc.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs4
34 files changed, 494 insertions, 69 deletions
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<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,
@@ -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)]
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<fn(pm::TokenStream) -> pm::TokenStream>,
+    pub krate: CrateNum,
 }
 
 impl base::ProcMacro for BangProcMacro {
@@ -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() {
@@ -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 {
@@ -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| {
@@ -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 {
@@ -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<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(),
         }
     }
 
@@ -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
@@ -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::<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),
@@ -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.
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<ast::Attribute> {
         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<DefIndex, $T:ty>),+ $(,)?) => {
+    ($($name:ident: Table<$IDX:ty, $T:ty>),+ $(,)?) => {
         #[derive(MetadataEncodable, MetadataDecodable)]
         crate struct LazyTables<'tcx> {
-            $($name: Lazy!(Table<DefIndex, $T>)),+
+            $($name: Lazy!(Table<$IDX, $T>)),+
         }
 
         #[derive(Default)]
         struct TableBuilders<'tcx> {
-            $($name: TableBuilder<DefIndex, $T>),+
+            $($name: TableBuilder<$IDX, $T>),+
         }
 
         impl TableBuilders<'tcx> {
@@ -315,6 +315,7 @@ define_tables! {
     // definitions from any given crate.
     def_keys: Table<DefIndex, Lazy<DefKey>>,
     def_path_hashes: Table<DefIndex, Lazy<DefPathHash>>,
+    proc_macro_quoted_spans: Table<usize, Lazy<Span>>,
 }
 
 #[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<FxHashSet<Span>>,
     /// 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<Vec<Span>>,
 }
 
 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<Span> {
+        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<String>;
+                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::<crate::TokenStream>().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::<crate::TokenStream>()
+}
+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<Span> {
             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<Span> {
         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);
             }