Skip to content

Commit 489c642

Browse files
committed
Validate input in Term::new
1 parent 6de5129 commit 489c642

File tree

2 files changed

+110
-3
lines changed

2 files changed

+110
-3
lines changed

src/stable.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,8 @@ thread_local!(static SYMBOLS: RefCell<Interner> = RefCell::new(Interner::new()))
403403

404404
impl Term {
405405
pub fn new(string: &str, span: Span) -> Term {
406+
validate_term(string);
407+
406408
Term {
407409
intern: SYMBOLS.with(|s| s.borrow_mut().intern(string)),
408410
span: span,
@@ -426,6 +428,42 @@ impl Term {
426428
}
427429
}
428430

431+
fn validate_term(string: &str) {
432+
let validate = if string.starts_with('\'') {
433+
&string[1..]
434+
} else if string.starts_with("r#") {
435+
&string[2..]
436+
} else {
437+
string
438+
};
439+
440+
if validate.is_empty() {
441+
panic!("Term is not allowed to be empty; use Option<Term>");
442+
}
443+
444+
if validate.bytes().all(|digit| digit >= b'0' && digit <= b'9') {
445+
panic!("Term cannot be a number; use Literal instead");
446+
}
447+
448+
fn xid_ok(string: &str) -> bool {
449+
let mut chars = string.chars();
450+
let first = chars.next().unwrap();
451+
if !(UnicodeXID::is_xid_start(first) || first == '_') {
452+
return false;
453+
}
454+
for ch in chars {
455+
if !UnicodeXID::is_xid_continue(ch) {
456+
return false;
457+
}
458+
}
459+
true
460+
}
461+
462+
if !xid_ok(validate) {
463+
panic!("{:?} is not a valid Term", string);
464+
}
465+
}
466+
429467
impl fmt::Debug for Term {
430468
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
431469
f.debug_tuple("Term").field(&self.as_str()).finish()

tests/test.rs

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,78 @@ use std::str::{self, FromStr};
55
use proc_macro2::{Literal, Span, Term, TokenStream, TokenTree};
66

77
#[test]
8-
fn symbols() {
9-
assert_eq!(Term::new("foo", Span::call_site()).as_str(), "foo");
10-
assert_eq!(Term::new("bar", Span::call_site()).as_str(), "bar");
8+
fn terms() {
9+
assert_eq!(Term::new("String", Span::call_site()).as_str(), "String");
10+
assert_eq!(Term::new("fn", Span::call_site()).as_str(), "fn");
11+
assert_eq!(Term::new("_", Span::call_site()).as_str(), "_");
12+
}
13+
14+
#[test]
15+
fn raw_terms() {
16+
assert_eq!(Term::new("r#String", Span::call_site()).as_str(), "r#String");
17+
assert_eq!(Term::new("r#fn", Span::call_site()).as_str(), "r#fn");
18+
assert_eq!(Term::new("r#_", Span::call_site()).as_str(), "r#_");
19+
}
20+
21+
#[test]
22+
fn lifetimes() {
23+
assert_eq!(Term::new("'a", Span::call_site()).as_str(), "'a");
24+
assert_eq!(Term::new("'static", Span::call_site()).as_str(), "'static");
25+
assert_eq!(Term::new("'_", Span::call_site()).as_str(), "'_");
26+
}
27+
28+
#[test]
29+
#[should_panic(expected = "Term is not allowed to be empty; use Option<Term>")]
30+
fn term_empty() {
31+
Term::new("", Span::call_site());
32+
}
33+
34+
#[test]
35+
#[should_panic(expected = "Term cannot be a number; use Literal instead")]
36+
fn term_number() {
37+
Term::new("255", Span::call_site());
38+
}
39+
40+
#[test]
41+
#[should_panic(expected = "\"a#\" is not a valid Term")]
42+
fn term_invalid() {
43+
Term::new("a#", Span::call_site());
44+
}
45+
46+
#[test]
47+
#[should_panic(expected = "Term is not allowed to be empty; use Option<Term>")]
48+
fn raw_term_empty() {
49+
Term::new("r#", Span::call_site());
50+
}
51+
52+
#[test]
53+
#[should_panic(expected = "Term cannot be a number; use Literal instead")]
54+
fn raw_term_number() {
55+
Term::new("r#255", Span::call_site());
56+
}
57+
58+
#[test]
59+
#[should_panic(expected = "\"r#a#\" is not a valid Term")]
60+
fn raw_term_invalid() {
61+
Term::new("r#a#", Span::call_site());
62+
}
63+
64+
#[test]
65+
#[should_panic(expected = "Term is not allowed to be empty; use Option<Term>")]
66+
fn lifetime_empty() {
67+
Term::new("'", Span::call_site());
68+
}
69+
70+
#[test]
71+
#[should_panic(expected = "Term cannot be a number; use Literal instead")]
72+
fn lifetime_number() {
73+
Term::new("'255", Span::call_site());
74+
}
75+
76+
#[test]
77+
#[should_panic(expected = r#""\'a#" is not a valid Term"#)]
78+
fn lifetime_invalid() {
79+
Term::new("'a#", Span::call_site());
1180
}
1281

1382
#[test]

0 commit comments

Comments
 (0)