diff --git a/src/fallback.rs b/src/fallback.rs index 50d10db3..3d2feae1 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 { + 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 9dec3096..ccfea451 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 { + 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 e5caed83..13a05a89 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 { +pub(crate) fn literal(input: Cursor) -> PResult { 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 24d86e87..2829dd79 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 { 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 for Literal { } } +impl FromStr for Literal { + type Err = LexError; + + fn from_str(repr: &str) -> Result { + 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 { diff --git a/tests/test.rs b/tests/test.rs index 6d0a93eb..75a880f5 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::().is_ok()); + assert!("1.0".parse::().is_ok()); + assert!("'a'".parse::().is_ok()); + assert!("\"\n\"".parse::().is_ok()); + assert!("0 1".parse::().is_err()); + assert!(" 0".parse::().is_err()); + assert!("0 ".parse::().is_err()); + assert!("/* comment */0".parse::().is_err()); + assert!("0/* comment */".parse::().is_err()); + assert!("0// comment".parse::().is_err()); +} + #[test] fn roundtrip() { fn roundtrip(p: &str) {