Skip to content

Commit 09e7051

Browse files
committed
Add unstable Literal::subspan().
1 parent f1e2fa8 commit 09e7051

File tree

4 files changed

+228
-1
lines changed

4 files changed

+228
-1
lines changed

src/libproc_macro/lib.rs

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ mod diagnostic;
5050
#[unstable(feature = "proc_macro_diagnostic", issue = "54140")]
5151
pub use diagnostic::{Diagnostic, Level, MultiSpan};
5252

53+
use std::ops::{Bound, RangeBounds};
5354
use std::{ascii, fmt, iter};
5455
use std::path::PathBuf;
5556
use rustc_data_structures::sync::Lrc;
@@ -59,7 +60,7 @@ use syntax::errors::DiagnosticBuilder;
5960
use syntax::parse::{self, token};
6061
use syntax::symbol::Symbol;
6162
use syntax::tokenstream::{self, DelimSpan};
62-
use syntax_pos::{Pos, FileName};
63+
use syntax_pos::{Pos, FileName, BytePos};
6364

6465
/// The main type provided by this crate, representing an abstract stream of
6566
/// tokens, or, more specifically, a sequence of token trees.
@@ -1168,6 +1169,50 @@ impl Literal {
11681169
pub fn set_span(&mut self, span: Span) {
11691170
self.span = span;
11701171
}
1172+
1173+
/// Returns a `Span` that is a subset of `self.span()` containing only the
1174+
/// source bytes in range `range`. Returns `None` if the would-be trimmed
1175+
/// span is outside the bounds of `self`.
1176+
// FIXME(SergioBenitez): check that the byte range starts and ends at a
1177+
// UTF-8 boundary of the source. otherwise, it's likely that a panic will
1178+
// occur elsewhere when the source text is printed.
1179+
// FIXME(SergioBenitez): there is no way for the user to know what
1180+
// `self.span()` actually maps to, so this method can currently only be
1181+
// called blindly. For example, `to_string()` for the character 'c' returns
1182+
// "'\u{63}'"; there is no way for the user to know whether the source text
1183+
// was 'c' or whether it was '\u{63}'.
1184+
#[unstable(feature = "proc_macro_span", issue = "54725")]
1185+
pub fn subspan<R: RangeBounds<usize>>(&self, range: R) -> Option<Span> {
1186+
let inner = self.span().0;
1187+
let length = inner.hi().to_usize() - inner.lo().to_usize();
1188+
1189+
let start = match range.start_bound() {
1190+
Bound::Included(&lo) => lo,
1191+
Bound::Excluded(&lo) => lo + 1,
1192+
Bound::Unbounded => 0,
1193+
};
1194+
1195+
let end = match range.end_bound() {
1196+
Bound::Included(&hi) => hi + 1,
1197+
Bound::Excluded(&hi) => hi,
1198+
Bound::Unbounded => length,
1199+
};
1200+
1201+
// Bounds check the values, preventing addition overflow and OOB spans.
1202+
if start > u32::max_value() as usize
1203+
|| end > u32::max_value() as usize
1204+
|| (u32::max_value() - start as u32) < inner.lo().to_u32()
1205+
|| (u32::max_value() - end as u32) < inner.lo().to_u32()
1206+
|| start >= end
1207+
|| end > length
1208+
{
1209+
return None;
1210+
}
1211+
1212+
let new_lo = inner.lo() + BytePos::from_usize(start);
1213+
let new_hi = inner.lo() + BytePos::from_usize(end);
1214+
Some(Span(inner.with_lo(new_lo).with_hi(new_hi)))
1215+
}
11711216
}
11721217

11731218
/// Prints the literal as a string that should be losslessly convertible
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// no-prefer-dynamic
12+
13+
#![crate_type = "proc-macro"]
14+
#![feature(proc_macro_diagnostic, proc_macro_span)]
15+
16+
extern crate proc_macro;
17+
18+
use proc_macro::{TokenStream, TokenTree, Span, Diagnostic};
19+
20+
fn parse(input: TokenStream) -> Result<(), Diagnostic> {
21+
if let Some(TokenTree::Literal(lit)) = input.into_iter().next() {
22+
let mut spans = vec![];
23+
let string = lit.to_string();
24+
for hi in string.matches("hi") {
25+
let index = hi.as_ptr() as usize - string.as_ptr() as usize;
26+
let subspan = lit.subspan(index..(index + hi.len())).unwrap();
27+
spans.push(subspan);
28+
}
29+
30+
if !spans.is_empty() {
31+
Err(Span::call_site().error("found 'hi's").span_note(spans, "here"))
32+
} else {
33+
Ok(())
34+
}
35+
} else {
36+
Err(Span::call_site().error("invalid input: expected string literal"))
37+
}
38+
}
39+
40+
#[proc_macro]
41+
pub fn subspan(input: TokenStream) -> TokenStream {
42+
if let Err(diag) = parse(input) {
43+
diag.emit();
44+
}
45+
46+
TokenStream::new()
47+
}

