Skip to content
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

proc_macro::Span::at_start and at_end #53904

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 41 additions & 1 deletion src/libproc_macro/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ use syntax::errors::DiagnosticBuilder;
use syntax::parse::{self, token};
use syntax::symbol::Symbol;
use syntax::tokenstream;
use syntax_pos::{Pos, FileName};
use syntax_pos::{BytePos, Pos, FileName};

/// The main type provided by this crate, representing an abstract stream of
/// tokens, or, more specifically, a sequence of token trees.
Expand Down Expand Up @@ -348,6 +348,46 @@ impl Span {
}
}

/// Produces a span pointing to the first byte of this span.
///
/// When used on the span of a [`Group`], the first byte is the opening
/// delimiter of the group. If we have a span pointing to the `(self)`
/// argument list in the line below, the span produced by `at_start()` would
/// be:
///
/// ```text
/// pub fn at_start(self) -> Span {
/// ^
/// ```
#[stable(feature = "proc_macro_span_start_end", since = "1.30.0")]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about something more generic like

pub fn sub_span(self, begin: usize, end: usize) -> Span

and, optionally,

pub fn length(&self) -> usize

This would then also solve my use case of pointing an error to part of a template string like the compiler can do

  |
6 |     format!("{.}", 1);
  |               ^ expected `}` in format string

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@parched
Yeah, len() + Index<RangeBounds<usize>> for spans, basically - span[a .. b], span[a..].
Except not Index exactly, because it must return a reference.

That would be a small, self-contained, but powerful API.
I like it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a safer addition than combining >1 spans or arbitrary arithmetic with lo/hi for spans, because we have only one hygienic context that's inherited, and the input span is valid and continuous, so the resulting span will be valid as well.

pub fn at_start(self) -> Span {
let lo = self.0.lo();
let new_hi = BytePos::from_usize(lo.to_usize() + 1);
Span(self.0.with_hi(new_hi))
}

/// Produces a span pointing to the last byte of this span.
///
/// When used on the span of a [`Group`], the last byte is the closing
/// delimiter of the group. If we have a span pointing to the `(self)`
/// argument list in the line below, the span produced by `at_end()` would
/// be:
///
/// ```text
/// pub fn at_end(self) -> Span {
/// ^
/// ```
#[stable(feature = "proc_macro_span_start_end", since = "1.30.0")]
pub fn at_end(self) -> Span {
let hi = self.0.hi();
if hi.to_usize() == 0 {
self
} else {
let new_lo = BytePos::from_usize(hi.to_usize() - 1);
Span(self.0.with_lo(new_lo))
}
}

/// Create a new span encompassing `self` and `other`.
///
/// Returns `None` if `self` and `other` are from different files.
Expand Down