Skip to content

Add 'Span::len()' and 'Span::trimmed()' proc-macro methods. #54373

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 39 additions & 1 deletion src/libproc_macro/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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<Span> {
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")]
Expand Down
48 changes: 48 additions & 0 deletions src/test/ui-fulldeps/proc-macro/auxiliary/trimmed-span.rs
Original file line number Diff line number Diff line change
@@ -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 <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 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()
}
39 changes: 39 additions & 0 deletions src/test/ui-fulldeps/proc-macro/trimmed-span.rs
Original file line number Diff line number Diff line change
@@ -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 <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: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
}
98 changes: 98 additions & 0 deletions src/test/ui-fulldeps/proc-macro/trimmed-span.stderr
Original file line number Diff line number Diff line change
@@ -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