diff options
Diffstat (limited to 'crates/syntax/src/ast/token_ext.rs')
-rw-r--r-- | crates/syntax/src/ast/token_ext.rs | 78 |
1 files changed, 78 insertions, 0 deletions
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs index 44f967acb..5623799b4 100644 --- a/crates/syntax/src/ast/token_ext.rs +++ b/crates/syntax/src/ast/token_ext.rs | |||
@@ -536,3 +536,81 @@ impl HasFormatSpecifier for ast::RawString { | |||
536 | Some(res) | 536 | Some(res) |
537 | } | 537 | } |
538 | } | 538 | } |
539 | |||
540 | impl ast::IntNumber { | ||
541 | #[rustfmt::skip] | ||
542 | pub(crate) const SUFFIXES: &'static [&'static str] = &[ | ||
543 | "u8", "u16", "u32", "u64", "u128", "usize", | ||
544 | "i8", "i16", "i32", "i64", "i128", "isize", | ||
545 | ]; | ||
546 | |||
547 | // FIXME: should probably introduce string token type? | ||
548 | // https://github.com/rust-analyzer/rust-analyzer/issues/6308 | ||
549 | pub fn value(&self) -> Option<(Radix, u128)> { | ||
550 | let token = self.syntax(); | ||
551 | |||
552 | let mut text = token.text().as_str(); | ||
553 | for suffix in ast::IntNumber::SUFFIXES { | ||
554 | if let Some(without_suffix) = text.strip_suffix(suffix) { | ||
555 | text = without_suffix; | ||
556 | break; | ||
557 | } | ||
558 | } | ||
559 | |||
560 | let buf; | ||
561 | if text.contains("_") { | ||
562 | buf = text.replace('_', ""); | ||
563 | text = buf.as_str(); | ||
564 | }; | ||
565 | |||
566 | let radix = Radix::identify(text)?; | ||
567 | let digits = &text[radix.prefix_len()..]; | ||
568 | let value = u128::from_str_radix(digits, radix as u32).ok()?; | ||
569 | Some((radix, value)) | ||
570 | } | ||
571 | } | ||
572 | |||
573 | impl ast::FloatNumber { | ||
574 | pub(crate) const SUFFIXES: &'static [&'static str] = &["f32", "f64"]; | ||
575 | } | ||
576 | |||
577 | #[derive(Debug, PartialEq, Eq, Copy, Clone)] | ||
578 | pub enum Radix { | ||
579 | Binary = 2, | ||
580 | Octal = 8, | ||
581 | Decimal = 10, | ||
582 | Hexadecimal = 16, | ||
583 | } | ||
584 | |||
585 | impl Radix { | ||
586 | pub const ALL: &'static [Radix] = | ||
587 | &[Radix::Binary, Radix::Octal, Radix::Decimal, Radix::Hexadecimal]; | ||
588 | |||
589 | fn identify(literal_text: &str) -> Option<Self> { | ||
590 | // We cannot express a literal in anything other than decimal in under 3 characters, so we return here if possible. | ||
591 | if literal_text.len() < 3 && literal_text.chars().all(|c| c.is_digit(10)) { | ||
592 | return Some(Self::Decimal); | ||
593 | } | ||
594 | |||
595 | let res = match &literal_text[..2] { | ||
596 | "0b" => Radix::Binary, | ||
597 | "0o" => Radix::Octal, | ||
598 | "0x" => Radix::Hexadecimal, | ||
599 | _ => Radix::Decimal, | ||
600 | }; | ||
601 | |||
602 | // Checks that all characters after the base prefix are all valid digits for that base. | ||
603 | if literal_text[res.prefix_len()..].chars().all(|c| c.is_digit(res as u32)) { | ||
604 | Some(res) | ||
605 | } else { | ||
606 | None | ||
607 | } | ||
608 | } | ||
609 | |||
610 | const fn prefix_len(&self) -> usize { | ||
611 | match self { | ||
612 | Self::Decimal => 0, | ||
613 | _ => 2, | ||
614 | } | ||
615 | } | ||
616 | } | ||