- 
                Notifications
    You must be signed in to change notification settings 
- Fork 13.9k
Implement span quoting for proc-macros #84278
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
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.
        
          
                library/proc_macro/src/quote.rs
              
                Outdated
          
        
      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 quotecrate 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,
MissingTypeoccurs 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), whereidis anopaque identifier for the span in the crate metadata.
and invoked by some consumer crate), the call to
proc_macro::Span::recover_proc_macro_spancauses us to load the spanfrom the proc-macro crate's metadata. The proc-macro then produces a
TokenStreamcontaining aSpanpointing 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.stdoutshowsthe 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
quotecrate). All span quoting goes through theproc_macro::quote_spanmethod, 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.rsCustom 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_macrocrate, so we can't simply hardcodethis path. Previously, the
quote_spanmethod 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_macrocrateto generate a path. This PR adds an additional argument to
quote_spanto specify the name of the
proc_macrocrate. Howver, this feels kindof hacky, and we may want to change this before stabilizing anything
quote-related.
Additionally, using
quote_spancurrently requires enabling theproc_macro_internalsfeature. 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_unstableto the span ofproc_macro::Span::recover_proc_macro_span.