Repository: alexcrichton/proc-macro2
Pull request #286 (2021-05-20 02:38:11)

impl FromStr for Literal
Corresponds to the libproc_macro impl added by https://github.com/rust-lang/rust/pull/84717.


Patch:
diff --git a/src/fallback.rs b/src/fallback.rs
index 50d10db..3d2feae 100644
--- a/src/fallback.rs
+++ b/src/fallback.rs
@@ -43,6 +43,12 @@ impl LexError {
     pub(crate) fn span(&self) -> Span {
         self.span
     }
+
+    fn call_site() -> Self {
+        LexError {
+            span: Span::call_site(),
+        }
+    }
 }
 
 impl TokenStream {
@@ -887,6 +893,20 @@ impl Literal {
     }
 }
 
+impl FromStr for Literal {
+    type Err = LexError;
+
+    fn from_str(repr: &str) -> Result<Self, Self::Err> {
+        let cursor = get_cursor(repr);
+        if let Ok((_rest, literal)) = parse::literal(cursor) {
+            if literal.text.len() == repr.len() {
+                return Ok(literal);
+            }
+        }
+        Err(LexError::call_site())
+    }
+}
+
 impl Display for Literal {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         Display::fmt(&self.text, f)
diff --git a/src/lib.rs b/src/lib.rs
index 9dec309..ccfea45 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1203,6 +1203,17 @@ impl Literal {
     }
 }
 
+impl FromStr for Literal {
+    type Err = LexError;
+
+    fn from_str(repr: &str) -> Result<Self, LexError> {
+        repr.parse().map(Literal::_new).map_err(|inner| LexError {
+            inner,
+            _marker: Marker,
+        })
+    }
+}
+
 impl Debug for Literal {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         Debug::fmt(&self.inner, f)
diff --git a/src/parse.rs b/src/parse.rs
index e5caed8..13a05a8 100644
--- a/src/parse.rs
+++ b/src/parse.rs
@@ -59,7 +59,7 @@ impl<'a> Cursor<'a> {
     }
 }
 
-struct Reject;
+pub(crate) struct Reject;
 type PResult<'a, O> = Result<(Cursor<'a>, O), Reject>;
 
 fn skip_whitespace(input: Cursor) -> Cursor {
@@ -310,7 +310,7 @@ fn ident_not_raw(input: Cursor) -> PResult<&str> {
     Ok((input.advance(end), &input.rest[..end]))
 }
 
-fn literal(input: Cursor) -> PResult<Literal> {
+pub(crate) fn literal(input: Cursor) -> PResult<Literal> {
     let rest = literal_nocapture(input)?;
     let end = input.len() - rest.len();
     Ok((rest, Literal::_new(input.rest[..end].to_string())))
diff --git a/src/wrapper.rs b/src/wrapper.rs
index 24d86e8..2829dd7 100644
--- a/src/wrapper.rs
+++ b/src/wrapper.rs
@@ -29,6 +29,14 @@ pub(crate) enum LexError {
     Fallback(fallback::LexError),
 }
 
+impl LexError {
+    fn call_site() -> Self {
+        LexError::Fallback(fallback::LexError {
+            span: fallback::Span::call_site(),
+        })
+    }
+}
+
 fn mismatch() -> ! {
     panic!("stable/nightly mismatch")
 }
@@ -108,11 +116,7 @@ impl FromStr for TokenStream {
 // Work around https://github.com/rust-lang/rust/issues/58736.
 fn proc_macro_parse(src: &str) -> Result<proc_macro::TokenStream, LexError> {
     let result = panic::catch_unwind(|| src.parse().map_err(LexError::Compiler));
-    result.unwrap_or_else(|_| {
-        Err(LexError::Fallback(fallback::LexError {
-            span: fallback::Span::call_site(),
-        }))
-    })
+    result.unwrap_or_else(|_| Err(LexError::call_site()))
 }
 
 impl Display for TokenStream {
@@ -912,6 +916,30 @@ impl From<fallback::Literal> for Literal {
     }
 }
 
+impl FromStr for Literal {
+    type Err = LexError;
+
+    fn from_str(repr: &str) -> Result<Self, Self::Err> {
+        if inside_proc_macro() {
+            // TODO: use libproc_macro's FromStr impl once it is available in
+            // rustc. https://github.com/rust-lang/rust/pull/84717
+            let tokens = proc_macro_parse(repr)?;
+            let mut iter = tokens.into_iter();
+            if let (Some(proc_macro::TokenTree::Literal(literal)), None) =
+                (iter.next(), iter.next())
+            {
+                if literal.to_string().len() == repr.len() {
+                    return Ok(Literal::Compiler(literal));
+                }
+            }
+            Err(LexError::call_site())
+        } else {
+            let literal = fallback::Literal::from_str(repr)?;
+            Ok(Literal::Fallback(literal))
+        }
+    }
+}
+
 impl Display for Literal {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match self {


Test patch:
diff --git a/tests/test.rs b/tests/test.rs
index 6d0a93e..75a880f 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -163,6 +163,20 @@ fn literal_iter_negative() {
     assert!(iter.next().is_none());
 }
 
+#[test]
+fn literal_parse() {
+    assert!("1".parse::<Literal>().is_ok());
+    assert!("1.0".parse::<Literal>().is_ok());
+    assert!("'a'".parse::<Literal>().is_ok());
+    assert!("\"\n\"".parse::<Literal>().is_ok());
+    assert!("0 1".parse::<Literal>().is_err());
+    assert!(" 0".parse::<Literal>().is_err());
+    assert!("0 ".parse::<Literal>().is_err());
+    assert!("/* comment */0".parse::<Literal>().is_err());
+    assert!("0/* comment */".parse::<Literal>().is_err());
+    assert!("0// comment".parse::<Literal>().is_err());
+}
+
 #[test]
 fn roundtrip() {
     fn roundtrip(p: &str) {