Validate input in Term::new
diff --git a/src/stable.rs b/src/stable.rs
index abfeab3..ad9870c 100644
--- a/src/stable.rs
+++ b/src/stable.rs
@@ -403,6 +403,8 @@
impl Term {
pub fn new(string: &str, span: Span) -> Term {
+ validate_term(string);
+
Term {
intern: SYMBOLS.with(|s| s.borrow_mut().intern(string)),
span: span,
@@ -426,6 +428,42 @@
}
}
+fn validate_term(string: &str) {
+ let validate = if string.starts_with('\'') {
+ &string[1..]
+ } else if string.starts_with("r#") {
+ &string[2..]
+ } else {
+ string
+ };
+
+ if validate.is_empty() {
+ panic!("Term is not allowed to be empty; use Option<Term>");
+ }
+
+ if validate.bytes().all(|digit| digit >= b'0' && digit <= b'9') {
+ panic!("Term cannot be a number; use Literal instead");
+ }
+
+ fn xid_ok(string: &str) -> bool {
+ let mut chars = string.chars();
+ let first = chars.next().unwrap();
+ if !(UnicodeXID::is_xid_start(first) || first == '_') {
+ return false;
+ }
+ for ch in chars {
+ if !UnicodeXID::is_xid_continue(ch) {
+ return false;
+ }
+ }
+ true
+ }
+
+ if !xid_ok(validate) {
+ panic!("{:?} is not a valid Term", string);
+ }
+}
+
impl fmt::Debug for Term {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("Term").field(&self.as_str()).finish()
diff --git a/tests/test.rs b/tests/test.rs
index 7699d53..caed47c 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -5,9 +5,78 @@
use proc_macro2::{Literal, Span, Term, TokenStream, TokenTree};
#[test]
-fn symbols() {
- assert_eq!(Term::new("foo", Span::call_site()).as_str(), "foo");
- assert_eq!(Term::new("bar", Span::call_site()).as_str(), "bar");
+fn terms() {
+ assert_eq!(Term::new("String", Span::call_site()).as_str(), "String");
+ assert_eq!(Term::new("fn", Span::call_site()).as_str(), "fn");
+ assert_eq!(Term::new("_", Span::call_site()).as_str(), "_");
+}
+
+#[test]
+fn raw_terms() {
+ assert_eq!(Term::new("r#String", Span::call_site()).as_str(), "r#String");
+ assert_eq!(Term::new("r#fn", Span::call_site()).as_str(), "r#fn");
+ assert_eq!(Term::new("r#_", Span::call_site()).as_str(), "r#_");
+}
+
+#[test]
+fn lifetimes() {
+ assert_eq!(Term::new("'a", Span::call_site()).as_str(), "'a");
+ assert_eq!(Term::new("'static", Span::call_site()).as_str(), "'static");
+ assert_eq!(Term::new("'_", Span::call_site()).as_str(), "'_");
+}
+
+#[test]
+#[should_panic(expected = "Term is not allowed to be empty; use Option<Term>")]
+fn term_empty() {
+ Term::new("", Span::call_site());
+}
+
+#[test]
+#[should_panic(expected = "Term cannot be a number; use Literal instead")]
+fn term_number() {
+ Term::new("255", Span::call_site());
+}
+
+#[test]
+#[should_panic(expected = "\"a#\" is not a valid Term")]
+fn term_invalid() {
+ Term::new("a#", Span::call_site());
+}
+
+#[test]
+#[should_panic(expected = "Term is not allowed to be empty; use Option<Term>")]
+fn raw_term_empty() {
+ Term::new("r#", Span::call_site());
+}
+
+#[test]
+#[should_panic(expected = "Term cannot be a number; use Literal instead")]
+fn raw_term_number() {
+ Term::new("r#255", Span::call_site());
+}
+
+#[test]
+#[should_panic(expected = "\"r#a#\" is not a valid Term")]
+fn raw_term_invalid() {
+ Term::new("r#a#", Span::call_site());
+}
+
+#[test]
+#[should_panic(expected = "Term is not allowed to be empty; use Option<Term>")]
+fn lifetime_empty() {
+ Term::new("'", Span::call_site());
+}
+
+#[test]
+#[should_panic(expected = "Term cannot be a number; use Literal instead")]
+fn lifetime_number() {
+ Term::new("'255", Span::call_site());
+}
+
+#[test]
+#[should_panic(expected = r#""\'a#" is not a valid Term"#)]
+fn lifetime_invalid() {
+ Term::new("'a#", Span::call_site());
}
#[test]