From 6a641338babf741bcdf8905b174c912b69a4e860 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 2 Sep 2018 07:38:25 -0700 Subject: [PATCH] proc_macro::Span::at_start and at_end Before this addition, every delimited group like (...) [...] {...} has only a single Span that covers the full source location from opening delimiter to closing delimiter. This makes it impossible for a procedural macro to trigger an error pointing to just the opening or closing delimiter. The Rust compiler does not seem to have the same limitation: mod m { type T = } error: expected type, found `}` --> src/main.rs:3:1 | 3 | } | ^ On that same input, a procedural macro would be forced to trigger the error on the last token inside the block, on the entire block, or on the next token after the block, none of which is really what you want for an error like above. This commit adds span.at_start() and span.at_end() which access the Span associated with just the first byte (opening delimiter) and just the last byte (closing delimiter) of the group. Relevant to Syn as we implement real error messages for when parsing fails in a procedural macro. --- src/libproc_macro/lib.rs | 42 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs index 8c667d2f87111..4afd8e9214f32 100644 --- a/src/libproc_macro/lib.rs +++ b/src/libproc_macro/lib.rs @@ -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. @@ -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")] + 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.