1#[cfg(feature = "parsing")]
2use crate::lookahead;
3#[cfg(feature = "parsing")]
4use crate::parse::{Parse, Parser};
5use crate::{Error, Result};
6use proc_macro2::{Ident, Literal, Span};
7#[cfg(feature = "parsing")]
8use proc_macro2::{TokenStream, TokenTree};
9use std::fmt::{self, Display};
10#[cfg(feature = "extra-traits")]
11use std::hash::{Hash, Hasher};
12use std::str::{self, FromStr};
13
14ast_enum_of_structs! {
15 /// A Rust literal such as a string or integer or boolean.
16 ///
17 /// # Syntax tree enum
18 ///
19 /// This type is a [syntax tree enum].
20 ///
21 /// [syntax tree enum]: crate::Expr#syntax-tree-enums
22 pub enum Lit {
23 /// A UTF-8 string literal: `"foo"`.
24 Str(LitStr),
25
26 /// A byte string literal: `b"foo"`.
27 ByteStr(LitByteStr),
28
29 /// A byte literal: `b'f'`.
30 Byte(LitByte),
31
32 /// A character literal: `'a'`.
33 Char(LitChar),
34
35 /// An integer literal: `1` or `1u16`.
36 Int(LitInt),
37
38 /// A floating point literal: `1f64` or `1.0e10f64`.
39 ///
40 /// Must be finite. May not be infinite or NaN.
41 Float(LitFloat),
42
43 /// A boolean literal: `true` or `false`.
44 Bool(LitBool),
45
46 /// A raw token literal not interpreted by Syn.
47 Verbatim(Literal),
48 }
49}
50
51ast_struct! {
52 /// A UTF-8 string literal: `"foo"`.
53 pub struct LitStr {
54 repr: Box<LitRepr>,
55 }
56}
57
58ast_struct! {
59 /// A byte string literal: `b"foo"`.
60 pub struct LitByteStr {
61 repr: Box<LitRepr>,
62 }
63}
64
65ast_struct! {
66 /// A byte literal: `b'f'`.
67 pub struct LitByte {
68 repr: Box<LitRepr>,
69 }
70}
71
72ast_struct! {
73 /// A character literal: `'a'`.
74 pub struct LitChar {
75 repr: Box<LitRepr>,
76 }
77}
78
79struct LitRepr {
80 token: Literal,
81 suffix: Box<str>,
82}
83
84ast_struct! {
85 /// An integer literal: `1` or `1u16`.
86 pub struct LitInt {
87 repr: Box<LitIntRepr>,
88 }
89}
90
91struct LitIntRepr {
92 token: Literal,
93 digits: Box<str>,
94 suffix: Box<str>,
95}
96
97ast_struct! {
98 /// A floating point literal: `1f64` or `1.0e10f64`.
99 ///
100 /// Must be finite. May not be infinite or NaN.
101 pub struct LitFloat {
102 repr: Box<LitFloatRepr>,
103 }
104}
105
106struct LitFloatRepr {
107 token: Literal,
108 digits: Box<str>,
109 suffix: Box<str>,
110}
111
112ast_struct! {
113 /// A boolean literal: `true` or `false`.
114 pub struct LitBool {
115 pub value: bool,
116 pub span: Span,
117 }
118}
119
120impl LitStr {
121 pub fn new(value: &str, span: Span) -> Self {
122 let mut token = Literal::string(value);
123 token.set_span(span);
124 LitStr {
125 repr: Box::new(LitRepr {
126 token,
127 suffix: Box::<str>::default(),
128 }),
129 }
130 }
131
132 pub fn value(&self) -> String {
133 let repr = self.repr.token.to_string();
134 let (value, _suffix) = value::parse_lit_str(&repr);
135 String::from(value)
136 }
137
138 /// Parse a syntax tree node from the content of this string literal.
139 ///
140 /// All spans in the syntax tree will point to the span of this `LitStr`.
141 ///
142 /// # Example
143 ///
144 /// ```
145 /// use proc_macro2::Span;
146 /// use syn::{Attribute, Error, Ident, Lit, Meta, MetaNameValue, Path, Result};
147 ///
148 /// // Parses the path from an attribute that looks like:
149 /// //
150 /// // #[path = "a::b::c"]
151 /// //
152 /// // or returns `None` if the input is some other attribute.
153 /// fn get_path(attr: &Attribute) -> Result<Option<Path>> {
154 /// if !attr.path.is_ident("path") {
155 /// return Ok(None);
156 /// }
157 ///
158 /// match attr.parse_meta()? {
159 /// Meta::NameValue(MetaNameValue { lit: Lit::Str(lit_str), .. }) => {
160 /// lit_str.parse().map(Some)
161 /// }
162 /// _ => {
163 /// let message = "expected #[path = \"...\"]";
164 /// Err(Error::new_spanned(attr, message))
165 /// }
166 /// }
167 /// }
168 /// ```
169 #[cfg(feature = "parsing")]
170 #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
171 pub fn parse<T: Parse>(&self) -> Result<T> {
172 self.parse_with(T::parse)
173 }
174
175 /// Invoke parser on the content of this string literal.
176 ///
177 /// All spans in the syntax tree will point to the span of this `LitStr`.
178 ///
179 /// # Example
180 ///
181 /// ```
182 /// # use proc_macro2::Span;
183 /// # use syn::{LitStr, Result};
184 /// #
185 /// # fn main() -> Result<()> {
186 /// # let lit_str = LitStr::new("a::b::c", Span::call_site());
187 /// #
188 /// # const IGNORE: &str = stringify! {
189 /// let lit_str: LitStr = /* ... */;
190 /// # };
191 ///
192 /// // Parse a string literal like "a::b::c" into a Path, not allowing
193 /// // generic arguments on any of the path segments.
194 /// let basic_path = lit_str.parse_with(syn::Path::parse_mod_style)?;
195 /// #
196 /// # Ok(())
197 /// # }
198 /// ```
199 #[cfg(feature = "parsing")]
200 #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
201 pub fn parse_with<F: Parser>(&self, parser: F) -> Result<F::Output> {
202 use proc_macro2::Group;
203
204 // Token stream with every span replaced by the given one.
205 fn respan_token_stream(stream: TokenStream, span: Span) -> TokenStream {
206 stream
207 .into_iter()
208 .map(|token| respan_token_tree(token, span))
209 .collect()
210 }
211
212 // Token tree with every span replaced by the given one.
213 fn respan_token_tree(mut token: TokenTree, span: Span) -> TokenTree {
214 match &mut token {
215 TokenTree::Group(g) => {
216 let stream = respan_token_stream(g.stream(), span);
217 *g = Group::new(g.delimiter(), stream);
218 g.set_span(span);
219 }
220 other => other.set_span(span),
221 }
222 token
223 }
224
225 // Parse string literal into a token stream with every span equal to the
226 // original literal's span.
227 let mut tokens = TokenStream::from_str(&self.value())?;
228 tokens = respan_token_stream(tokens, self.span());
229
230 parser.parse2(tokens)
231 }
232
233 pub fn span(&self) -> Span {
234 self.repr.token.span()
235 }
236
237 pub fn set_span(&mut self, span: Span) {
238 self.repr.token.set_span(span);
239 }
240
241 pub fn suffix(&self) -> &str {
242 &self.repr.suffix
243 }
244
245 pub fn token(&self) -> Literal {
246 self.repr.token.clone()
247 }
248}
249
250impl LitByteStr {
251 pub fn new(value: &[u8], span: Span) -> Self {
252 let mut token = Literal::byte_string(value);
253 token.set_span(span);
254 LitByteStr {
255 repr: Box::new(LitRepr {
256 token,
257 suffix: Box::<str>::default(),
258 }),
259 }
260 }
261
262 pub fn value(&self) -> Vec<u8> {
263 let repr = self.repr.token.to_string();
264 let (value, _suffix) = value::parse_lit_byte_str(&repr);
265 value
266 }
267
268 pub fn span(&self) -> Span {
269 self.repr.token.span()
270 }
271
272 pub fn set_span(&mut self, span: Span) {
273 self.repr.token.set_span(span);
274 }
275
276 pub fn suffix(&self) -> &str {
277 &self.repr.suffix
278 }
279
280 pub fn token(&self) -> Literal {
281 self.repr.token.clone()
282 }
283}
284
285impl LitByte {
286 pub fn new(value: u8, span: Span) -> Self {
287 let mut token = Literal::u8_suffixed(value);
288 token.set_span(span);
289 LitByte {
290 repr: Box::new(LitRepr {
291 token,
292 suffix: Box::<str>::default(),
293 }),
294 }
295 }
296
297 pub fn value(&self) -> u8 {
298 let repr = self.repr.token.to_string();
299 let (value, _suffix) = value::parse_lit_byte(&repr);
300 value
301 }
302
303 pub fn span(&self) -> Span {
304 self.repr.token.span()
305 }
306
307 pub fn set_span(&mut self, span: Span) {
308 self.repr.token.set_span(span);
309 }
310
311 pub fn suffix(&self) -> &str {
312 &self.repr.suffix
313 }
314
315 pub fn token(&self) -> Literal {
316 self.repr.token.clone()
317 }
318}
319
320impl LitChar {
321 pub fn new(value: char, span: Span) -> Self {
322 let mut token = Literal::character(value);
323 token.set_span(span);
324 LitChar {
325 repr: Box::new(LitRepr {
326 token,
327 suffix: Box::<str>::default(),
328 }),
329 }
330 }
331
332 pub fn value(&self) -> char {
333 let repr = self.repr.token.to_string();
334 let (value, _suffix) = value::parse_lit_char(&repr);
335 value
336 }
337
338 pub fn span(&self) -> Span {
339 self.repr.token.span()
340 }
341
342 pub fn set_span(&mut self, span: Span) {
343 self.repr.token.set_span(span);
344 }
345
346 pub fn suffix(&self) -> &str {
347 &self.repr.suffix
348 }
349
350 pub fn token(&self) -> Literal {
351 self.repr.token.clone()
352 }
353}
354
355impl LitInt {
356 pub fn new(repr: &str, span: Span) -> Self {
357 let (digits, suffix) = match value::parse_lit_int(repr) {
358 Some(parse) => parse,
359 None => panic!("Not an integer literal: `{}`", repr),
360 };
361
362 let mut token = match value::to_literal(repr, &digits, &suffix) {
363 Some(token) => token,
364 None => panic!("Unsupported integer literal: `{}`", repr),
365 };
366
367 token.set_span(span);
368 LitInt {
369 repr: Box::new(LitIntRepr {
370 token,
371 digits,
372 suffix,
373 }),
374 }
375 }
376
377 pub fn base10_digits(&self) -> &str {
378 &self.repr.digits
379 }
380
381 /// Parses the literal into a selected number type.
382 ///
383 /// This is equivalent to `lit.base10_digits().parse()` except that the
384 /// resulting errors will be correctly spanned to point to the literal token
385 /// in the macro input.
386 ///
387 /// ```
388 /// use syn::LitInt;
389 /// use syn::parse::{Parse, ParseStream, Result};
390 ///
391 /// struct Port {
392 /// value: u16,
393 /// }
394 ///
395 /// impl Parse for Port {
396 /// fn parse(input: ParseStream) -> Result<Self> {
397 /// let lit: LitInt = input.parse()?;
398 /// let value = lit.base10_parse::<u16>()?;
399 /// Ok(Port { value })
400 /// }
401 /// }
402 /// ```
403 pub fn base10_parse<N>(&self) -> Result<N>
404 where
405 N: FromStr,
406 N::Err: Display,
407 {
408 self.base10_digits()
409 .parse()
410 .map_err(|err| Error::new(self.span(), err))
411 }
412
413 pub fn suffix(&self) -> &str {
414 &self.repr.suffix
415 }
416
417 pub fn span(&self) -> Span {
418 self.repr.token.span()
419 }
420
421 pub fn set_span(&mut self, span: Span) {
422 self.repr.token.set_span(span);
423 }
424
425 pub fn token(&self) -> Literal {
426 self.repr.token.clone()
427 }
428}
429
430impl From<Literal> for LitInt {
431 fn from(token: Literal) -> Self {
432 let repr: String = token.to_string();
433 if let Some((digits: Box, suffix: Box)) = value::parse_lit_int(&repr) {
434 LitInt {
435 repr: Box::new(LitIntRepr {
436 token,
437 digits,
438 suffix,
439 }),
440 }
441 } else {
442 panic!("Not an integer literal: `{}`", repr);
443 }
444 }
445}
446
447impl Display for LitInt {
448 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
449 self.repr.token.fmt(formatter)
450 }
451}
452
453impl LitFloat {
454 pub fn new(repr: &str, span: Span) -> Self {
455 let (digits, suffix) = match value::parse_lit_float(repr) {
456 Some(parse) => parse,
457 None => panic!("Not a float literal: `{}`", repr),
458 };
459
460 let mut token = match value::to_literal(repr, &digits, &suffix) {
461 Some(token) => token,
462 None => panic!("Unsupported float literal: `{}`", repr),
463 };
464
465 token.set_span(span);
466 LitFloat {
467 repr: Box::new(LitFloatRepr {
468 token,
469 digits,
470 suffix,
471 }),
472 }
473 }
474
475 pub fn base10_digits(&self) -> &str {
476 &self.repr.digits
477 }
478
479 pub fn base10_parse<N>(&self) -> Result<N>
480 where
481 N: FromStr,
482 N::Err: Display,
483 {
484 self.base10_digits()
485 .parse()
486 .map_err(|err| Error::new(self.span(), err))
487 }
488
489 pub fn suffix(&self) -> &str {
490 &self.repr.suffix
491 }
492
493 pub fn span(&self) -> Span {
494 self.repr.token.span()
495 }
496
497 pub fn set_span(&mut self, span: Span) {
498 self.repr.token.set_span(span);
499 }
500
501 pub fn token(&self) -> Literal {
502 self.repr.token.clone()
503 }
504}
505
506impl From<Literal> for LitFloat {
507 fn from(token: Literal) -> Self {
508 let repr: String = token.to_string();
509 if let Some((digits: Box, suffix: Box)) = value::parse_lit_float(&repr) {
510 LitFloat {
511 repr: Box::new(LitFloatRepr {
512 token,
513 digits,
514 suffix,
515 }),
516 }
517 } else {
518 panic!("Not a float literal: `{}`", repr);
519 }
520 }
521}
522
523impl Display for LitFloat {
524 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
525 self.repr.token.fmt(formatter)
526 }
527}
528
529impl LitBool {
530 pub fn new(value: bool, span: Span) -> Self {
531 LitBool { value, span }
532 }
533
534 pub fn value(&self) -> bool {
535 self.value
536 }
537
538 pub fn span(&self) -> Span {
539 self.span
540 }
541
542 pub fn set_span(&mut self, span: Span) {
543 self.span = span;
544 }
545
546 pub fn token(&self) -> Ident {
547 let s: &str = if self.value { "true" } else { "false" };
548 Ident::new(string:s, self.span)
549 }
550}
551
552#[cfg(feature = "extra-traits")]
553mod debug_impls {
554 use super::*;
555 use std::fmt::{self, Debug};
556
557 #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
558 impl Debug for LitStr {
559 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
560 formatter
561 .debug_struct("LitStr")
562 .field("token", &format_args!("{}", self.repr.token))
563 .finish()
564 }
565 }
566
567 #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
568 impl Debug for LitByteStr {
569 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
570 formatter
571 .debug_struct("LitByteStr")
572 .field("token", &format_args!("{}", self.repr.token))
573 .finish()
574 }
575 }
576
577 #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
578 impl Debug for LitByte {
579 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
580 formatter
581 .debug_struct("LitByte")
582 .field("token", &format_args!("{}", self.repr.token))
583 .finish()
584 }
585 }
586
587 #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
588 impl Debug for LitChar {
589 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
590 formatter
591 .debug_struct("LitChar")
592 .field("token", &format_args!("{}", self.repr.token))
593 .finish()
594 }
595 }
596
597 #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
598 impl Debug for LitInt {
599 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
600 formatter
601 .debug_struct("LitInt")
602 .field("token", &format_args!("{}", self.repr.token))
603 .finish()
604 }
605 }
606
607 #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
608 impl Debug for LitFloat {
609 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
610 formatter
611 .debug_struct("LitFloat")
612 .field("token", &format_args!("{}", self.repr.token))
613 .finish()
614 }
615 }
616
617 #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
618 impl Debug for LitBool {
619 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
620 formatter
621 .debug_struct("LitBool")
622 .field("value", &self.value)
623 .finish()
624 }
625 }
626}
627
628#[cfg(feature = "clone-impls")]
629#[cfg_attr(doc_cfg, doc(cfg(feature = "clone-impls")))]
630impl Clone for LitRepr {
631 fn clone(&self) -> Self {
632 LitRepr {
633 token: self.token.clone(),
634 suffix: self.suffix.clone(),
635 }
636 }
637}
638
639#[cfg(feature = "clone-impls")]
640#[cfg_attr(doc_cfg, doc(cfg(feature = "clone-impls")))]
641impl Clone for LitIntRepr {
642 fn clone(&self) -> Self {
643 LitIntRepr {
644 token: self.token.clone(),
645 digits: self.digits.clone(),
646 suffix: self.suffix.clone(),
647 }
648 }
649}
650
651#[cfg(feature = "clone-impls")]
652#[cfg_attr(doc_cfg, doc(cfg(feature = "clone-impls")))]
653impl Clone for LitFloatRepr {
654 fn clone(&self) -> Self {
655 LitFloatRepr {
656 token: self.token.clone(),
657 digits: self.digits.clone(),
658 suffix: self.suffix.clone(),
659 }
660 }
661}
662
663macro_rules! lit_extra_traits {
664 ($ty:ident) => {
665 #[cfg(feature = "clone-impls")]
666 #[cfg_attr(doc_cfg, doc(cfg(feature = "clone-impls")))]
667 impl Clone for $ty {
668 fn clone(&self) -> Self {
669 $ty {
670 repr: self.repr.clone(),
671 }
672 }
673 }
674
675 #[cfg(feature = "extra-traits")]
676 #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
677 impl PartialEq for $ty {
678 fn eq(&self, other: &Self) -> bool {
679 self.repr.token.to_string() == other.repr.token.to_string()
680 }
681 }
682
683 #[cfg(feature = "extra-traits")]
684 #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
685 impl Hash for $ty {
686 fn hash<H>(&self, state: &mut H)
687 where
688 H: Hasher,
689 {
690 self.repr.token.to_string().hash(state);
691 }
692 }
693
694 #[cfg(feature = "parsing")]
695 #[doc(hidden)]
696 #[allow(non_snake_case)]
697 pub fn $ty(marker: lookahead::TokenMarker) -> $ty {
698 match marker {}
699 }
700 };
701}
702
703lit_extra_traits!(LitStr);
704lit_extra_traits!(LitByteStr);
705lit_extra_traits!(LitByte);
706lit_extra_traits!(LitChar);
707lit_extra_traits!(LitInt);
708lit_extra_traits!(LitFloat);
709
710#[cfg(feature = "parsing")]
711#[doc(hidden)]
712#[allow(non_snake_case)]
713pub fn LitBool(marker: lookahead::TokenMarker) -> LitBool {
714 match marker {}
715}
716
717ast_enum! {
718 /// The style of a string literal, either plain quoted or a raw string like
719 /// `r##"data"##`.
720 pub enum StrStyle #no_visit {
721 /// An ordinary string like `"data"`.
722 Cooked,
723 /// A raw string like `r##"data"##`.
724 ///
725 /// The unsigned integer is the number of `#` symbols used.
726 Raw(usize),
727 }
728}
729
730#[cfg(feature = "parsing")]
731#[doc(hidden)]
732#[allow(non_snake_case)]
733pub fn Lit(marker: lookahead::TokenMarker) -> Lit {
734 match marker {}
735}
736
737#[cfg(feature = "parsing")]
738pub mod parsing {
739 use super::*;
740 use crate::buffer::Cursor;
741 use crate::parse::{Parse, ParseStream, Result};
742 use proc_macro2::Punct;
743
744 #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
745 impl Parse for Lit {
746 fn parse(input: ParseStream) -> Result<Self> {
747 input.step(|cursor| {
748 if let Some((lit, rest)) = cursor.literal() {
749 return Ok((Lit::new(lit), rest));
750 }
751
752 if let Some((ident, rest)) = cursor.ident() {
753 let value = ident == "true";
754 if value || ident == "false" {
755 let lit_bool = LitBool {
756 value,
757 span: ident.span(),
758 };
759 return Ok((Lit::Bool(lit_bool), rest));
760 }
761 }
762
763 if let Some((punct, rest)) = cursor.punct() {
764 if punct.as_char() == '-' {
765 if let Some((lit, rest)) = parse_negative_lit(punct, rest) {
766 return Ok((lit, rest));
767 }
768 }
769 }
770
771 Err(cursor.error("expected literal"))
772 })
773 }
774 }
775
776 fn parse_negative_lit(neg: Punct, cursor: Cursor) -> Option<(Lit, Cursor)> {
777 let (lit, rest) = cursor.literal()?;
778
779 let mut span = neg.span();
780 span = span.join(lit.span()).unwrap_or(span);
781
782 let mut repr = lit.to_string();
783 repr.insert(0, '-');
784
785 if let Some((digits, suffix)) = value::parse_lit_int(&repr) {
786 if let Some(mut token) = value::to_literal(&repr, &digits, &suffix) {
787 token.set_span(span);
788 return Some((
789 Lit::Int(LitInt {
790 repr: Box::new(LitIntRepr {
791 token,
792 digits,
793 suffix,
794 }),
795 }),
796 rest,
797 ));
798 }
799 }
800
801 let (digits, suffix) = value::parse_lit_float(&repr)?;
802 let mut token = value::to_literal(&repr, &digits, &suffix)?;
803 token.set_span(span);
804 Some((
805 Lit::Float(LitFloat {
806 repr: Box::new(LitFloatRepr {
807 token,
808 digits,
809 suffix,
810 }),
811 }),
812 rest,
813 ))
814 }
815
816 #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
817 impl Parse for LitStr {
818 fn parse(input: ParseStream) -> Result<Self> {
819 let head = input.fork();
820 match input.parse() {
821 Ok(Lit::Str(lit)) => Ok(lit),
822 _ => Err(head.error("expected string literal")),
823 }
824 }
825 }
826
827 #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
828 impl Parse for LitByteStr {
829 fn parse(input: ParseStream) -> Result<Self> {
830 let head = input.fork();
831 match input.parse() {
832 Ok(Lit::ByteStr(lit)) => Ok(lit),
833 _ => Err(head.error("expected byte string literal")),
834 }
835 }
836 }
837
838 #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
839 impl Parse for LitByte {
840 fn parse(input: ParseStream) -> Result<Self> {
841 let head = input.fork();
842 match input.parse() {
843 Ok(Lit::Byte(lit)) => Ok(lit),
844 _ => Err(head.error("expected byte literal")),
845 }
846 }
847 }
848
849 #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
850 impl Parse for LitChar {
851 fn parse(input: ParseStream) -> Result<Self> {
852 let head = input.fork();
853 match input.parse() {
854 Ok(Lit::Char(lit)) => Ok(lit),
855 _ => Err(head.error("expected character literal")),
856 }
857 }
858 }
859
860 #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
861 impl Parse for LitInt {
862 fn parse(input: ParseStream) -> Result<Self> {
863 let head = input.fork();
864 match input.parse() {
865 Ok(Lit::Int(lit)) => Ok(lit),
866 _ => Err(head.error("expected integer literal")),
867 }
868 }
869 }
870
871 #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
872 impl Parse for LitFloat {
873 fn parse(input: ParseStream) -> Result<Self> {
874 let head = input.fork();
875 match input.parse() {
876 Ok(Lit::Float(lit)) => Ok(lit),
877 _ => Err(head.error("expected floating point literal")),
878 }
879 }
880 }
881
882 #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
883 impl Parse for LitBool {
884 fn parse(input: ParseStream) -> Result<Self> {
885 let head = input.fork();
886 match input.parse() {
887 Ok(Lit::Bool(lit)) => Ok(lit),
888 _ => Err(head.error("expected boolean literal")),
889 }
890 }
891 }
892}
893
894#[cfg(feature = "printing")]
895mod printing {
896 use super::*;
897 use proc_macro2::TokenStream;
898 use quote::{ToTokens, TokenStreamExt};
899
900 #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
901 impl ToTokens for LitStr {
902 fn to_tokens(&self, tokens: &mut TokenStream) {
903 self.repr.token.to_tokens(tokens);
904 }
905 }
906
907 #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
908 impl ToTokens for LitByteStr {
909 fn to_tokens(&self, tokens: &mut TokenStream) {
910 self.repr.token.to_tokens(tokens);
911 }
912 }
913
914 #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
915 impl ToTokens for LitByte {
916 fn to_tokens(&self, tokens: &mut TokenStream) {
917 self.repr.token.to_tokens(tokens);
918 }
919 }
920
921 #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
922 impl ToTokens for LitChar {
923 fn to_tokens(&self, tokens: &mut TokenStream) {
924 self.repr.token.to_tokens(tokens);
925 }
926 }
927
928 #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
929 impl ToTokens for LitInt {
930 fn to_tokens(&self, tokens: &mut TokenStream) {
931 self.repr.token.to_tokens(tokens);
932 }
933 }
934
935 #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
936 impl ToTokens for LitFloat {
937 fn to_tokens(&self, tokens: &mut TokenStream) {
938 self.repr.token.to_tokens(tokens);
939 }
940 }
941
942 #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
943 impl ToTokens for LitBool {
944 fn to_tokens(&self, tokens: &mut TokenStream) {
945 tokens.append(self.token());
946 }
947 }
948}
949
950mod value {
951 use super::*;
952 use crate::bigint::BigInt;
953 use std::char;
954 use std::ops::{Index, RangeFrom};
955
956 impl Lit {
957 /// Interpret a Syn literal from a proc-macro2 literal.
958 pub fn new(token: Literal) -> Self {
959 let repr = token.to_string();
960
961 match byte(&repr, 0) {
962 b'"' | b'r' => {
963 let (_, suffix) = parse_lit_str(&repr);
964 return Lit::Str(LitStr {
965 repr: Box::new(LitRepr { token, suffix }),
966 });
967 }
968 b'b' => match byte(&repr, 1) {
969 b'"' | b'r' => {
970 let (_, suffix) = parse_lit_byte_str(&repr);
971 return Lit::ByteStr(LitByteStr {
972 repr: Box::new(LitRepr { token, suffix }),
973 });
974 }
975 b'\'' => {
976 let (_, suffix) = parse_lit_byte(&repr);
977 return Lit::Byte(LitByte {
978 repr: Box::new(LitRepr { token, suffix }),
979 });
980 }
981 _ => {}
982 },
983 b'\'' => {
984 let (_, suffix) = parse_lit_char(&repr);
985 return Lit::Char(LitChar {
986 repr: Box::new(LitRepr { token, suffix }),
987 });
988 }
989 b'0'..=b'9' | b'-' => {
990 if let Some((digits, suffix)) = parse_lit_int(&repr) {
991 return Lit::Int(LitInt {
992 repr: Box::new(LitIntRepr {
993 token,
994 digits,
995 suffix,
996 }),
997 });
998 }
999 if let Some((digits, suffix)) = parse_lit_float(&repr) {
1000 return Lit::Float(LitFloat {
1001 repr: Box::new(LitFloatRepr {
1002 token,
1003 digits,
1004 suffix,
1005 }),
1006 });
1007 }
1008 }
1009 b't' | b'f' => {
1010 if repr == "true" || repr == "false" {
1011 return Lit::Bool(LitBool {
1012 value: repr == "true",
1013 span: token.span(),
1014 });
1015 }
1016 }
1017 _ => {}
1018 }
1019
1020 panic!("Unrecognized literal: `{}`", repr);
1021 }
1022
1023 pub fn suffix(&self) -> &str {
1024 match self {
1025 Lit::Str(lit) => lit.suffix(),
1026 Lit::ByteStr(lit) => lit.suffix(),
1027 Lit::Byte(lit) => lit.suffix(),
1028 Lit::Char(lit) => lit.suffix(),
1029 Lit::Int(lit) => lit.suffix(),
1030 Lit::Float(lit) => lit.suffix(),
1031 Lit::Bool(_) | Lit::Verbatim(_) => "",
1032 }
1033 }
1034
1035 pub fn span(&self) -> Span {
1036 match self {
1037 Lit::Str(lit) => lit.span(),
1038 Lit::ByteStr(lit) => lit.span(),
1039 Lit::Byte(lit) => lit.span(),
1040 Lit::Char(lit) => lit.span(),
1041 Lit::Int(lit) => lit.span(),
1042 Lit::Float(lit) => lit.span(),
1043 Lit::Bool(lit) => lit.span,
1044 Lit::Verbatim(lit) => lit.span(),
1045 }
1046 }
1047
1048 pub fn set_span(&mut self, span: Span) {
1049 match self {
1050 Lit::Str(lit) => lit.set_span(span),
1051 Lit::ByteStr(lit) => lit.set_span(span),
1052 Lit::Byte(lit) => lit.set_span(span),
1053 Lit::Char(lit) => lit.set_span(span),
1054 Lit::Int(lit) => lit.set_span(span),
1055 Lit::Float(lit) => lit.set_span(span),
1056 Lit::Bool(lit) => lit.span = span,
1057 Lit::Verbatim(lit) => lit.set_span(span),
1058 }
1059 }
1060 }
1061
1062 /// Get the byte at offset idx, or a default of `b'\0'` if we're looking
1063 /// past the end of the input buffer.
1064 pub fn byte<S: AsRef<[u8]> + ?Sized>(s: &S, idx: usize) -> u8 {
1065 let s = s.as_ref();
1066 if idx < s.len() {
1067 s[idx]
1068 } else {
1069 0
1070 }
1071 }
1072
1073 fn next_chr(s: &str) -> char {
1074 s.chars().next().unwrap_or('\0')
1075 }
1076
1077 // Returns (content, suffix).
1078 pub fn parse_lit_str(s: &str) -> (Box<str>, Box<str>) {
1079 match byte(s, 0) {
1080 b'"' => parse_lit_str_cooked(s),
1081 b'r' => parse_lit_str_raw(s),
1082 _ => unreachable!(),
1083 }
1084 }
1085
1086 // Clippy false positive
1087 // https://github.com/rust-lang-nursery/rust-clippy/issues/2329
1088 #[allow(clippy::needless_continue)]
1089 fn parse_lit_str_cooked(mut s: &str) -> (Box<str>, Box<str>) {
1090 assert_eq!(byte(s, 0), b'"');
1091 s = &s[1..];
1092
1093 let mut content = String::new();
1094 'outer: loop {
1095 let ch = match byte(s, 0) {
1096 b'"' => break,
1097 b'\\' => {
1098 let b = byte(s, 1);
1099 s = &s[2..];
1100 match b {
1101 b'x' => {
1102 let (byte, rest) = backslash_x(s);
1103 s = rest;
1104 assert!(byte <= 0x80, "Invalid \\x byte in string literal");
1105 char::from_u32(u32::from(byte)).unwrap()
1106 }
1107 b'u' => {
1108 let (chr, rest) = backslash_u(s);
1109 s = rest;
1110 chr
1111 }
1112 b'n' => '\n',
1113 b'r' => '\r',
1114 b't' => '\t',
1115 b'\\' => '\\',
1116 b'0' => '\0',
1117 b'\'' => '\'',
1118 b'"' => '"',
1119 b'\r' | b'\n' => loop {
1120 let b = byte(s, 0);
1121 match b {
1122 b' ' | b'\t' | b'\n' | b'\r' => s = &s[1..],
1123 _ => continue 'outer,
1124 }
1125 },
1126 b => panic!("unexpected byte {:?} after \\ character in byte literal", b),
1127 }
1128 }
1129 b'\r' => {
1130 assert_eq!(byte(s, 1), b'\n', "Bare CR not allowed in string");
1131 s = &s[2..];
1132 '\n'
1133 }
1134 _ => {
1135 let ch = next_chr(s);
1136 s = &s[ch.len_utf8()..];
1137 ch
1138 }
1139 };
1140 content.push(ch);
1141 }
1142
1143 assert!(s.starts_with('"'));
1144 let content = content.into_boxed_str();
1145 let suffix = s[1..].to_owned().into_boxed_str();
1146 (content, suffix)
1147 }
1148
1149 fn parse_lit_str_raw(mut s: &str) -> (Box<str>, Box<str>) {
1150 assert_eq!(byte(s, 0), b'r');
1151 s = &s[1..];
1152
1153 let mut pounds = 0;
1154 while byte(s, pounds) == b'#' {
1155 pounds += 1;
1156 }
1157 assert_eq!(byte(s, pounds), b'"');
1158 let close = s.rfind('"').unwrap();
1159 for end in s[close + 1..close + 1 + pounds].bytes() {
1160 assert_eq!(end, b'#');
1161 }
1162
1163 let content = s[pounds + 1..close].to_owned().into_boxed_str();
1164 let suffix = s[close + 1 + pounds..].to_owned().into_boxed_str();
1165 (content, suffix)
1166 }
1167
1168 // Returns (content, suffix).
1169 pub fn parse_lit_byte_str(s: &str) -> (Vec<u8>, Box<str>) {
1170 assert_eq!(byte(s, 0), b'b');
1171 match byte(s, 1) {
1172 b'"' => parse_lit_byte_str_cooked(s),
1173 b'r' => parse_lit_byte_str_raw(s),
1174 _ => unreachable!(),
1175 }
1176 }
1177
1178 // Clippy false positive
1179 // https://github.com/rust-lang-nursery/rust-clippy/issues/2329
1180 #[allow(clippy::needless_continue)]
1181 fn parse_lit_byte_str_cooked(mut s: &str) -> (Vec<u8>, Box<str>) {
1182 assert_eq!(byte(s, 0), b'b');
1183 assert_eq!(byte(s, 1), b'"');
1184 s = &s[2..];
1185
1186 // We're going to want to have slices which don't respect codepoint boundaries.
1187 let mut v = s.as_bytes();
1188
1189 let mut out = Vec::new();
1190 'outer: loop {
1191 let byte = match byte(v, 0) {
1192 b'"' => break,
1193 b'\\' => {
1194 let b = byte(v, 1);
1195 v = &v[2..];
1196 match b {
1197 b'x' => {
1198 let (b, rest) = backslash_x(v);
1199 v = rest;
1200 b
1201 }
1202 b'n' => b'\n',
1203 b'r' => b'\r',
1204 b't' => b'\t',
1205 b'\\' => b'\\',
1206 b'0' => b'\0',
1207 b'\'' => b'\'',
1208 b'"' => b'"',
1209 b'\r' | b'\n' => loop {
1210 let byte = byte(v, 0);
1211 let ch = char::from_u32(u32::from(byte)).unwrap();
1212 if ch.is_whitespace() {
1213 v = &v[1..];
1214 } else {
1215 continue 'outer;
1216 }
1217 },
1218 b => panic!("unexpected byte {:?} after \\ character in byte literal", b),
1219 }
1220 }
1221 b'\r' => {
1222 assert_eq!(byte(v, 1), b'\n', "Bare CR not allowed in string");
1223 v = &v[2..];
1224 b'\n'
1225 }
1226 b => {
1227 v = &v[1..];
1228 b
1229 }
1230 };
1231 out.push(byte);
1232 }
1233
1234 assert_eq!(byte(v, 0), b'"');
1235 let suffix = s[s.len() - v.len() + 1..].to_owned().into_boxed_str();
1236 (out, suffix)
1237 }
1238
1239 fn parse_lit_byte_str_raw(s: &str) -> (Vec<u8>, Box<str>) {
1240 assert_eq!(byte(s, 0), b'b');
1241 let (value, suffix) = parse_lit_str_raw(&s[1..]);
1242 (String::from(value).into_bytes(), suffix)
1243 }
1244
1245 // Returns (value, suffix).
1246 pub fn parse_lit_byte(s: &str) -> (u8, Box<str>) {
1247 assert_eq!(byte(s, 0), b'b');
1248 assert_eq!(byte(s, 1), b'\'');
1249
1250 // We're going to want to have slices which don't respect codepoint boundaries.
1251 let mut v = s[2..].as_bytes();
1252
1253 let b = match byte(v, 0) {
1254 b'\\' => {
1255 let b = byte(v, 1);
1256 v = &v[2..];
1257 match b {
1258 b'x' => {
1259 let (b, rest) = backslash_x(v);
1260 v = rest;
1261 b
1262 }
1263 b'n' => b'\n',
1264 b'r' => b'\r',
1265 b't' => b'\t',
1266 b'\\' => b'\\',
1267 b'0' => b'\0',
1268 b'\'' => b'\'',
1269 b'"' => b'"',
1270 b => panic!("unexpected byte {:?} after \\ character in byte literal", b),
1271 }
1272 }
1273 b => {
1274 v = &v[1..];
1275 b
1276 }
1277 };
1278
1279 assert_eq!(byte(v, 0), b'\'');
1280 let suffix = s[s.len() - v.len() + 1..].to_owned().into_boxed_str();
1281 (b, suffix)
1282 }
1283
1284 // Returns (value, suffix).
1285 pub fn parse_lit_char(mut s: &str) -> (char, Box<str>) {
1286 assert_eq!(byte(s, 0), b'\'');
1287 s = &s[1..];
1288
1289 let ch = match byte(s, 0) {
1290 b'\\' => {
1291 let b = byte(s, 1);
1292 s = &s[2..];
1293 match b {
1294 b'x' => {
1295 let (byte, rest) = backslash_x(s);
1296 s = rest;
1297 assert!(byte <= 0x80, "Invalid \\x byte in string literal");
1298 char::from_u32(u32::from(byte)).unwrap()
1299 }
1300 b'u' => {
1301 let (chr, rest) = backslash_u(s);
1302 s = rest;
1303 chr
1304 }
1305 b'n' => '\n',
1306 b'r' => '\r',
1307 b't' => '\t',
1308 b'\\' => '\\',
1309 b'0' => '\0',
1310 b'\'' => '\'',
1311 b'"' => '"',
1312 b => panic!("unexpected byte {:?} after \\ character in byte literal", b),
1313 }
1314 }
1315 _ => {
1316 let ch = next_chr(s);
1317 s = &s[ch.len_utf8()..];
1318 ch
1319 }
1320 };
1321 assert_eq!(byte(s, 0), b'\'');
1322 let suffix = s[1..].to_owned().into_boxed_str();
1323 (ch, suffix)
1324 }
1325
1326 fn backslash_x<S>(s: &S) -> (u8, &S)
1327 where
1328 S: Index<RangeFrom<usize>, Output = S> + AsRef<[u8]> + ?Sized,
1329 {
1330 let mut ch = 0;
1331 let b0 = byte(s, 0);
1332 let b1 = byte(s, 1);
1333 ch += 0x10
1334 * match b0 {
1335 b'0'..=b'9' => b0 - b'0',
1336 b'a'..=b'f' => 10 + (b0 - b'a'),
1337 b'A'..=b'F' => 10 + (b0 - b'A'),
1338 _ => panic!("unexpected non-hex character after \\x"),
1339 };
1340 ch += match b1 {
1341 b'0'..=b'9' => b1 - b'0',
1342 b'a'..=b'f' => 10 + (b1 - b'a'),
1343 b'A'..=b'F' => 10 + (b1 - b'A'),
1344 _ => panic!("unexpected non-hex character after \\x"),
1345 };
1346 (ch, &s[2..])
1347 }
1348
1349 fn backslash_u(mut s: &str) -> (char, &str) {
1350 if byte(s, 0) != b'{' {
1351 panic!("{}", "expected { after \\u");
1352 }
1353 s = &s[1..];
1354
1355 let mut ch = 0;
1356 let mut digits = 0;
1357 loop {
1358 let b = byte(s, 0);
1359 let digit = match b {
1360 b'0'..=b'9' => b - b'0',
1361 b'a'..=b'f' => 10 + b - b'a',
1362 b'A'..=b'F' => 10 + b - b'A',
1363 b'_' if digits > 0 => {
1364 s = &s[1..];
1365 continue;
1366 }
1367 b'}' if digits == 0 => panic!("invalid empty unicode escape"),
1368 b'}' => break,
1369 _ => panic!("unexpected non-hex character after \\u"),
1370 };
1371 if digits == 6 {
1372 panic!("overlong unicode escape (must have at most 6 hex digits)");
1373 }
1374 ch *= 0x10;
1375 ch += u32::from(digit);
1376 digits += 1;
1377 s = &s[1..];
1378 }
1379 assert!(byte(s, 0) == b'}');
1380 s = &s[1..];
1381
1382 if let Some(ch) = char::from_u32(ch) {
1383 (ch, s)
1384 } else {
1385 panic!("character code {:x} is not a valid unicode character", ch);
1386 }
1387 }
1388
1389 // Returns base 10 digits and suffix.
1390 pub fn parse_lit_int(mut s: &str) -> Option<(Box<str>, Box<str>)> {
1391 let negative = byte(s, 0) == b'-';
1392 if negative {
1393 s = &s[1..];
1394 }
1395
1396 let base = match (byte(s, 0), byte(s, 1)) {
1397 (b'0', b'x') => {
1398 s = &s[2..];
1399 16
1400 }
1401 (b'0', b'o') => {
1402 s = &s[2..];
1403 8
1404 }
1405 (b'0', b'b') => {
1406 s = &s[2..];
1407 2
1408 }
1409 (b'0'..=b'9', _) => 10,
1410 _ => return None,
1411 };
1412
1413 let mut value = BigInt::new();
1414 'outer: loop {
1415 let b = byte(s, 0);
1416 let digit = match b {
1417 b'0'..=b'9' => b - b'0',
1418 b'a'..=b'f' if base > 10 => b - b'a' + 10,
1419 b'A'..=b'F' if base > 10 => b - b'A' + 10,
1420 b'_' => {
1421 s = &s[1..];
1422 continue;
1423 }
1424 // If looking at a floating point literal, we don't want to
1425 // consider it an integer.
1426 b'.' if base == 10 => return None,
1427 b'e' | b'E' if base == 10 => {
1428 let mut has_exp = false;
1429 for (i, b) in s[1..].bytes().enumerate() {
1430 match b {
1431 b'_' => {}
1432 b'-' | b'+' => return None,
1433 b'0'..=b'9' => has_exp = true,
1434 _ => {
1435 let suffix = &s[1 + i..];
1436 if has_exp && crate::ident::xid_ok(suffix) {
1437 return None;
1438 } else {
1439 break 'outer;
1440 }
1441 }
1442 }
1443 }
1444 if has_exp {
1445 return None;
1446 } else {
1447 break;
1448 }
1449 }
1450 _ => break,
1451 };
1452
1453 if digit >= base {
1454 return None;
1455 }
1456
1457 value *= base;
1458 value += digit;
1459 s = &s[1..];
1460 }
1461
1462 let suffix = s;
1463 if suffix.is_empty() || crate::ident::xid_ok(suffix) {
1464 let mut repr = value.to_string();
1465 if negative {
1466 repr.insert(0, '-');
1467 }
1468 Some((repr.into_boxed_str(), suffix.to_owned().into_boxed_str()))
1469 } else {
1470 None
1471 }
1472 }
1473
1474 // Returns base 10 digits and suffix.
1475 pub fn parse_lit_float(input: &str) -> Option<(Box<str>, Box<str>)> {
1476 // Rust's floating point literals are very similar to the ones parsed by
1477 // the standard library, except that rust's literals can contain
1478 // ignorable underscores. Let's remove those underscores.
1479
1480 let mut bytes = input.to_owned().into_bytes();
1481
1482 let start = (*bytes.first()? == b'-') as usize;
1483 match bytes.get(start)? {
1484 b'0'..=b'9' => {}
1485 _ => return None,
1486 }
1487
1488 let mut read = start;
1489 let mut write = start;
1490 let mut has_dot = false;
1491 let mut has_e = false;
1492 let mut has_sign = false;
1493 let mut has_exponent = false;
1494 while read < bytes.len() {
1495 match bytes[read] {
1496 b'_' => {
1497 // Don't increase write
1498 read += 1;
1499 continue;
1500 }
1501 b'0'..=b'9' => {
1502 if has_e {
1503 has_exponent = true;
1504 }
1505 bytes[write] = bytes[read];
1506 }
1507 b'.' => {
1508 if has_e || has_dot {
1509 return None;
1510 }
1511 has_dot = true;
1512 bytes[write] = b'.';
1513 }
1514 b'e' | b'E' => {
1515 match bytes[read + 1..]
1516 .iter()
1517 .find(|b| **b != b'_')
1518 .unwrap_or(&b'\0')
1519 {
1520 b'-' | b'+' | b'0'..=b'9' => {}
1521 _ => break,
1522 }
1523 if has_e {
1524 if has_exponent {
1525 break;
1526 } else {
1527 return None;
1528 }
1529 }
1530 has_e = true;
1531 bytes[write] = b'e';
1532 }
1533 b'-' | b'+' => {
1534 if has_sign || has_exponent || !has_e {
1535 return None;
1536 }
1537 has_sign = true;
1538 if bytes[read] == b'-' {
1539 bytes[write] = bytes[read];
1540 } else {
1541 // Omit '+'
1542 read += 1;
1543 continue;
1544 }
1545 }
1546 _ => break,
1547 }
1548 read += 1;
1549 write += 1;
1550 }
1551
1552 if has_e && !has_exponent {
1553 return None;
1554 }
1555
1556 let mut digits = String::from_utf8(bytes).unwrap();
1557 let suffix = digits.split_off(read);
1558 digits.truncate(write);
1559 if suffix.is_empty() || crate::ident::xid_ok(&suffix) {
1560 Some((digits.into_boxed_str(), suffix.into_boxed_str()))
1561 } else {
1562 None
1563 }
1564 }
1565
1566 #[allow(clippy::unnecessary_wraps)]
1567 pub fn to_literal(repr: &str, digits: &str, suffix: &str) -> Option<Literal> {
1568 #[cfg(syn_no_negative_literal_parse)]
1569 {
1570 // Rustc older than https://github.com/rust-lang/rust/pull/87262.
1571 if repr.starts_with('-') {
1572 let f64_parse_finite = || digits.parse().ok().filter(|x: &f64| x.is_finite());
1573 let f32_parse_finite = || digits.parse().ok().filter(|x: &f32| x.is_finite());
1574 return if suffix == "f64" {
1575 f64_parse_finite().map(Literal::f64_suffixed)
1576 } else if suffix == "f32" {
1577 f32_parse_finite().map(Literal::f32_suffixed)
1578 } else if suffix == "i64" {
1579 digits.parse().ok().map(Literal::i64_suffixed)
1580 } else if suffix == "i32" {
1581 digits.parse().ok().map(Literal::i32_suffixed)
1582 } else if suffix == "i16" {
1583 digits.parse().ok().map(Literal::i16_suffixed)
1584 } else if suffix == "i8" {
1585 digits.parse().ok().map(Literal::i8_suffixed)
1586 } else if !suffix.is_empty() {
1587 None
1588 } else if digits.contains('.') {
1589 f64_parse_finite().map(Literal::f64_unsuffixed)
1590 } else {
1591 digits.parse().ok().map(Literal::i64_unsuffixed)
1592 };
1593 }
1594 }
1595 let _ = digits;
1596 let _ = suffix;
1597 Some(repr.parse::<Literal>().unwrap())
1598 }
1599}
1600