about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDavid Tolnay <dtolnay@gmail.com>2018-09-02 06:59:02 -0700
committerDavid Tolnay <dtolnay@gmail.com>2018-09-02 14:34:45 -0700
commitc5a561c0abe432006063408dc3371891585319a6 (patch)
tree20d680284235efa0a4598baef109764d493f81f5
parent3480ac2a805f8755f41dba2f081446df9d572986 (diff)
downloadrust-c5a561c0abe432006063408dc3371891585319a6.tar.gz
rust-c5a561c0abe432006063408dc3371891585319a6.zip
proc_macro::Group::span_open and span_close
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 group.span_open() and group.span_close() which access
the Span associated with just the opening delimiter and just the closing
delimiter of the group. Relevant to Syn as we implement real error
messages for when parsing fails in a procedural macro.
-rw-r--r--src/libproc_macro/lib.rs43
1 files changed, 42 insertions, 1 deletions
diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs
index 8c667d2f871..8ed7b8f52ba 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.
@@ -671,11 +671,52 @@ impl Group {
 
     /// Returns the span for the delimiters of this token stream, spanning the
     /// entire `Group`.
+    ///
+    /// ```text
+    /// pub fn span(&self) -> Span {
+    ///            ^^^^^^^
+    /// ```
     #[stable(feature = "proc_macro_lib2", since = "1.29.0")]
     pub fn span(&self) -> Span {
         self.span
     }
 
+    /// Returns the span pointing to the opening delimiter of this group, or the
+    /// span of the entire group if this is a None-delimited group.
+    ///
+    /// ```text
+    /// pub fn span_open(&self) -> Span {
+    ///                 ^
+    /// ```
+    #[unstable(feature = "proc_macro_span", issue = "38356")]
+    pub fn span_open(&self) -> Span {
+        if self.delimiter == Delimiter::None {
+            self.span
+        } else {
+            let lo = self.span.0.lo();
+            let new_hi = BytePos::from_usize(lo.to_usize() + 1);
+            Span(self.span.0.with_hi(new_hi))
+        }
+    }
+
+    /// Returns the span pointing to the closing delimiter of this group, or the
+    /// span of the entire group if this is a None-delimited group.
+    ///
+    /// ```text
+    /// pub fn span_close(&self) -> Span {
+    ///                        ^
+    /// ```
+    #[unstable(feature = "proc_macro_span", issue = "38356")]
+    pub fn span_close(&self) -> Span {
+        let hi = self.span.0.hi();
+        if self.delimiter == Delimiter::None || hi.to_usize() == 0 {
+            self.span
+        } else {
+            let new_lo = BytePos::from_usize(hi.to_usize() - 1);
+            Span(self.span.0.with_lo(new_lo))
+        }
+    }
+
     /// Configures the span for this `Group`'s delimiters, but not its internal
     /// tokens.
     ///