]> git.lizzy.rs Git - rust.git/commitdiff
Add unstable Literal::subspan().
authorSergio Benitez <sb@sergio.bz>
Wed, 21 Nov 2018 05:12:30 +0000 (21:12 -0800)
committerSergio Benitez <sb@sergio.bz>
Wed, 21 Nov 2018 05:17:20 +0000 (21:17 -0800)
src/libproc_macro/lib.rs
src/test/ui-fulldeps/auxiliary/subspan.rs [new file with mode: 0644]
src/test/ui-fulldeps/subspan.rs [new file with mode: 0644]
src/test/ui-fulldeps/subspan.stderr [new file with mode: 0644]

index 60b6a8bac41d399c59b21b9847333eb154394970..e9d2d97e3646bada963ff2b1a8c48c839a9735aa 100644 (file)
@@ -50,6 +50,7 @@
 #[unstable(feature = "proc_macro_diagnostic", issue = "54140")]
 pub use diagnostic::{Diagnostic, Level, MultiSpan};
 
+use std::ops::{Bound, RangeBounds};
 use std::{ascii, fmt, iter};
 use std::path::PathBuf;
 use rustc_data_structures::sync::Lrc;
@@ -59,7 +60,7 @@
 use syntax::parse::{self, token};
 use syntax::symbol::Symbol;
 use syntax::tokenstream::{self, DelimSpan};
-use syntax_pos::{Pos, FileName};
+use syntax_pos::{Pos, FileName, BytePos};
 
 /// The main type provided by this crate, representing an abstract stream of
 /// tokens, or, more specifically, a sequence of token trees.
@@ -1168,6 +1169,50 @@ pub fn span(&self) -> Span {
     pub fn set_span(&mut self, span: Span) {
         self.span = span;
     }
+
+    /// Returns a `Span` that is a subset of `self.span()` containing only the
+    /// source bytes in range `range`. Returns `None` if the would-be trimmed
+    /// span is outside the bounds of `self`.
+    // FIXME(SergioBenitez): check that the byte range starts and ends at a
+    // UTF-8 boundary of the source. otherwise, it's likely that a panic will
+    // occur elsewhere when the source text is printed.
+    // FIXME(SergioBenitez): there is no way for the user to know what
+    // `self.span()` actually maps to, so this method can currently only be
+    // called blindly. For example, `to_string()` for the character 'c' returns
+    // "'\u{63}'"; there is no way for the user to know whether the source text
+    // was 'c' or whether it was '\u{63}'.
+    #[unstable(feature = "proc_macro_span", issue = "54725")]
+    pub fn subspan<R: RangeBounds<usize>>(&self, range: R) -> Option<Span> {
+        let inner = self.span().0;
+        let length = inner.hi().to_usize() - inner.lo().to_usize();
+
+        let start = match range.start_bound() {
+            Bound::Included(&lo) => lo,
+            Bound::Excluded(&lo) => lo + 1,
+            Bound::Unbounded => 0,
+        };
+
+        let end = match range.end_bound() {
+            Bound::Included(&hi) => hi + 1,
+            Bound::Excluded(&hi) => hi,
+            Bound::Unbounded => length,
+        };
+
+        // Bounds check the values, preventing addition overflow and OOB spans.
+        if start > u32::max_value() as usize
+            || end > u32::max_value() as usize
+            || (u32::max_value() - start as u32) < inner.lo().to_u32()
+            || (u32::max_value() - end as u32) < inner.lo().to_u32()
+            || start >= end
+            || end > length
+        {
+            return None;
+        }
+
+        let new_lo = inner.lo() + BytePos::from_usize(start);
+        let new_hi = inner.lo() + BytePos::from_usize(end);
+        Some(Span(inner.with_lo(new_lo).with_hi(new_hi)))
+    }
 }
 
 /// Prints the literal as a string that should be losslessly convertible
