-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement span quoting for proc-macros #84278
Implement span quoting for proc-macros #84278
Conversation
r? @estebank (rust-highfive has picked a reviewer for you, use r? to override) |
cc @dtolnay - from what I can tell, the |
This comment has been minimized.
This comment has been minimized.
@@ -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 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even though this is unstable, how will this change affect the ecosystem? I'm guessing some nightly only crates will stop working until they update their deps, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A search of Github didn't turn up any results (other than library/proc_macro/src/quote.rs
in forks of rustc): https://github.com/search?l=Rust&p=1&q=quote_span&type=Code
I would be very surprised if anyone was using this:
- It currently doesn't do anything, so it's easier to manually use
Span::call_site()
orSpan::def_site()
- This is only useful if you're writing your own custom quote macro - as far as I know, the
quote
crate is the only custom macro there is, and it doesn't usequote_span
.
Triage: CI is still red here. |
ecd65e5
to
a714345
Compare
This comment has been minimized.
This comment has been minimized.
@bors r+ rollup=never |
📌 Commit 74e6edf8f6469664470e598c3bfd88342954b4e7 has been approved by |
⌛ Testing commit 74e6edf8f6469664470e598c3bfd88342954b4e7 with merge ee28776c5a047da768ba6f418ba0317b6b868567... |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
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`.
The spans generated by `quote!` are (intentionally) no longer all the same, so I removed that check entirely.
74e6edf
to
dbf4910
Compare
@bors r=estebank |
📌 Commit dbf4910 has been approved by |
I'm still interested in reviewing this PR before it's merged, and understanding why it requires such invasive changes and keeping new data for proc macros. |
☀️ Test successful - checks-actions |
@petrochenkov It looks like you commented just after I re-approved it, so it got merged anyway. Did you want to revert this, or are there some specific cleanups you'd like me to make in a follow-up PR? |
I didn't look at this in detail yet, will look next weekend (since this is already merged and it's too late to hurry). |
Cleanup span quoting I finally got to reviewing rust-lang#84278. See the individual commit messages. r? `@Aaron1011`
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:Here,
MissingType
occurs inside the implementation of the proc-macro#[error_from_attribute]
. Previosuly, this would always result in aspan 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:
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 toproc_macro::Span::recover_proc_macro_span(id)
, whereid
is anopaque identifier for the span in the crate metadata.
and invoked by some consumer crate), the call to
proc_macro::Span::recover_proc_macro_span
causes us to load the spanfrom the proc-macro crate's metadata. The proc-macro then produces a
TokenStream
containing aSpan
pointing into the proc-macro crateitself.
The recursive nature of 'quote!' can be difficult to understand at
first. The file
src/test/ui/proc-macro/quote-debug.stdout
showsthe output of the
quote!
macro, which should make this eaier tounderstand.
This PR also supports custom quoting spans in custom quote macros (e.g.
the
quote
crate). All span quoting goes through theproc_macro::quote_span
method, which can be called by a custom quotemacro 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-macrossupport renaming the
proc_macro
crate, so we can't simply hardcodethis path. Previously, the
quote_span
method used the pathcrate::Span
- however, this only works when it is called by thebuiltin
quote!
macro in the same crate. To support being called fromarbitrary crates, we need access to the name of the
proc_macro
crateto generate a path. This PR adds an additional argument to
quote_span
to specify the name of the
proc_macro
crate. Howver, this feels kindof hacky, and we may want to change this before stabilizing anything
quote-related.
Additionally, using
quote_span
currently requires enabling theproc_macro_internals
feature. The builtinquote!
macrohas an
#[allow_internal_unstable]
attribute, but this won't work forcustom quote implementations. This will likely require some additional
tricks to apply
allow_internal_unstable
to the span ofproc_macro::Span::recover_proc_macro_span
.