diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs index 1a0dde3ccd7c3..1555bf2573cbc 100644 --- a/src/libproc_macro/lib.rs +++ b/src/libproc_macro/lib.rs @@ -59,7 +59,7 @@ use syntax::errors::DiagnosticBuilder; use syntax::parse::{self, token}; use syntax::symbol::Symbol; use syntax::tokenstream::{self, DelimSpan}; -use syntax_pos::{Pos, FileName}; +use syntax_pos::{Pos, BytePos, FileName}; /// The main type provided by this crate, representing an abstract stream of /// tokens, or, more specifically, a sequence of token trees. @@ -282,6 +282,12 @@ macro_rules! diagnostic_method { } impl Span { + /// Returns the length, in bytes of the span. + #[unstable(feature = "proc_macro_span", issue = "38356")] + pub fn len(&self) -> usize { + self.0.hi().to_usize() - self.0.lo().to_usize() + } + /// A span that resolves at the macro definition site. #[unstable(feature = "proc_macro_span", issue = "38356")] pub fn def_site() -> Span { @@ -353,6 +359,38 @@ impl Span { Some(Span(self.0.to(other.0))) } + /// Creates a new span by trimming `self` on the left by `left` bytes and on + /// the right by `right` bytes. Returns `None` if the would-be trimmed span + /// is outside the bounds of `self`. + #[unstable(feature = "proc_macro_span", issue = "38356")] + pub fn trimmed(&self, left: usize, right: usize) -> Option { + if left > u32::max_value() as usize || right > u32::max_value() as usize { + return None; + } + + // Ensure that the addition won't overflow. + let (inner, left, right) = (self.0, left as u32, right as u32); + if u32::max_value() - left < inner.lo().to_u32() { + return None; + } + + // Ensure that the subtraction won't underflow. + if right > inner.hi().to_u32() { + return None; + } + + // Compute the new lo and hi. + let new_lo = inner.lo() + BytePos::from_u32(left); + let new_hi = inner.hi() - BytePos::from_u32(right); + + // Ensure we're still inside the old `Span` and didn't cross paths. + if new_lo >= new_hi { + return None; + } + + Some(Span(inner.with_lo(new_lo).with_hi(new_hi))) + } + /// Creates a new span with the same line/column information as `self` but /// that resolves symbols as though it were at `other`. #[unstable(feature = "proc_macro_span", issue = "38356")] diff --git a/src/test/ui-fulldeps/proc-macro/auxiliary/trimmed-span.rs b/src/test/ui-fulldeps/proc-macro/auxiliary/trimmed-span.rs new file mode 100644 index 0000000000000..e0d0354c23256 --- /dev/null +++ b/src/test/ui-fulldeps/proc-macro/auxiliary/trimmed-span.rs @@ -0,0 +1,48 @@ +// Copyright 2017 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 remaining = string.len() - (index + 2); + let sub_span = lit.span().trimmed(index, remaining).unwrap(); + spans.push(sub_span); + } + + 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 trimmed(input: TokenStream) -> TokenStream { + if let Err(diag) = parse(input) { + diag.emit(); + } + + TokenStream::new() +} diff --git a/src/test/ui-fulldeps/proc-macro/trimmed-span.rs b/src/test/ui-fulldeps/proc-macro/trimmed-span.rs new file mode 100644 index 0000000000000..08a5f296a3554 --- /dev/null +++ b/src/test/ui-fulldeps/proc-macro/trimmed-span.rs @@ -0,0 +1,39 @@ +// Copyright 2016 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:trimmed-span.rs +// ignore-stage1 + +#![feature(proc_macro_non_items)] + +extern crate trimmed_span; + +use trimmed_span::trimmed; + +fn main() { + // This one emits no error. + trimmed!(""); + + // Exactly one 'hi'. + trimmed!("hi"); //~ ERROR found 'hi's + + // Now two, back to back. + trimmed!("hihi"); //~ ERROR found 'hi's + + // Now three, back to back. + trimmed!("hihihi"); //~ ERROR found 'hi's + + // Now several, with spacing. + trimmed!("why I hide? hi!"); //~ ERROR found 'hi's + trimmed!("hey, hi, hidy, hidy, hi hi"); //~ ERROR found 'hi's + trimmed!("this is a hi, and this is another hi"); //~ ERROR found 'hi's + trimmed!("how are you this evening"); //~ ERROR found 'hi's + trimmed!("this is highly eradic"); //~ ERROR found 'hi's +} diff --git a/src/test/ui-fulldeps/proc-macro/trimmed-span.stderr b/src/test/ui-fulldeps/proc-macro/trimmed-span.stderr new file mode 100644 index 0000000000000..6be8eb3b5374e --- /dev/null +++ b/src/test/ui-fulldeps/proc-macro/trimmed-span.stderr @@ -0,0 +1,98 @@ +error: found 'hi's + --> $DIR/trimmed-span.rs:25:5 + | +LL | trimmed!("hi"); //~ ERROR found 'hi's + | ^^^^^^^^^^^^^^^ + | +note: here + --> $DIR/trimmed-span.rs:25:15 + | +LL | trimmed!("hi"); //~ ERROR found 'hi's + | ^^ + +error: found 'hi's + --> $DIR/trimmed-span.rs:28:5 + | +LL | trimmed!("hihi"); //~ ERROR found 'hi's + | ^^^^^^^^^^^^^^^^^ + | +note: here + --> $DIR/trimmed-span.rs:28:15 + | +LL | trimmed!("hihi"); //~ ERROR found 'hi's + | ^^^^ + +error: found 'hi's + --> $DIR/trimmed-span.rs:31:5 + | +LL | trimmed!("hihihi"); //~ ERROR found 'hi's + | ^^^^^^^^^^^^^^^^^^^ + | +note: here + --> $DIR/trimmed-span.rs:31:15 + | +LL | trimmed!("hihihi"); //~ ERROR found 'hi's + | ^^^^^^ + +error: found 'hi's + --> $DIR/trimmed-span.rs:34:5 + | +LL | trimmed!("why I hide? hi!"); //~ ERROR found 'hi's + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: here + --> $DIR/trimmed-span.rs:34:21 + | +LL | trimmed!("why I hide? hi!"); //~ ERROR found 'hi's + | ^^ ^^ + +error: found 'hi's + --> $DIR/trimmed-span.rs:35:5 + | +LL | trimmed!("hey, hi, hidy, hidy, hi hi"); //~ ERROR found 'hi's + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: here + --> $DIR/trimmed-span.rs:35:20 + | +LL | trimmed!("hey, hi, hidy, hidy, hi hi"); //~ ERROR found 'hi's + | ^^ ^^ ^^ ^^ ^^ + +error: found 'hi's + --> $DIR/trimmed-span.rs:36:5 + | +LL | trimmed!("this is a hi, and this is another hi"); //~ ERROR found 'hi's + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: here + --> $DIR/trimmed-span.rs:36:16 + | +LL | trimmed!("this is a hi, and this is another hi"); //~ ERROR found 'hi's + | ^^ ^^ ^^ ^^ + +error: found 'hi's + --> $DIR/trimmed-span.rs:37:5 + | +LL | trimmed!("how are you this evening"); //~ ERROR found 'hi's + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: here + --> $DIR/trimmed-span.rs:37:28 + | +LL | trimmed!("how are you this evening"); //~ ERROR found 'hi's + | ^^ + +error: found 'hi's + --> $DIR/trimmed-span.rs:38:5 + | +LL | trimmed!("this is highly eradic"); //~ ERROR found 'hi's + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: here + --> $DIR/trimmed-span.rs:38:16 + | +LL | trimmed!("this is highly eradic"); //~ ERROR found 'hi's + | ^^ ^^ + +error: aborting due to 8 previous errors +