diff --git a/src/test/ui-fulldeps/auxiliary/subspan.rs b/src/test/ui-fulldeps/auxiliary/subspan.rs
new file mode 100644 (file)
index 0000000..134b04d
--- /dev/null
@@ -0,0 +1,47 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+#![feature(proc_macro_diagnostic, proc_macro_span)]
+
+extern crate proc_macro;
+
+use proc_macro::{TokenStream, TokenTree, Span, Diagnostic};
+
+fn parse(input: TokenStream) -> Result<(), Diagnostic> {
+    if let Some(TokenTree::Literal(lit)) = input.into_iter().next() {
+        let mut spans = vec![];
+        let string = lit.to_string();
+        for hi in string.matches("hi") {
+            let index = hi.as_ptr() as usize - string.as_ptr() as usize;
+            let subspan = lit.subspan(index..(index + hi.len())).unwrap();
+            spans.push(subspan);
+        }
+
+        if !spans.is_empty() {
+            Err(Span::call_site().error("found 'hi's").span_note(spans, "here"))
+        } else {
+            Ok(())
+        }
+    } else {
+        Err(Span::call_site().error("invalid input: expected string literal"))
+    }
+}
+
+#[proc_macro]
+pub fn subspan(input: TokenStream) -> TokenStream {
+    if let Err(diag) = parse(input) {
+        diag.emit();
+    }
+
+    TokenStream::new()
+}
diff --git a/src/test/ui-fulldeps/subspan.rs b/src/test/ui-fulldeps/subspan.rs
new file mode 100644 (file)
index 0000000..437123c
--- /dev/null
@@ -0,0 +1,37 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// aux-build:subspan.rs
+// ignore-stage1
+
+extern crate subspan;
+
+use subspan::subspan;
+
+// This one emits no error.
+subspan!("");
+
+// Exactly one 'hi'.
+subspan!("hi"); //~ ERROR found 'hi's
+
+// Now two, back to back.
+subspan!("hihi"); //~ ERROR found 'hi's
+
+// Now three, back to back.
+subspan!("hihihi"); //~ ERROR found 'hi's
+
+// Now several, with spacing.
+subspan!("why I hide? hi!"); //~ ERROR found 'hi's
+subspan!("hey, hi, hidy, hidy, hi hi"); //~ ERROR found 'hi's
+subspan!("this is a hi, and this is another hi"); //~ ERROR found 'hi's
+subspan!("how are you this evening"); //~ ERROR found 'hi's
+subspan!("this is highly eradic"); //~ ERROR found 'hi's
+
+fn main() { }
diff --git a/src/test/ui-fulldeps/subspan.stderr b/src/test/ui-fulldeps/subspan.stderr
new file mode 100644 (file)
index 0000000..4d3928c
--- /dev/null
@@ -0,0 +1,98 @@
+error: found 'hi's
+  --> $DIR/subspan.rs:22:1
+   |
+LL | subspan!("hi"); //~ ERROR found 'hi's
+   | ^^^^^^^^^^^^^^^
+   |
+note: here
+  --> $DIR/subspan.rs:22:11
+   |
+LL | subspan!("hi"); //~ ERROR found 'hi's
+   |           ^^
+
+error: found 'hi's
+  --> $DIR/subspan.rs:25:1
+   |
+LL | subspan!("hihi"); //~ ERROR found 'hi's
+   | ^^^^^^^^^^^^^^^^^
+   |
+note: here
+  --> $DIR/subspan.rs:25:11
+   |
+LL | subspan!("hihi"); //~ ERROR found 'hi's
+   |           ^^^^
+
+error: found 'hi's
+  --> $DIR/subspan.rs:28:1
+   |
+LL | subspan!("hihihi"); //~ ERROR found 'hi's
+   | ^^^^^^^^^^^^^^^^^^^
+   |
+note: here
+  --> $DIR/subspan.rs:28:11
+   |
+LL | subspan!("hihihi"); //~ ERROR found 'hi's
+   |           ^^^^^^
+
+error: found 'hi's
+  --> $DIR/subspan.rs:31:1
+   |
+LL | subspan!("why I hide? hi!"); //~ ERROR found 'hi's
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: here
+  --> $DIR/subspan.rs:31:17
+   |
+LL | subspan!("why I hide? hi!"); //~ ERROR found 'hi's
+   |                 ^^    ^^
+
+error: found 'hi's
+  --> $DIR/subspan.rs:32:1
+   |
+LL | subspan!("hey, hi, hidy, hidy, hi hi"); //~ ERROR found 'hi's
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: here
+  --> $DIR/subspan.rs:32:16
+   |
+LL | subspan!("hey, hi, hidy, hidy, hi hi"); //~ ERROR found 'hi's
+   |                ^^  ^^    ^^    ^^ ^^
+
+error: found 'hi's
+  --> $DIR/subspan.rs:33:1
+   |
+LL | subspan!("this is a hi, and this is another hi"); //~ ERROR found 'hi's
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: here
+  --> $DIR/subspan.rs:33:12
+   |
+LL | subspan!("this is a hi, and this is another hi"); //~ ERROR found 'hi's
+   |            ^^       ^^       ^^             ^^
+
+error: found 'hi's
+  --> $DIR/subspan.rs:34:1
+   |
+LL | subspan!("how are you this evening"); //~ ERROR found 'hi's
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: here
+  --> $DIR/subspan.rs:34:24
+   |
+LL | subspan!("how are you this evening"); //~ ERROR found 'hi's
+   |                        ^^
+
+error: found 'hi's
+  --> $DIR/subspan.rs:35:1
+   |
+LL | subspan!("this is highly eradic"); //~ ERROR found 'hi's
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: here
+  --> $DIR/subspan.rs:35:12
+   |
+LL | subspan!("this is highly eradic"); //~ ERROR found 'hi's
+   |            ^^     ^^
+
+error: aborting due to 8 previous errors
+