src/test/ui-fulldeps/subspan.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// aux-build:subspan.rs
12+
// ignore-stage1
13+
14+
extern crate subspan;
15+
16+
use subspan::subspan;
17+
18+
// This one emits no error.
19+
subspan!("");
20+
21+
// Exactly one 'hi'.
22+
subspan!("hi"); //~ ERROR found 'hi's
23+
24+
// Now two, back to back.
25+
subspan!("hihi"); //~ ERROR found 'hi's
26+
27+
// Now three, back to back.
28+
subspan!("hihihi"); //~ ERROR found 'hi's
29+
30+
// Now several, with spacing.
31+
subspan!("why I hide? hi!"); //~ ERROR found 'hi's
32+
subspan!("hey, hi, hidy, hidy, hi hi"); //~ ERROR found 'hi's
33+
subspan!("this is a hi, and this is another hi"); //~ ERROR found 'hi's
34+
subspan!("how are you this evening"); //~ ERROR found 'hi's
35+
subspan!("this is highly eradic"); //~ ERROR found 'hi's
36+
37+
fn main() { }

src/test/ui-fulldeps/subspan.stderr

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
error: found 'hi's
2+
--> $DIR/subspan.rs:22:1
3+
|
4+
LL | subspan!("hi"); //~ ERROR found 'hi's
5+
| ^^^^^^^^^^^^^^^
6+
|
7+
note: here
8+
--> $DIR/subspan.rs:22:11
9+
|
10+
LL | subspan!("hi"); //~ ERROR found 'hi's
11+
| ^^
12+
13+
error: found 'hi's
14+
--> $DIR/subspan.rs:25:1
15+
|
16+
LL | subspan!("hihi"); //~ ERROR found 'hi's
17+
| ^^^^^^^^^^^^^^^^^
18+
|
19+
note: here
20+
--> $DIR/subspan.rs:25:11
21+
|
22+
LL | subspan!("hihi"); //~ ERROR found 'hi's
23+
| ^^^^
24+
25+
error: found 'hi's
26+
--> $DIR/subspan.rs:28:1
27+
|
28+
LL | subspan!("hihihi"); //~ ERROR found 'hi's
29+
| ^^^^^^^^^^^^^^^^^^^
30+
|
31+
note: here
32+
--> $DIR/subspan.rs:28:11
33+
|
34+
LL | subspan!("hihihi"); //~ ERROR found 'hi's
35+
| ^^^^^^
36+
37+
error: found 'hi's
38+
--> $DIR/subspan.rs:31:1
39+
|
40+
LL | subspan!("why I hide? hi!"); //~ ERROR found 'hi's
41+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
42+
|
43+
note: here
44+
--> $DIR/subspan.rs:31:17
45+
|
46+
LL | subspan!("why I hide? hi!"); //~ ERROR found 'hi's
47+
| ^^ ^^
48+
49+
error: found 'hi's
50+
--> $DIR/subspan.rs:32:1
51+
|
52+
LL | subspan!("hey, hi, hidy, hidy, hi hi"); //~ ERROR found 'hi's
53+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
54+
|
55+
note: here
56+
--> $DIR/subspan.rs:32:16
57+
|
58+
LL | subspan!("hey, hi, hidy, hidy, hi hi"); //~ ERROR found 'hi's
59+
| ^^ ^^ ^^ ^^ ^^
60+
61+
error: found 'hi's
62+
--> $DIR/subspan.rs:33:1
63+
|
64+
LL | subspan!("this is a hi, and this is another hi"); //~ ERROR found 'hi's
65+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66+
|
67+
note: here
68+
--> $DIR/subspan.rs:33:12
69+
|
70+
LL | subspan!("this is a hi, and this is another hi"); //~ ERROR found 'hi's
71+
| ^^ ^^ ^^ ^^
72+
73+
error: found 'hi's
74+
--> $DIR/subspan.rs:34:1
75+
|
76+
LL | subspan!("how are you this evening"); //~ ERROR found 'hi's
77+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
78+
|
79+
note: here
80+
--> $DIR/subspan.rs:34:24
81+
|
82+
LL | subspan!("how are you this evening"); //~ ERROR found 'hi's
83+
| ^^
84+
85+
error: found 'hi's
86+
--> $DIR/subspan.rs:35:1
87+
|
88+
LL | subspan!("this is highly eradic"); //~ ERROR found 'hi's
89+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
90+
|
91+
note: here
92+
--> $DIR/subspan.rs:35:12
93+
|
94+
LL | subspan!("this is highly eradic"); //~ ERROR found 'hi's
95+
| ^^ ^^
96+
97+
error: aborting due to 8 previous errors
98+

0 commit comments

Comments
 (0)