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