]> git.proxmox.com Git - rustc.git/blobdiff - vendor/syn/src/lit.rs
New upstream version 1.52.0~beta.3+dfsg1
[rustc.git] / vendor / syn / src / lit.rs
index d4670c0d7a6a260ec86e0560bfee33406588ff01..0aa50a590c9f4f578e7c12ca225641772f117f70 100644 (file)
-// Copyright 2018 Syn Developers
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-use proc_macro2::{Literal, Span};
-use std::str;
-
+#[cfg(feature = "parsing")]
+use crate::lookahead;
+#[cfg(feature = "parsing")]
+use crate::parse::{Parse, Parser};
+use crate::{Error, Result};
 #[cfg(feature = "printing")]
 use proc_macro2::Ident;
-
 #[cfg(feature = "parsing")]
 use proc_macro2::TokenStream;
-#[cfg(feature = "parsing")]
-use Error;
-
 use proc_macro2::TokenTree;
-
+use proc_macro2::{Literal, Span};
+use std::fmt::{self, Display};
 #[cfg(feature = "extra-traits")]
 use std::hash::{Hash, Hasher};
-
-#[cfg(feature = "parsing")]
-use lookahead;
-#[cfg(feature = "parsing")]
-use parse::Parse;
+use std::str::{self, FromStr};
 
 ast_enum_of_structs! {
     /// A Rust literal such as a string or integer or boolean.
     ///
-    /// *This type is available if Syn is built with the `"derive"` or `"full"`
-    /// feature.*
-    ///
     /// # Syntax tree enum
     ///
     /// This type is a [syntax tree enum].
     ///
-    /// [syntax tree enum]: enum.Expr.html#syntax-tree-enums
+    /// [syntax tree enum]: crate::Expr#syntax-tree-enums
     pub enum Lit {
         /// A UTF-8 string literal: `"foo"`.
-        ///
-        /// *This type is available if Syn is built with the `"derive"` or
-        /// `"full"` feature.*
-        pub Str(LitStr #manual_extra_traits {
-            token: Literal,
-        }),
+        Str(LitStr),
 
         /// A byte string literal: `b"foo"`.
-        ///
-        /// *This type is available if Syn is built with the `"derive"` or
-        /// `"full"` feature.*
-        pub ByteStr(LitByteStr #manual_extra_traits {
-            token: Literal,
-        }),
+        ByteStr(LitByteStr),
 
         /// A byte literal: `b'f'`.
-        ///
-        /// *This type is available if Syn is built with the `"derive"` or
-        /// `"full"` feature.*
-        pub Byte(LitByte #manual_extra_traits {
-            token: Literal,
-        }),
+        Byte(LitByte),
 
         /// A character literal: `'a'`.
-        ///
-        /// *This type is available if Syn is built with the `"derive"` or
-        /// `"full"` feature.*
-        pub Char(LitChar #manual_extra_traits {
-            token: Literal,
-        }),
+        Char(LitChar),
 
         /// An integer literal: `1` or `1u16`.
-        ///
-        /// Holds up to 64 bits of data. Use `LitVerbatim` for any larger
-        /// integer literal.
-        ///
-        /// *This type is available if Syn is built with the `"derive"` or
-        /// `"full"` feature.*
-        pub Int(LitInt #manual_extra_traits {
-            token: Literal,
-        }),
+        Int(LitInt),
 
         /// A floating point literal: `1f64` or `1.0e10f64`.
         ///
         /// Must be finite. May not be infinte or NaN.
-        ///
-        /// *This type is available if Syn is built with the `"derive"` or
-        /// `"full"` feature.*
-        pub Float(LitFloat #manual_extra_traits {
-            token: Literal,
-        }),
+        Float(LitFloat),
 
         /// A boolean literal: `true` or `false`.
