From 09e7051b7eb1d2aa1e1d34b7b09a594cba901141 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Tue, 20 Nov 2018 21:12:30 -0800 Subject: [PATCH] Add unstable Literal::subspan(). --- src/libproc_macro/lib.rs | 47 ++++++++++- src/test/ui-fulldeps/auxiliary/subspan.rs | 47 +++++++++++ src/test/ui-fulldeps/subspan.rs | 37 +++++++++ src/test/ui-fulldeps/subspan.stderr | 98 +++++++++++++++++++++++ 4 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 src/test/ui-fulldeps/auxiliary/subspan.rs create mode 100644 src/test/ui-fulldeps/subspan.rs create mode 100644 src/test/ui-fulldeps/subspan.stderr diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs index 60b6a8bac41..e9d2d97e364 100644 --- a/src/libproc_macro/lib.rs +++ b/src/libproc_macro/lib.rs @@ -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>(&self, range: R) -> Option { + 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 index 00000000000..134b04d7333 --- /dev/null +++ b/src/test/ui-fulldeps/auxiliary/subspan.rs @@ -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 or the MIT license +// , 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 index 00000000000..437123ca479 --- /dev/null +++ b/src/test/ui-fulldeps/subspan.rs @@ -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 or the MIT license +// , 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 index 00000000000..4d3928cae72 --- /dev/null +++ b/src/test/ui-fulldeps/subspan.stderr @@ -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 + -- 2.44.0