diff options
Diffstat (limited to 'crates/syntax/src/ast/token_ext.rs')
-rw-r--r-- | crates/syntax/src/ast/token_ext.rs | 97 |
1 files changed, 86 insertions, 11 deletions
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs index c5ef92733..8d3fad5a6 100644 --- a/crates/syntax/src/ast/token_ext.rs +++ b/crates/syntax/src/ast/token_ext.rs | |||
@@ -8,11 +8,11 @@ use std::{ | |||
8 | use rustc_lexer::unescape::{unescape_literal, Mode}; | 8 | use rustc_lexer::unescape::{unescape_literal, Mode}; |
9 | 9 | ||
10 | use crate::{ | 10 | use crate::{ |
11 | ast::{AstToken, Comment, RawString, String, Whitespace}, | 11 | ast::{self, AstToken}, |
12 | TextRange, TextSize, | 12 | TextRange, TextSize, |
13 | }; | 13 | }; |
14 | 14 | ||
15 | impl Comment { | 15 | impl ast::Comment { |
16 | pub fn kind(&self) -> CommentKind { | 16 | pub fn kind(&self) -> CommentKind { |
17 | kind_by_prefix(self.text()) | 17 | kind_by_prefix(self.text()) |
18 | } | 18 | } |
@@ -80,7 +80,7 @@ fn kind_by_prefix(text: &str) -> CommentKind { | |||
80 | panic!("bad comment text: {:?}", text) | 80 | panic!("bad comment text: {:?}", text) |
81 | } | 81 | } |
82 | 82 | ||
83 | impl Whitespace { | 83 | impl ast::Whitespace { |
84 | pub fn spans_multiple_lines(&self) -> bool { | 84 | pub fn spans_multiple_lines(&self) -> bool { |
85 | let text = self.text(); | 85 | let text = self.text(); |
86 | text.find('\n').map_or(false, |idx| text[idx + 1..].contains('\n')) | 86 | text.find('\n').map_or(false, |idx| text[idx + 1..].contains('\n')) |
@@ -138,19 +138,19 @@ pub trait HasQuotes: AstToken { | |||
138 | } | 138 | } |
139 | } | 139 | } |
140 | 140 | ||
141 | impl HasQuotes for String {} | 141 | impl HasQuotes for ast::String {} |
142 | impl HasQuotes for RawString {} | 142 | impl HasQuotes for ast::RawString {} |
143 | 143 | ||
144 | pub trait HasStringValue: HasQuotes { | 144 | pub trait HasStringValue: HasQuotes { |
145 | fn value(&self) -> Option<Cow<'_, str>>; | 145 | fn value(&self) -> Option<Cow<'_, str>>; |
146 | } | 146 | } |
147 | 147 | ||
148 | impl HasStringValue for String { | 148 | impl HasStringValue for ast::String { |
149 | fn value(&self) -> Option<Cow<'_, str>> { | 149 | fn value(&self) -> Option<Cow<'_, str>> { |
150 | let text = self.text().as_str(); | 150 | let text = self.text().as_str(); |
151 | let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; | 151 | let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; |
152 | 152 | ||
153 | let mut buf = std::string::String::with_capacity(text.len()); | 153 | let mut buf = String::with_capacity(text.len()); |
154 | let mut has_error = false; | 154 | let mut has_error = false; |
155 | unescape_literal(text, Mode::Str, &mut |_, unescaped_char| match unescaped_char { | 155 | unescape_literal(text, Mode::Str, &mut |_, unescaped_char| match unescaped_char { |
156 | Ok(c) => buf.push(c), | 156 | Ok(c) => buf.push(c), |
@@ -166,7 +166,8 @@ impl HasStringValue for String { | |||
166 | } | 166 | } |
167 | } | 167 | } |
168 | 168 | ||
169 | impl HasStringValue for RawString { | 169 | // FIXME: merge `ast::RawString` and `ast::String`. |
170 | impl HasStringValue for ast::RawString { | ||
170 | fn value(&self) -> Option<Cow<'_, str>> { | 171 | fn value(&self) -> Option<Cow<'_, str>> { |
171 | let text = self.text().as_str(); | 172 | let text = self.text().as_str(); |
172 | let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; | 173 | let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; |
@@ -174,7 +175,7 @@ impl HasStringValue for RawString { | |||
174 | } | 175 | } |
175 | } | 176 | } |
176 | 177 | ||
177 | impl RawString { | 178 | impl ast::RawString { |
178 | pub fn map_range_up(&self, range: TextRange) -> Option<TextRange> { | 179 | pub fn map_range_up(&self, range: TextRange) -> Option<TextRange> { |
179 | let contents_range = self.text_range_between_quotes()?; | 180 | let contents_range = self.text_range_between_quotes()?; |
180 | assert!(TextRange::up_to(contents_range.len()).contains_range(range)); | 181 | assert!(TextRange::up_to(contents_range.len()).contains_range(range)); |
@@ -500,7 +501,7 @@ pub trait HasFormatSpecifier: AstToken { | |||
500 | } | 501 | } |
501 | } | 502 | } |
502 | 503 | ||
503 | impl HasFormatSpecifier for String { | 504 | impl HasFormatSpecifier for ast::String { |
504 | fn char_ranges( | 505 | fn char_ranges( |
505 | &self, | 506 | &self, |
506 | ) -> Option<Vec<(TextRange, Result<char, rustc_lexer::unescape::EscapeError>)>> { | 507 | ) -> Option<Vec<(TextRange, Result<char, rustc_lexer::unescape::EscapeError>)>> { |
@@ -521,7 +522,7 @@ impl HasFormatSpecifier for String { | |||
521 | } | 522 | } |
522 | } | 523 | } |
523 | 524 | ||
524 | impl HasFormatSpecifier for RawString { | 525 | impl HasFormatSpecifier for ast::RawString { |
525 | fn char_ranges( | 526 | fn char_ranges( |
526 | &self, | 527 | &self, |
527 | ) -> Option<Vec<(TextRange, Result<char, rustc_lexer::unescape::EscapeError>)>> { | 528 | ) -> Option<Vec<(TextRange, Result<char, rustc_lexer::unescape::EscapeError>)>> { |
@@ -536,3 +537,77 @@ impl HasFormatSpecifier for RawString { | |||
536 | Some(res) | 537 | Some(res) |
537 | } | 538 | } |
538 | } | 539 | } |
540 | |||
541 | impl ast::IntNumber { | ||
542 | #[rustfmt::skip] | ||
543 | pub(crate) const SUFFIXES: &'static [&'static str] = &[ | ||
544 | "u8", "u16", "u32", "u64", "u128", "usize", | ||
545 | "i8", "i16", "i32", "i64", "i128", "isize", | ||
546 | ]; | ||
547 | |||
548 | pub fn radix(&self) -> Radix { | ||
549 | match self.text().get(..2).unwrap_or_default() { | ||
550 | "0b" => Radix::Binary, | ||
551 | "0o" => Radix::Octal, | ||
552 | "0x" => Radix::Hexadecimal, | ||
553 | _ => Radix::Decimal, | ||
554 | } | ||
555 | } | ||
556 | |||
557 | pub fn value(&self) -> Option<u128> { | ||
558 | let token = self.syntax(); | ||
559 | |||
560 | let mut text = token.text().as_str(); | ||
561 | if let Some(suffix) = self.suffix() { | ||
562 | text = &text[..text.len() - suffix.len()] | ||
563 | } | ||
564 | |||
565 | let radix = self.radix(); | ||
566 | text = &text[radix.prefix_len()..]; | ||
567 | |||
568 | let buf; | ||
569 | if text.contains("_") { | ||
570 | buf = text.replace('_', ""); | ||
571 | text = buf.as_str(); | ||
572 | }; | ||
573 | |||
574 | let value = u128::from_str_radix(text, radix as u32).ok()?; | ||
575 | Some(value) | ||
576 | } | ||
577 | |||
578 | pub fn suffix(&self) -> Option<&str> { | ||
579 | let text = self.text(); | ||
580 | // FIXME: don't check a fixed set of suffixes, `1_0_1___lol` is valid | ||
581 | // syntax, suffix is `lol`. | ||
582 | ast::IntNumber::SUFFIXES.iter().find_map(|suffix| { | ||
583 | if text.ends_with(suffix) { | ||
584 | return Some(&text[text.len() - suffix.len()..]); | ||
585 | } | ||
586 | None | ||
587 | }) | ||
588 | } | ||
589 | } | ||
590 | |||
591 | impl ast::FloatNumber { | ||
592 | pub(crate) const SUFFIXES: &'static [&'static str] = &["f32", "f64"]; | ||
593 | } | ||
594 | |||
595 | #[derive(Debug, PartialEq, Eq, Copy, Clone)] | ||
596 | pub enum Radix { | ||
597 | Binary = 2, | ||
598 | Octal = 8, | ||
599 | Decimal = 10, | ||
600 | Hexadecimal = 16, | ||
601 | } | ||
602 | |||
603 | impl Radix { | ||
604 | pub const ALL: &'static [Radix] = | ||
605 | &[Radix::Binary, Radix::Octal, Radix::Decimal, Radix::Hexadecimal]; | ||
606 | |||
607 | const fn prefix_len(&self) -> usize { | ||
608 | match self { | ||
609 | Self::Decimal => 0, | ||
610 | _ => 2, | ||
611 | } | ||
612 | } | ||
613 | } | ||