-        ///
-        /// *This type is available if Syn is built with the `"derive"` or
-        /// `"full"` feature.*
-        pub Bool(LitBool #manual_extra_traits {
-            pub value: bool,
-            pub span: Span,
-        }),
-
-        /// A raw token literal not interpreted by Syn, possibly because it
-        /// represents an integer larger than 64 bits.
-        ///
-        /// *This type is available if Syn is built with the `"derive"` or
-        /// `"full"` feature.*
-        pub Verbatim(LitVerbatim #manual_extra_traits {
-            pub token: Literal,
-        }),
+        Bool(LitBool),
+
+        /// A raw token literal not interpreted by Syn.
+        Verbatim(Literal),
+    }
+}
+
+ast_struct! {
+    /// A UTF-8 string literal: `"foo"`.
+    pub struct LitStr {
+        repr: Box<LitRepr>,
+    }
+}
+
+ast_struct! {
+    /// A byte string literal: `b"foo"`.
+    pub struct LitByteStr {
+        repr: Box<LitRepr>,
+    }
+}
+
+ast_struct! {
+    /// A byte literal: `b'f'`.
+    pub struct LitByte {
+        repr: Box<LitRepr>,
+    }
+}
+
+ast_struct! {
+    /// A character literal: `'a'`.
+    pub struct LitChar {
+        repr: Box<LitRepr>,
+    }
+}
+
+struct LitRepr {
+    token: Literal,
+    suffix: Box<str>,
+}
+
+ast_struct! {
+    /// An integer literal: `1` or `1u16`.
+    pub struct LitInt {
+        repr: Box<LitIntRepr>,
+    }
+}
+
+struct LitIntRepr {
+    token: Literal,
+    digits: Box<str>,
+    suffix: Box<str>,
+}
+
+ast_struct! {
+    /// A floating point literal: `1f64` or `1.0e10f64`.
+    ///
+    /// Must be finite. May not be infinite or NaN.
+    pub struct LitFloat {
+        repr: Box<LitFloatRepr>,
+    }
+}
+
+struct LitFloatRepr {
+    token: Literal,
+    digits: Box<str>,
+    suffix: Box<str>,
+}
+
+ast_struct! {
+    /// A boolean literal: `true` or `false`.
+    pub struct LitBool {
+        pub value: bool,
+        pub span: Span,
     }
 }
 
 impl LitStr {
     pub fn new(value: &str, span: Span) -> Self {
-        let mut lit = Literal::string(value);
-        lit.set_span(span);
-        LitStr { token: lit }
+        let mut token = Literal::string(value);
+        token.set_span(span);
+        LitStr {
+            repr: Box::new(LitRepr {
+                token,
+                suffix: Box::<str>::default(),
+            }),
+        }
     }
 
     pub fn value(&self) -> String {
-        value::parse_lit_str(&self.token.to_string())
+        let repr = self.repr.token.to_string();
+        let (value, _suffix) = value::parse_lit_str(&repr);
+        String::from(value)
     }
 
     /// Parse a syntax tree node from the content of this string literal.
@@ -130,9 +145,6 @@ impl LitStr {
     /// # Example
     ///
     /// ```
-    /// # extern crate proc_macro2;
-    /// # extern crate syn;
-    /// #
     /// use proc_macro2::Span;
     /// use syn::{Attribute, Error, Ident, Lit, Meta, MetaNameValue, Path, Result};
     ///
@@ -151,23 +163,46 @@ impl LitStr {
     ///             lit_str.parse().map(Some)
     ///         }
     ///         _ => {
-    ///             let error_span = attr.bracket_token.span;
     ///             let message = "expected #[path = \"...\"]";
-    ///             Err(Error::new(error_span, message))
+    ///             Err(Error::new_spanned(attr, message))
     ///         }
     ///     }
     /// }
     /// ```
     #[cfg(feature = "parsing")]
-    pub fn parse<T: Parse>(&self) -> Result<T, Error> {
-        use proc_macro2::Group;
+    #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
+    pub fn parse<T: Parse>(&self) -> Result<T> {
+        self.parse_with(T::parse)
+    }
 
-        // Parse string literal into a token stream with every span equal to the
-        // original literal's span.
-        fn spanned_tokens(s: &LitStr) -> Result<TokenStream, Error> {
-            let stream = ::parse_str(&s.value())?;
-            Ok(respan_token_stream(stream, s.span()))
-        }
+    /// Invoke parser on the content of this string literal.
+    ///
+    /// All spans in the syntax tree will point to the span of this `LitStr`.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// # use proc_macro2::Span;
+    /// # use syn::{LitStr, Result};
+    /// #
+    /// # fn main() -> Result<()> {
+    /// #     let lit_str = LitStr::new("a::b::c", Span::call_site());
+    /// #
+    /// #     const IGNORE: &str = stringify! {
+    /// let lit_str: LitStr = /* ... */;
+    /// #     };
+    ///
+    /// // Parse a string literal like "a::b::c" into a Path, not allowing
+    /// // generic arguments on any of the path segments.
+    /// let basic_path = lit_str.parse_with(syn::Path::parse_mod_style)?;
+    /// #
+    /// #     Ok(())
+    /// # }
+    /// ```
+    #[cfg(feature = "parsing")]
+    #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
+    pub fn parse_with<F: Parser>(&self, parser: F) -> Result<F::Output> {
+        use proc_macro2::Group;
 
         // Token stream with every span replaced by the given one.
         fn respan_token_stream(stream: TokenStream, span: Span) -> TokenStream {
@@ -179,26 +214,35 @@ impl LitStr {
 
         // Token tree with every span replaced by the given one.
         fn respan_token_tree(mut token: TokenTree, span: Span) -> TokenTree {
-            match token {
-                TokenTree::Group(ref mut g) => {
-                    let stream = respan_token_stream(g.stream().clone(), span);
+            match &mut token {
+                TokenTree::Group(g) => {
+                    let stream = respan_token_stream(g.stream(), span);
                     *g = Group::new(g.delimiter(), stream);
                     g.set_span(span);
                 }
-                ref mut other => other.set_span(span),
+                other => other.set_span(span),
             }
             token
         }
 
-        spanned_tokens(self).and_then(::parse2)
+        // Parse string literal into a token stream with every span equal to the
+        // original literal's span.
+        let mut tokens = crate::parse_str(&self.value())?;
+        tokens = respan_token_stream(tokens, self.span());
+
+        parser.parse2(tokens)
     }
 
     pub fn span(&self) -> Span {
-        self.token.span()
+        self.repr.token.span()
     }
 
     pub fn set_span(&mut self, span: Span) {
-        self.token.set_span(span)
+        self.repr.token.set_span(span)
+    }
+
+    pub fn suffix(&self) -> &str {
+        &self.repr.suffix
     }
 }
 
@@ -206,19 +250,30 @@ impl LitByteStr {
     pub fn new(value: &[u8], span: Span) -> Self {
         let mut token = Literal::byte_string(value);
         token.set_span(span);
-        LitByteStr { token: token }
+        LitByteStr {
+            repr: Box::new(LitRepr {
+                token,
+                suffix: Box::<str>::default(),
+            }),
+        }
     }
 
     pub fn value(&self) -> Vec<u8> {
-        value::parse_lit_byte_str(&self.token.to_string())
+        let repr = self.repr.token.to_string();
+        let (value, _suffix) = value::parse_lit_byte_str(&repr);
+        value
     }
 
     pub fn span(&self) -> Span {
-        self.token.span()
+        self.repr.token.span()
     }
 
     pub fn set_span(&mut self, span: Span) {
-        self.token.set_span(span)
+        self.repr.token.set_span(span)
+    }
+
+    pub fn suffix(&self) -> &str {
+        &self.repr.suffix
     }
 }
 
@@ -226,19 +281,30 @@ impl LitByte {
     pub fn new(value: u8, span: Span) -> Self {
         let mut token = Literal::u8_suffixed(value);
         token.set_span(span);
-        LitByte { token: token }
+        LitByte {
+            repr: Box::new(LitRepr {
+                token,
+                suffix: Box::<str>::default(),
+            }),
+        }
     }
 
     pub fn value(&self) -> u8 {
-        value::parse_lit_byte(&self.token.to_string())
+        let repr = self.repr.token.to_string();
+        let (value, _suffix) = value::parse_lit_byte(&repr);
+        value
     }
 
     pub fn span(&self) -> Span {
-        self.token.span()
+        self.repr.token.span()
     }
 
     pub fn set_span(&mut self, span: Span) {
-        self.token.set_span(span)
+        self.repr.token.set_span(span)
+    }
+
+    pub fn suffix(&self) -> &str {
+        &self.repr.suffix
     }
 }
 
@@ -246,132 +312,356 @@ impl LitChar {
     pub fn new(value: char, span: Span) -> Self {
         let mut token = Literal::character(value);
         token.set_span(span);
-        LitChar { token: token }
+        LitChar {
+            repr: Box::new(LitRepr {
+                token,
+                suffix: Box::<str>::default(),
+            }),
+        }
     }
 
     pub fn value(&self) -> char {
-        value::parse_lit_char(&self.token.to_string())
+        let repr = self.repr.token.to_string();
+        let (value, _suffix) = value::parse_lit_char(&repr);
+        value
     }
 
     pub fn span(&self) -> Span {
-        self.token.span()
+        self.repr.token.span()
     }
 
     pub fn set_span(&mut self, span: Span) {
-        self.token.set_span(span)
+        self.repr.token.set_span(span)
+    }
+
+    pub fn suffix(&self) -> &str {
+        &self.repr.suffix
     }
 }
 
 impl LitInt {
-    pub fn new(value: u64, suffix: IntSuffix, span: Span) -> Self {
-        let mut token = match suffix {
-            IntSuffix::Isize => Literal::isize_suffixed(value as isize),
-            IntSuffix::I8 => Literal::i8_suffixed(value as i8),
-            IntSuffix::I16 => Literal::i16_suffixed(value as i16),
-            IntSuffix::I32 => Literal::i32_suffixed(value as i32),
-            IntSuffix::I64 => Literal::i64_suffixed(value as i64),
-            IntSuffix::I128 => value::to_literal(&format!("{}i128", value)),
-            IntSuffix::Usize => Literal::usize_suffixed(value as usize),
-            IntSuffix::U8 => Literal::u8_suffixed(value as u8),
-            IntSuffix::U16 => Literal::u16_suffixed(value as u16),
-            IntSuffix::U32 => Literal::u32_suffixed(value as u32),
-            IntSuffix::U64 => Literal::u64_suffixed(value),
-            IntSuffix::U128 => value::to_literal(&format!("{}u128", value)),
-            IntSuffix::None => Literal::u64_unsuffixed(value),
+    pub fn new(repr: &str, span: Span) -> Self {
+        let (digits, suffix) = match value::parse_lit_int(repr) {
+            Some(parse) => parse,
+            None => panic!("Not an integer literal: `{}`", repr),
+        };
+
+        let mut token = match value::to_literal(repr, &digits, &suffix) {
+            Some(token) => token,
+            None => panic!("Unsupported integer literal: `{}`", repr),
         };
+
         token.set_span(span);
-        LitInt { token: token }
-    }
-
-    pub fn value(&self) -> u64 {
-        value::parse_lit_int(&self.token.to_string()).unwrap()
-    }
-
-    pub fn suffix(&self) -> IntSuffix {
-        let value = self.token.to_string();
-        for (s, suffix) in vec![
-            ("i8", IntSuffix::I8),
-            ("i16", IntSuffix::I16),
-            ("i32", IntSuffix::I32),
-            ("i64", IntSuffix::I64),
-            ("i128", IntSuffix::I128),
-            ("isize", IntSuffix::Isize),
-            ("u8", IntSuffix::U8),
-            ("u16", IntSuffix::U16),
-            ("u32", IntSuffix::U32),
-            ("u64", IntSuffix::U64),
-            ("u128", IntSuffix::U128),
-            ("usize", IntSuffix::Usize),
-        ] {
-            if value.ends_with(s) {
-                return suffix;
-            }
+        LitInt {
+            repr: Box::new(LitIntRepr {
+                token,
+                digits,
+                suffix,
+            }),
         }
-        IntSuffix::None
+    }
+
+    pub fn base10_digits(&self) -> &str {
+        &self.repr.digits
+    }
+
+    /// Parses the literal into a selected number type.
+    ///
+    /// This is equivalent to `lit.base10_digits().parse()` except that the
+    /// resulting errors will be correctly spanned to point to the literal token
+    /// in the macro input.
+    ///
+    /// ```
+    /// use syn::LitInt;
+    /// use syn::parse::{Parse, ParseStream, Result};
+    ///
+    /// struct Port {
+    ///     value: u16,
+    /// }
+    ///
+    /// impl Parse for Port {
+    ///     fn parse(input: ParseStream) -> Result<Self> {
+    ///         let lit: LitInt = input.parse()?;
+    ///         let value = lit.base10_parse::<u16>()?;
+    ///         Ok(Port { value })
+    ///     }
+    /// }
+    /// ```
+    pub fn base10_parse<N>(&self) -> Result<N>
+    where
+        N: FromStr,
+        N::Err: Display,
+    {
+        self.base10_digits()
+            .parse()
+            .map_err(|err| Error::new(self.span(), err))
+    }
+
+    pub fn suffix(&self) -> &str {
+        &self.repr.suffix
     }
 
     pub fn span(&self) -> Span {
-        self.token.span()
+        self.repr.token.span()
     }
 
     pub fn set_span(&mut self, span: Span) {
-        self.token.set_span(span)
+        self.repr.token.set_span(span)
+    }
+}
+
+impl From<Literal> for LitInt {
+    fn from(token: Literal) -> Self {
+        let repr = token.to_string();
+        if let Some((digits, suffix)) = value::parse_lit_int(&repr) {
+            LitInt {
+                repr: Box::new(LitIntRepr {
+                    token,
+                    digits,
+                    suffix,
+                }),
+            }
+        } else {
+            panic!("Not an integer literal: `{}`", repr);
+        }
+    }
+}
+
+impl Display for LitInt {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        self.repr.token.fmt(formatter)
     }
 }
 
 impl LitFloat {
-    pub fn new(value: f64, suffix: FloatSuffix, span: Span) -> Self {
-        let mut token = match suffix {
-            FloatSuffix::F32 => Literal::f32_suffixed(value as f32),
-            FloatSuffix::F64 => Literal::f64_suffixed(value),
-            FloatSuffix::None => Literal::f64_unsuffixed(value),
+    pub fn new(repr: &str, span: Span) -> Self {
+        let (digits, suffix) = match value::parse_lit_float(repr) {
+            Some(parse) => parse,
+            None => panic!("Not a float literal: `{}`", repr),
+        };
+
+        let mut token = match value::to_literal(repr, &digits, &suffix) {
+            Some(token) => token,
+            None => panic!("Unsupported float literal: `{}`", repr),
         };
+
         token.set_span(span);
-        LitFloat { token: token }
+        LitFloat {
+            repr: Box::new(LitFloatRepr {
+                token,
+                digits,
+                suffix,
+            }),
+        }
+    }
+
+    pub fn base10_digits(&self) -> &str {
+        &self.repr.digits
+    }
+
+    pub fn base10_parse<N>(&self) -> Result<N>
+    where
+        N: FromStr,
+        N::Err: Display,
+    {
+        self.base10_digits()
+            .parse()
+            .map_err(|err| Error::new(self.span(), err))
+    }
+
+    pub fn suffix(&self) -> &str {
+        &self.repr.suffix
     }
 
-    pub fn value(&self) -> f64 {
-        value::parse_lit_float(&self.token.to_string())
+    pub fn span(&self) -> Span {
+        self.repr.token.span()
+    }
+
+    pub fn set_span(&mut self, span: Span) {
+        self.repr.token.set_span(span)
     }
+}
 
-    pub fn suffix(&self) -> FloatSuffix {
-        let value = self.token.to_string();
-        for (s, suffix) in vec![("f32", FloatSuffix::F32), ("f64", FloatSuffix::F64)] {
-            if value.ends_with(s) {
-                return suffix;
+impl From<Literal> for LitFloat {
+    fn from(token: Literal) -> Self {
+        let repr = token.to_string();
+        if let Some((digits, suffix)) = value::parse_lit_float(&repr) {
+            LitFloat {
+                repr: Box::new(LitFloatRepr {
+                    token,
+                    digits,
+                    suffix,
+                }),
             }
+        } else {
+            panic!("Not a float literal: `{}`", repr);
         }
-        FloatSuffix::None
+    }
+}
+
+impl Display for LitFloat {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        self.repr.token.fmt(formatter)
+    }
+}
+
+impl LitBool {
+    pub fn new(value: bool, span: Span) -> Self {
+        LitBool { value, span }
+    }
+
+    pub fn value(&self) -> bool {
+        self.value
     }
 
     pub fn span(&self) -> Span {
-        self.token.span()
+        self.span
     }
 
     pub fn set_span(&mut self, span: Span) {
-        self.token.set_span(span)
+        self.span = span;
+    }
+}
+
+#[cfg(feature = "extra-traits")]
+mod debug_impls {
+    use super::*;
+    use std::fmt::{self, Debug};
+
+    #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
+    impl Debug for LitStr {
+        fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+            formatter
+                .debug_struct("LitStr")
+                .field("token", &format_args!("{}", self.repr.token))
+                .finish()
+        }
+    }
+
+    #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
+    impl Debug for LitByteStr {
+        fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+            formatter
+                .debug_struct("LitByteStr")
+                .field("token", &format_args!("{}", self.repr.token))
+                .finish()
+        }
+    }
+
+    #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
+    impl Debug for LitByte {
+        fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+            formatter
+                .debug_struct("LitByte")
+                .field("token", &format_args!("{}", self.repr.token))
+                .finish()
+        }
+    }
+
+    #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
+    impl Debug for LitChar {
+        fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+            formatter
+                .debug_struct("LitChar")
+                .field("token", &format_args!("{}", self.repr.token))
+                .finish()
+        }
+    }
+
+    #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
+    impl Debug for LitInt {
+        fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+            formatter
+                .debug_struct("LitInt")
+                .field("token", &format_args!("{}", self.repr.token))
+                .finish()
+        }
+    }
+
+    #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
+    impl Debug for LitFloat {
+        fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+            formatter
+                .debug_struct("LitFloat")
+                .field("token", &format_args!("{}", self.repr.token))
+                .finish()
+        }
+    }
+
+    #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
+    impl Debug for LitBool {
+        fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+            formatter
+                .debug_struct("LitBool")
+                .field("value", &self.value)
+                .finish()
+        }
+    }
+}
+
+#[cfg(feature = "clone-impls")]
+#[cfg_attr(doc_cfg, doc(cfg(feature = "clone-impls")))]
+impl Clone for LitRepr {
+    fn clone(&self) -> Self {
+        LitRepr {
+            token: self.token.clone(),
+            suffix: self.suffix.clone(),
+        }
+    }
+}
+
+#[cfg(feature = "clone-impls")]
+#[cfg_attr(doc_cfg, doc(cfg(feature = "clone-impls")))]
+impl Clone for LitIntRepr {
+    fn clone(&self) -> Self {
+        LitIntRepr {
+            token: self.token.clone(),
+            digits: self.digits.clone(),
+            suffix: self.suffix.clone(),
+        }
+    }
+}
+
+#[cfg(feature = "clone-impls")]
+#[cfg_attr(doc_cfg, doc(cfg(feature = "clone-impls")))]
+impl Clone for LitFloatRepr {
+    fn clone(&self) -> Self {
+        LitFloatRepr {
+            token: self.token.clone(),
+            digits: self.digits.clone(),
+            suffix: self.suffix.clone(),
+        }
     }
 }
 
 macro_rules! lit_extra_traits {
-    ($ty:ident, $field:ident) => {
-        #[cfg(feature = "extra-traits")]
-        impl Eq for $ty {}
+    ($ty:ident) => {
+        #[cfg(feature = "clone-impls")]
+        #[cfg_attr(doc_cfg, doc(cfg(feature = "clone-impls")))]
+        impl Clone for $ty {
+            fn clone(&self) -> Self {
+                $ty {
+                    repr: self.repr.clone(),
+                }
+            }
+        }
 
         #[cfg(feature = "extra-traits")]
+        #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
         impl PartialEq for $ty {
             fn eq(&self, other: &Self) -> bool {
-                self.$field.to_string() == other.$field.to_string()
+                self.repr.token.to_string() == other.repr.token.to_string()
             }
         }
 
         #[cfg(feature = "extra-traits")]
+        #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
         impl Hash for $ty {
             fn hash<H>(&self, state: &mut H)
             where
                 H: Hasher,
             {
-                self.$field.to_string().hash(state);
+                self.repr.token.to_string().hash(state);
             }
         }
 
@@ -384,31 +674,23 @@ macro_rules! lit_extra_traits {
     };
 }
 
-impl LitVerbatim {
-    pub fn span(&self) -> Span {
-        self.token.span()
-    }
+lit_extra_traits!(LitStr);
+lit_extra_traits!(LitByteStr);
+lit_extra_traits!(LitByte);
+lit_extra_traits!(LitChar);
+lit_extra_traits!(LitInt);
+lit_extra_traits!(LitFloat);
 
-    pub fn set_span(&mut self, span: Span) {
-        self.token.set_span(span)
-    }
+#[cfg(feature = "parsing")]
+#[doc(hidden)]
+#[allow(non_snake_case)]
+pub fn LitBool(marker: lookahead::TokenMarker) -> LitBool {
+    match marker {}
 }
 
-lit_extra_traits!(LitStr, token);
-lit_extra_traits!(LitByteStr, token);
-lit_extra_traits!(LitByte, token);
-lit_extra_traits!(LitChar, token);
-lit_extra_traits!(LitInt, token);
-lit_extra_traits!(LitFloat, token);
-lit_extra_traits!(LitBool, value);
-lit_extra_traits!(LitVerbatim, token);
-
 ast_enum! {
     /// The style of a string literal, either plain quoted or a raw string like
     /// `r##"data"##`.
-    ///
-    /// *This type is available if Syn is built with the `"derive"` or `"full"`
-    /// feature.*
     pub enum StrStyle #no_visit {
         /// An ordinary string like `"data"`.
         Cooked,
@@ -419,41 +701,6 @@ ast_enum! {
     }
 }
 
-ast_enum! {
-    /// The suffix on an integer literal if any, like the `u8` in `127u8`.
-    ///
-    /// *This type is available if Syn is built with the `"derive"` or `"full"`
-    /// feature.*
-    pub enum IntSuffix #no_visit {
-        I8,
-        I16,
-        I32,
-        I64,
-        I128,
-        Isize,
-        U8,
-        U16,
-        U32,
-        U64,
-        U128,
-        Usize,
-        None,
-    }
-}
-
-ast_enum! {
-    /// The suffix on a floating point literal if any, like the `f32` in
-    /// `1.0f32`.
-    ///
-    /// *This type is available if Syn is built with the `"derive"` or `"full"`
-    /// feature.*
-    pub enum FloatSuffix #no_visit {
-        F32,
-        F64,
-        None,
-    }
-}
-
 #[cfg(feature = "parsing")]
 #[doc(hidden)]
 #[allow(non_snake_case)]
@@ -464,98 +711,154 @@ pub fn Lit(marker: lookahead::TokenMarker) -> Lit {
 #[cfg(feature = "parsing")]
 pub mod parsing {
     use super::*;
-    use parse::{Parse, ParseStream, Result};
+    use crate::buffer::Cursor;
+    use crate::parse::{Parse, ParseStream, Result};
+    use proc_macro2::Punct;
 
+    #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
     impl Parse for Lit {
         fn parse(input: ParseStream) -> Result<Self> {
             input.step(|cursor| {
                 if let Some((lit, rest)) = cursor.literal() {
                     return Ok((Lit::new(lit), rest));
                 }
-                while let Some((ident, rest)) = cursor.ident() {
-                    let value = if ident == "true" {
-                        true
-                    } else if ident == "false" {
-                        false
-                    } else {
-                        break;
-                    };
-                    let lit_bool = LitBool {
-                        value: value,
-                        span: ident.span(),
-                    };
-                    return Ok((Lit::Bool(lit_bool), rest));
+
+                if let Some((ident, rest)) = cursor.ident() {
+                    let value = ident == "true";
+                    if value || ident == "false" {
+                        let lit_bool = LitBool {
+                            value,
+                            span: ident.span(),
+                        };
+                        return Ok((Lit::Bool(lit_bool), rest));
+                    }
+                }
+
+                if let Some((punct, rest)) = cursor.punct() {
+                    if punct.as_char() == '-' {
+                        if let Some((lit, rest)) = parse_negative_lit(punct, rest) {
+                            return Ok((lit, rest));
+                        }
+                    }
                 }
+
                 Err(cursor.error("expected literal"))
             })
         }
     }
 
+    fn parse_negative_lit(neg: Punct, cursor: Cursor) -> Option<(Lit, Cursor)> {
+        let (lit, rest) = cursor.literal()?;
+
+        let mut span = neg.span();
+        span = span.join(lit.span()).unwrap_or(span);
+
+        let mut repr = lit.to_string();
+        repr.insert(0, '-');
+
+        if let Some((digits, suffix)) = value::parse_lit_int(&repr) {
+            if let Some(mut token) = value::to_literal(&repr, &digits, &suffix) {
+                token.set_span(span);
+                return Some((
+                    Lit::Int(LitInt {
+                        repr: Box::new(LitIntRepr {
+                            token,
+                            digits,
+                            suffix,
+                        }),
+                    }),
+                    rest,
+                ));
+            }
+        }
+
+        let (digits, suffix) = value::parse_lit_float(&repr)?;
+        let mut token = value::to_literal(&repr, &digits, &suffix)?;
+        token.set_span(span);
+        Some((
+            Lit::Float(LitFloat {
+                repr: Box::new(LitFloatRepr {
+                    token,
+                    digits,
+                    suffix,
+                }),
+            }),
+            rest,
+        ))
+    }
+
+    #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
     impl Parse for LitStr {
         fn parse(input: ParseStream) -> Result<Self> {
             let head = input.fork();
-            match input.parse()? {
-                Lit::Str(lit) => Ok(lit),
+            match input.parse() {
+                Ok(Lit::Str(lit)) => Ok(lit),
                 _ => Err(head.error("expected string literal")),
             }
         }
     }
 
+    #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
     impl Parse for LitByteStr {
         fn parse(input: ParseStream) -> Result<Self> {
             let head = input.fork();
-            match input.parse()? {
-                Lit::ByteStr(lit) => Ok(lit),
+            match input.parse() {
+                Ok(Lit::ByteStr(lit)) => Ok(lit),
                 _ => Err(head.error("expected byte string literal")),
             }
         }
     }
 
+    #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
     impl Parse for LitByte {
         fn parse(input: ParseStream) -> Result<Self> {
             let head = input.fork();
-            match input.parse()? {
-                Lit::Byte(lit) => Ok(lit),
+            match input.parse() {
+                Ok(Lit::Byte(lit)) => Ok(lit),
                 _ => Err(head.error("expected byte literal")),
             }
         }
     }
 
+    #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
     impl Parse for LitChar {
         fn parse(input: ParseStream) -> Result<Self> {
             let head = input.fork();
-            match input.parse()? {
-                Lit::Char(lit) => Ok(lit),
+            match input.parse() {
+                Ok(Lit::Char(lit)) => Ok(lit),
                 _ => Err(head.error("expected character literal")),
             }
         }
     }
 
+    #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
     impl Parse for LitInt {
         fn parse(input: ParseStream) -> Result<Self> {
             let head = input.fork();
-            match input.parse()? {
-                Lit::Int(lit) => Ok(lit),
+            match input.parse() {
+                Ok(Lit::Int(lit)) => Ok(lit),
                 _ => Err(head.error("expected integer literal")),
             }
         }
     }
 
+    #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
     impl Parse for LitFloat {
         fn parse(input: ParseStream) -> Result<Self> {
             let head = input.fork();
-            match input.parse()? {
-                Lit::Float(lit) => Ok(lit),
+            match input.parse() {
+                Ok(Lit::Float(lit)) => Ok(lit),
                 _ => Err(head.error("expected floating point literal")),
             }
         }
     }
 
+    #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
     impl Parse for LitBool {
         fn parse(input: ParseStream) -> Result<Self> {
             let head = input.fork();
-            match input.parse()? {
-                Lit::Bool(lit) => Ok(lit),
+            match input.parse() {
+                Ok(Lit::Bool(lit)) => Ok(lit),
                 _ => Err(head.error("expected boolean literal")),
             }
         }
@@ -568,124 +871,167 @@ mod printing {
     use proc_macro2::TokenStream;
     use quote::{ToTokens, TokenStreamExt};
 
+    #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
     impl ToTokens for LitStr {
         fn to_tokens(&self, tokens: &mut TokenStream) {
-            self.token.to_tokens(tokens);
+            self.repr.token.to_tokens(tokens);
         }
     }
 
+    #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
     impl ToTokens for LitByteStr {
         fn to_tokens(&self, tokens: &mut TokenStream) {
-            self.token.to_tokens(tokens);
+            self.repr.token.to_tokens(tokens);
         }
     }
 
+    #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
     impl ToTokens for LitByte {
         fn to_tokens(&self, tokens: &mut TokenStream) {
-            self.token.to_tokens(tokens);
+            self.repr.token.to_tokens(tokens);
         }
     }
 
+    #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
     impl ToTokens for LitChar {
         fn to_tokens(&self, tokens: &mut TokenStream) {
-            self.token.to_tokens(tokens);
+            self.repr.token.to_tokens(tokens);
         }
     }
 
+    #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
     impl ToTokens for LitInt {
         fn to_tokens(&self, tokens: &mut TokenStream) {
-            self.token.to_tokens(tokens);
+            self.repr.token.to_tokens(tokens);
         }
     }
 
+    #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
     impl ToTokens for LitFloat {
         fn to_tokens(&self, tokens: &mut TokenStream) {
-            self.token.to_tokens(tokens);
+            self.repr.token.to_tokens(tokens);
         }
     }
 
+    #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
     impl ToTokens for LitBool {
         fn to_tokens(&self, tokens: &mut TokenStream) {
             let s = if self.value { "true" } else { "false" };
             tokens.append(Ident::new(s, self.span));
         }
     }
-
-    impl ToTokens for LitVerbatim {
-        fn to_tokens(&self, tokens: &mut TokenStream) {
-            self.token.to_tokens(tokens);
-        }
-    }
 }
 
 mod value {
     use super::*;
+    use crate::bigint::BigInt;
     use proc_macro2::TokenStream;
     use std::char;
     use std::ops::{Index, RangeFrom};
 
     impl Lit {
         /// Interpret a Syn literal from a proc-macro2 literal.
-        ///
-        /// Not all proc-macro2 literals are valid Syn literals. In particular,
-        /// doc comments are considered by proc-macro2 to be literals but in Syn
-        /// they are [`Attribute`].
-        ///
-        /// [`Attribute`]: struct.Attribute.html
-        ///
-        /// # Panics
-        ///
-        /// Panics if the input is a doc comment literal.
         pub fn new(token: Literal) -> Self {
-            let value = token.to_string();
-
-            match value::byte(&value, 0) {
-                b'"' | b'r' => return Lit::Str(LitStr { token: token }),
-                b'b' => match value::byte(&value, 1) {
-                    b'"' | b'r' => return Lit::ByteStr(LitByteStr { token: token }),
-                    b'\'' => return Lit::Byte(LitByte { token: token }),
+            let repr = token.to_string();
+
+            match byte(&repr, 0) {
+                b'"' | b'r' => {
+                    let (_, suffix) = parse_lit_str(&repr);
+                    return Lit::Str(LitStr {
+                        repr: Box::new(LitRepr { token, suffix }),
+                    });
+                }
+                b'b' => match byte(&repr, 1) {
+                    b'"' | b'r' => {
+                        let (_, suffix) = parse_lit_byte_str(&repr);
+                        return Lit::ByteStr(LitByteStr {
+                            repr: Box::new(LitRepr { token, suffix }),
+                        });
+                    }
+                    b'\'' => {
+                        let (_, suffix) = parse_lit_byte(&repr);
+                        return Lit::Byte(LitByte {
+                            repr: Box::new(LitRepr { token, suffix }),
+                        });
+                    }
                     _ => {}
                 },
-                b'\'' => return Lit::Char(LitChar { token: token }),
-                b'0'...b'9' => {
-                    if number_is_int(&value) {
-                        return Lit::Int(LitInt { token: token });
-                    } else if number_is_float(&value) {
-                        return Lit::Float(LitFloat { token: token });
-                    } else {
-                        // number overflow
-                        return Lit::Verbatim(LitVerbatim { token: token });
+                b'\'' => {
+                    let (_, suffix) = parse_lit_char(&repr);
+                    return Lit::Char(LitChar {
+                        repr: Box::new(LitRepr { token, suffix }),
+                    });
+                }
+                b'0'..=b'9' | b'-' => {
+                    if let Some((digits, suffix)) = parse_lit_int(&repr) {
+                        return Lit::Int(LitInt {
+                            repr: Box::new(LitIntRepr {
+                                token,
+                                digits,
+                                suffix,
+                            }),
+                        });
+                    }
+                    if let Some((digits, suffix)) = parse_lit_float(&repr) {
+                        return Lit::Float(LitFloat {
+                            repr: Box::new(LitFloatRepr {
+                                token,
+                                digits,
+                                suffix,
+                            }),
+                        });
                     }
                 }
-                _ => {
-                    if value == "true" || value == "false" {
+                b't' | b'f' => {
+                    if repr == "true" || repr == "false" {
                         return Lit::Bool(LitBool {
-                            value: value == "true",
+                            value: repr == "true",
                             span: token.span(),
                         });
                     }
                 }
+                _ => {}
             }
 
-            panic!("Unrecognized literal: {}", value);
+            panic!("Unrecognized literal: `{}`", repr);
         }
-    }
 
-    fn number_is_int(value: &str) -> bool {
-        if number_is_float(value) {
-            false
-        } else {
-            value::parse_lit_int(value).is_some()
+        pub fn suffix(&self) -> &str {
+            match self {
+                Lit::Str(lit) => lit.suffix(),
+                Lit::ByteStr(lit) => lit.suffix(),
+                Lit::Byte(lit) => lit.suffix(),
+                Lit::Char(lit) => lit.suffix(),
+                Lit::Int(lit) => lit.suffix(),
+                Lit::Float(lit) => lit.suffix(),
+                Lit::Bool(_) | Lit::Verbatim(_) => "",
+            }
         }
-    }
 
-    fn number_is_float(value: &str) -> bool {
-        if value.contains('.') {
-            true
-        } else if value.starts_with("0x") || value.ends_with("size") {
-            false
-        } else {
-            value.contains('e') || value.contains('E')
+        pub fn span(&self) -> Span {
+            match self {
+                Lit::Str(lit) => lit.span(),
+                Lit::ByteStr(lit) => lit.span(),
+                Lit::Byte(lit) => lit.span(),
+                Lit::Char(lit) => lit.span(),
+                Lit::Int(lit) => lit.span(),
+                Lit::Float(lit) => lit.span(),
+                Lit::Bool(lit) => lit.span,
+                Lit::Verbatim(lit) => lit.span(),
+            }
+        }
+
+        pub fn set_span(&mut self, span: Span) {
+            match self {
+                Lit::Str(lit) => lit.set_span(span),
+                Lit::ByteStr(lit) => lit.set_span(span),
+                Lit::Byte(lit) => lit.set_span(span),
+                Lit::Char(lit) => lit.set_span(span),
+                Lit::Int(lit) => lit.set_span(span),
+                Lit::Float(lit) => lit.set_span(span),
+                Lit::Bool(lit) => lit.span = span,
+                Lit::Verbatim(lit) => lit.set_span(span),
+            }
         }
     }
 
@@ -704,7 +1050,8 @@ mod value {
         s.chars().next().unwrap_or('\0')
     }
 
-    pub fn parse_lit_str(s: &str) -> String {
+    // Returns (content, suffix).
+    pub fn parse_lit_str(s: &str) -> (Box<str>, Box<str>) {
         match byte(s, 0) {
             b'"' => parse_lit_str_cooked(s),
             b'r' => parse_lit_str_raw(s),
@@ -714,12 +1061,12 @@ mod value {
 
     // Clippy false positive
     // https://github.com/rust-lang-nursery/rust-clippy/issues/2329
-    #[cfg_attr(feature = "cargo-clippy", allow(needless_continue))]
-    fn parse_lit_str_cooked(mut s: &str) -> String {
+    #[allow(clippy::needless_continue)]
+    fn parse_lit_str_cooked(mut s: &str) -> (Box<str>, Box<str>) {
         assert_eq!(byte(s, 0), b'"');
         s = &s[1..];
 
-        let mut out = String::new();
+        let mut content = String::new();
         'outer: loop {
             let ch = match byte(s, 0) {
                 b'"' => break,
@@ -767,14 +1114,16 @@ mod value {
                     ch
                 }
             };
-            out.push(ch);
+            content.push(ch);
         }
 
-        assert_eq!(s, "\"");
-        out
+        assert!(s.starts_with('"'));
+        let content = content.into_boxed_str();
+        let suffix = s[1..].to_owned().into_boxed_str();
+        (content, suffix)
     }
 
-    fn parse_lit_str_raw(mut s: &str) -> String {
+    fn parse_lit_str_raw(mut s: &str) -> (Box<str>, Box<str>) {
         assert_eq!(byte(s, 0), b'r');
         s = &s[1..];
 
@@ -783,15 +1132,18 @@ mod value {
             pounds += 1;
         }
         assert_eq!(byte(s, pounds), b'"');
-        assert_eq!(byte(s, s.len() - pounds - 1), b'"');
-        for end in s[s.len() - pounds..].bytes() {
+        let close = s.rfind('"').unwrap();
+        for end in s[close + 1..close + 1 + pounds].bytes() {
             assert_eq!(end, b'#');
         }
 
-        s[pounds + 1..s.len() - pounds - 1].to_owned()
+        let content = s[pounds + 1..close].to_owned().into_boxed_str();
+        let suffix = s[close + 1 + pounds..].to_owned().into_boxed_str();
+        (content, suffix)
     }
 
-    pub fn parse_lit_byte_str(s: &str) -> Vec<u8> {
+    // Returns (content, suffix).
+    pub fn parse_lit_byte_str(s: &str) -> (Vec<u8>, Box<str>) {
         assert_eq!(byte(s, 0), b'b');
         match byte(s, 1) {
             b'"' => parse_lit_byte_str_cooked(s),
@@ -802,26 +1154,26 @@ mod value {
 
     // Clippy false positive
     // https://github.com/rust-lang-nursery/rust-clippy/issues/2329
-    #[cfg_attr(feature = "cargo-clippy", allow(needless_continue))]
-    fn parse_lit_byte_str_cooked(mut s: &str) -> Vec<u8> {
+    #[allow(clippy::needless_continue)]
+    fn parse_lit_byte_str_cooked(mut s: &str) -> (Vec<u8>, Box<str>) {
         assert_eq!(byte(s, 0), b'b');
         assert_eq!(byte(s, 1), b'"');
         s = &s[2..];
 
         // We're going to want to have slices which don't respect codepoint boundaries.
-        let mut s = s.as_bytes();
+        let mut v = s.as_bytes();
 
         let mut out = Vec::new();
         'outer: loop {
-            let byte = match byte(s, 0) {
+            let byte = match byte(v, 0) {
                 b'"' => break,
                 b'\\' => {
-                    let b = byte(s, 1);
-                    s = &s[2..];
+                    let b = byte(v, 1);
+                    v = &v[2..];
                     match b {
                         b'x' => {
-                            let (b, rest) = backslash_x(s);
-                            s = rest;
+                            let (b, rest) = backslash_x(v);
+                            v = rest;
                             b
                         }
                         b'n' => b'\n',
@@ -832,10 +1184,10 @@ mod value {
                         b'\'' => b'\'',
                         b'"' => b'"',
                         b'\r' | b'\n' => loop {
-                            let byte = byte(s, 0);
+                            let byte = byte(v, 0);
                             let ch = char::from_u32(u32::from(byte)).unwrap();
                             if ch.is_whitespace() {
-                                s = &s[1..];
+                                v = &v[1..];
                             } else {
                                 continue 'outer;
                             }
@@ -844,42 +1196,45 @@ mod value {
                     }
                 }
                 b'\r' => {
-                    assert_eq!(byte(s, 1), b'\n', "Bare CR not allowed in string");
-                    s = &s[2..];
+                    assert_eq!(byte(v, 1), b'\n', "Bare CR not allowed in string");
+                    v = &v[2..];
                     b'\n'
                 }
                 b => {
-                    s = &s[1..];
+                    v = &v[1..];
                     b
                 }
             };
             out.push(byte);
         }
 
-        assert_eq!(s, b"\"");
-        out
+        assert_eq!(byte(v, 0), b'"');
+        let suffix = s[s.len() - v.len() + 1..].to_owned().into_boxed_str();
+        (out, suffix)
     }
 
-    fn parse_lit_byte_str_raw(s: &str) -> Vec<u8> {
+    fn parse_lit_byte_str_raw(s: &str) -> (Vec<u8>, Box<str>) {
         assert_eq!(byte(s, 0), b'b');
-        parse_lit_str_raw(&s[1..]).into_bytes()
+        let (value, suffix) = parse_lit_str_raw(&s[1..]);
+        (String::from(value).into_bytes(), suffix)
     }
 
-    pub fn parse_lit_byte(s: &str) -> u8 {
+    // Returns (value, suffix).
+    pub fn parse_lit_byte(s: &str) -> (u8, Box<str>) {
         assert_eq!(byte(s, 0), b'b');
         assert_eq!(byte(s, 1), b'\'');
 
         // We're going to want to have slices which don't respect codepoint boundaries.
-        let mut s = s[2..].as_bytes();
+        let mut v = s[2..].as_bytes();
 
-        let b = match byte(s, 0) {
+        let b = match byte(v, 0) {
             b'\\' => {
-                let b = byte(s, 1);
-                s = &s[2..];
+                let b = byte(v, 1);
+                v = &v[2..];
                 match b {
                     b'x' => {
-                        let (b, rest) = backslash_x(s);
-                        s = rest;
+                        let (b, rest) = backslash_x(v);
+                        v = rest;
                         b
                     }
                     b'n' => b'\n',
@@ -893,16 +1248,18 @@ mod value {
                 }
             }
             b => {
-                s = &s[1..];
+                v = &v[1..];
                 b
             }
         };
 
-        assert_eq!(byte(s, 0), b'\'');
-        b
+        assert_eq!(byte(v, 0), b'\'');
+        let suffix = s[s.len() - v.len() + 1..].to_owned().into_boxed_str();
+        (b, suffix)
     }
 
-    pub fn parse_lit_char(mut s: &str) -> char {
+    // Returns (value, suffix).
+    pub fn parse_lit_char(mut s: &str) -> (char, Box<str>) {
         assert_eq!(byte(s, 0), b'\'');
         s = &s[1..];
 
@@ -938,8 +1295,9 @@ mod value {
                 ch
             }
         };
-        assert_eq!(s, "\'", "Expected end of char literal");
-        ch
+        assert_eq!(byte(s, 0), b'\'');
+        let suffix = s[1..].to_owned().into_boxed_str();
+        (ch, suffix)
     }
 
     fn backslash_x<S>(s: &S) -> (u8, &S)
@@ -951,15 +1309,15 @@ mod value {
         let b1 = byte(s, 1);
         ch += 0x10
             * match b0 {
-                b'0'...b'9' => b0 - b'0',
-                b'a'...b'f' => 10 + (b0 - b'a'),
-                b'A'...b'F' => 10 + (b0 - b'A'),
+                b'0'..=b'9' => b0 - b'0',
+                b'a'..=b'f' => 10 + (b0 - b'a'),
+                b'A'..=b'F' => 10 + (b0 - b'A'),
                 _ => panic!("unexpected non-hex character after \\x"),
             };
         ch += match b1 {
-            b'0'...b'9' => b1 - b'0',
-            b'a'...b'f' => 10 + (b1 - b'a'),
-            b'A'...b'F' => 10 + (b1 - b'A'),
+            b'0'..=b'9' => b1 - b'0',
+            b'a'..=b'f' => 10 + (b1 - b'a'),
+            b'A'..=b'F' => 10 + (b1 - b'A'),
             _ => panic!("unexpected non-hex character after \\x"),
         };
         (ch, &s[2..])
@@ -967,32 +1325,33 @@ mod value {
 
     fn backslash_u(mut s: &str) -> (char, &str) {
         if byte(s, 0) != b'{' {
-            panic!("expected {{ after \\u");
+            panic!("{}", "expected { after \\u");
         }
         s = &s[1..];
 
         let mut ch = 0;
-        for _ in 0..6 {
+        let mut digits = 0;
+        loop {
             let b = byte(s, 0);
-            match b {
-                b'0'...b'9' => {
-                    ch *= 0x10;
-                    ch += u32::from(b - b'0');
-                    s = &s[1..];
-                }
-                b'a'...b'f' => {
-                    ch *= 0x10;
-                    ch += u32::from(10 + b - b'a');
-                    s = &s[1..];
-                }
-                b'A'...b'F' => {
-                    ch *= 0x10;
-                    ch += u32::from(10 + b - b'A');
+            let digit = match b {
+                b'0'..=b'9' => b - b'0',
+                b'a'..=b'f' => 10 + b - b'a',
+                b'A'..=b'F' => 10 + b - b'A',
+                b'_' if digits > 0 => {
                     s = &s[1..];
+                    continue;
                 }
+                b'}' if digits == 0 => panic!("invalid empty unicode escape"),
                 b'}' => break,
                 _ => panic!("unexpected non-hex character after \\u"),
+            };
+            if digits == 6 {
+                panic!("overlong unicode escape (must have at most 6 hex digits)");
             }
+            ch *= 0x10;
+            ch += u32::from(digit);
+            digits += 1;
+            s = &s[1..];
         }
         assert!(byte(s, 0) == b'}');
         s = &s[1..];
@@ -1004,7 +1363,13 @@ mod value {
         }
     }
 
-    pub fn parse_lit_int(mut s: &str) -> Option<u64> {
+    // Returns base 10 digits and suffix.
+    pub fn parse_lit_int(mut s: &str) -> Option<(Box<str>, Box<str>)> {
+        let negative = byte(s, 0) == b'-';
+        if negative {
+            s = &s[1..];
+        }
+
         let base = match (byte(s, 0), byte(s, 1)) {
             (b'0', b'x') => {
                 s = &s[2..];
@@ -1018,73 +1383,192 @@ mod value {
                 s = &s[2..];
                 2
             }
-            (b'0'...b'9', _) => 10,
-            _ => unreachable!(),
+            (b'0'..=b'9', _) => 10,
+            _ => return None,
         };
 
-        let mut value = 0u64;
-        loop {
+        let mut value = BigInt::new();
+        'outer: loop {
             let b = byte(s, 0);
             let digit = match b {
-                b'0'...b'9' => u64::from(b - b'0'),
-                b'a'...b'f' if base > 10 => 10 + u64::from(b - b'a'),
-                b'A'...b'F' if base > 10 => 10 + u64::from(b - b'A'),
+                b'0'..=b'9' => b - b'0',
+                b'a'..=b'f' if base > 10 => b - b'a' + 10,
+                b'A'..=b'F' if base > 10 => b - b'A' + 10,
                 b'_' => {
                     s = &s[1..];
                     continue;
                 }
-                // NOTE: Looking at a floating point literal, we don't want to
-                // consider these integers.
+                // If looking at a floating point literal, we don't want to
+                // consider it an integer.
                 b'.' if base == 10 => return None,
-                b'e' | b'E' if base == 10 => return None,
+                b'e' | b'E' if base == 10 => {
+                    let mut has_exp = false;
+                    for (i, b) in s[1..].bytes().enumerate() {
+                        match b {
+                            b'_' => {}
+                            b'-' | b'+' => return None,
+                            b'0'..=b'9' => has_exp = true,
+                            _ => {
+                                let suffix = &s[1 + i..];
+                                if has_exp && crate::ident::xid_ok(suffix) {
+                                    return None;
+                                } else {
+                                    break 'outer;
+                                }
+                            }
+                        }
+                    }
+                    if has_exp {
+                        return None;
+                    } else {
+                        break;
+                    }
+                }
                 _ => break,
             };
 
             if digit >= base {
-                panic!("Unexpected digit {:x} out of base range", digit);
+                return None;
             }
 
-            value = match value.checked_mul(base) {
-                Some(value) => value,
-                None => return None,
-            };
-            value = match value.checked_add(digit) {
-                Some(value) => value,
-                None => return None,
-            };
+            value *= base;
+            value += digit;
             s = &s[1..];
         }
 
-        Some(value)
+        let suffix = s;
+        if suffix.is_empty() || crate::ident::xid_ok(&suffix) {
+            let mut repr = value.to_string();
+            if negative {
+                repr.insert(0, '-');
+            }
+            Some((repr.into_boxed_str(), suffix.to_owned().into_boxed_str()))
+        } else {
+            None
+        }
     }
 
-    pub fn parse_lit_float(input: &str) -> f64 {
+    // Returns base 10 digits and suffix.
+    pub fn parse_lit_float(input: &str) -> Option<(Box<str>, Box<str>)> {
         // Rust's floating point literals are very similar to the ones parsed by
         // the standard library, except that rust's literals can contain
         // ignorable underscores. Let's remove those underscores.
+
         let mut bytes = input.to_owned().into_bytes();
-        let mut write = 0;
-        for read in 0..bytes.len() {
-            if bytes[read] == b'_' {
-                continue; // Don't increase write
-            }
-            if write != read {
-                let x = bytes[read];
-                bytes[write] = x;
+
+        let start = (*bytes.get(0)? == b'-') as usize;
+        match bytes.get(start)? {
+            b'0'..=b'9' => {}
+            _ => return None,
+        }
+
+        let mut read = start;
+        let mut write = start;
+        let mut has_dot = false;
+        let mut has_e = false;
+        let mut has_sign = false;
+        let mut has_exponent = false;
+        while read < bytes.len() {
+            match bytes[read] {
+                b'_' => {
+                    // Don't increase write
+                    read += 1;
+                    continue;
+                }
+                b'0'..=b'9' => {
+                    if has_e {
+                        has_exponent = true;
+                    }
+                    bytes[write] = bytes[read];
+                }
+                b'.' => {
+                    if has_e || has_dot {
+                        return None;
+                    }
+                    has_dot = true;
+                    bytes[write] = b'.';
+                }
+                b'e' | b'E' => {
+                    match bytes[read + 1..]
+                        .iter()
+                        .find(|b| **b != b'_')
+                        .unwrap_or(&b'\0')
+                    {
+                        b'-' | b'+' | b'0'..=b'9' => {}
+                        _ => break,
+                    }
+                    if has_e {
+                        if has_exponent {
+                            break;
+                        } else {
+                            return None;
+                        }
+                    }
+                    has_e = true;
+                    bytes[write] = b'e';
+                }
+                b'-' | b'+' => {
+                    if has_sign || has_exponent || !has_e {
+                        return None;
+                    }
+                    has_sign = true;
+                    if bytes[read] == b'-' {
+                        bytes[write] = bytes[read];
+                    } else {
+                        // Omit '+'
+                        read += 1;
+                        continue;
+                    }
+                }
+                _ => break,
             }
+            read += 1;
             write += 1;
         }
-        bytes.truncate(write);
-        let input = String::from_utf8(bytes).unwrap();
-        let end = input.find('f').unwrap_or_else(|| input.len());
-        input[..end].parse().unwrap()
+
+        if has_e && !has_exponent {
+            return None;
+        }
+
+        let mut digits = String::from_utf8(bytes).unwrap();
+        let suffix = digits.split_off(read);
+        digits.truncate(write);
+        if suffix.is_empty() || crate::ident::xid_ok(&suffix) {
+            Some((digits.into_boxed_str(), suffix.into_boxed_str()))
+        } else {
+            None
+        }
     }
 
-    pub fn to_literal(s: &str) -> Literal {
-        let stream = s.parse::<TokenStream>().unwrap();
-        match stream.into_iter().next().unwrap() {
-            TokenTree::Literal(l) => l,
-            _ => unreachable!(),
+    pub fn to_literal(repr: &str, digits: &str, suffix: &str) -> Option<Literal> {
+        if repr.starts_with('-') {
+            let f64_parse_finite = || digits.parse().ok().filter(|x: &f64| x.is_finite());
+            let f32_parse_finite = || digits.parse().ok().filter(|x: &f32| x.is_finite());
+            if suffix == "f64" {
+                f64_parse_finite().map(Literal::f64_suffixed)
+            } else if suffix == "f32" {
+                f32_parse_finite().map(Literal::f32_suffixed)
+            } else if suffix == "i64" {
+                digits.parse().ok().map(Literal::i64_suffixed)
+            } else if suffix == "i32" {
+                digits.parse().ok().map(Literal::i32_suffixed)
+            } else if suffix == "i16" {
+                digits.parse().ok().map(Literal::i16_suffixed)
+            } else if suffix == "i8" {
+                digits.parse().ok().map(Literal::i8_suffixed)
+            } else if !suffix.is_empty() {
+                None
+            } else if digits.contains('.') {
+                f64_parse_finite().map(Literal::f64_unsuffixed)
+            } else {
+                digits.parse().ok().map(Literal::i64_unsuffixed)
+            }
+        } else {
+            let stream = repr.parse::<TokenStream>().unwrap();
+            match stream.into_iter().next().unwrap() {
+                TokenTree::Literal(l) => Some(l),
+                _ => unreachable!(),
+            }
         }
     }
 }