Skip to content

Add proc_macro::Group::{set_span_open,set_span_close} #800

Description

@Noratrieb

Proposal

Problem statement

proc_macro::Group represents a delimited group of tokens. This group has two important spans: the span of the opening token, and the span of the closing token. In early Rust, this was exposed with only a single span for both tokens.

Then in rust-lang/rust#53902 (and stabilized in rust-lang/rust#86136 in 1.55) two new methods were added to Group: span_open and span_close, representing the separate spans for the two.

This allows macros to use those separate Group spans to map spans more precisely, resulting in better error messages.

One thing that these methods do not allow is creating new Groups with those different spans, as the only way to set the span of group delimiters is using Group::set_span, which sets it for both. This means that functions like syn::token::Brace::surround cannot produce accurate spans, resulting in worse error messages.
In the case of attribute macros, having the exact same spans before and after expansion is especially important to avoid duplicate diagnostics, but in all cases better spans will result in better error messages.

Motivating examples or use cases

When using Brace::surround in tracing::instrument to re-build the body (tokio-rs/tracing#3557), the error message span is worse.

Expected:

error: expected type, found `}`
 --> src/main.rs:4:1
  |
3 |     let x:
  |          - while parsing the type for `x`
4 | }
  | ^ expected type

Actual:

error: expected type, found `}`
 --> src/main.rs:2:11
  |
2 |   fn main() {
  |  ___________^
3 | |     let x:
  | |          - while parsing the type for `x`
4 | | }
  | |_^ expected type

Solution sketch

// proc_macro

impl Group {
    /// Configures the span for this `Group`'s opening delimiter.
    ///
    /// ```text
    /// pub fn span_open(&self) -> Span {
    ///                 ^
    /// ```
    pub fn set_span_open(&mut self, span: Span);

    /// Configures the span for this `Group`'s closing delimiter.
    ///
    /// ```text
    /// pub fn span_close(&self) -> Span {
    ///                        ^
    /// ```
    pub fn set_span_close(&mut self, span: Span);
}

I have branches with draft implementations:

Alternatives

Proc-macros can sometimes do less manipulation of the token trees and preserve the originals to avoid needing this. But that will neither be easy nor desirable in all cases.

Links and related work

I could not find any mention of such APIs on the original tracking issue rust-lang/rust#54725, which might in part be because that tracking issue is a complete mess.

What happens now?

This issue contains an API change proposal (or ACP) and is part of the libs-api team feature lifecycle. Once this issue is filed, the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.

Possible responses

The libs team may respond in various different ways. First, the team will consider the problem (this doesn't require any concrete solution or alternatives to have been proposed):

  • We think this problem seems worth solving, and the standard library might be the right place to solve it.
  • We think that this probably doesn't belong in the standard library.

Second, if there's a concrete solution:

  • We think this specific solution looks roughly right, approved, you or someone else should implement this. (Further review will still happen on the subsequent implementation PR.)
  • We're not sure this is the right solution, and the alternatives or other materials don't give us enough information to be sure about that. Here are some questions we have that aren't answered, or rough ideas about alternatives we'd want to see discussed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    T-libs-apiapi-change-proposalA proposal to add or alter unstable APIs in the standard libraries

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions