aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/assists/src/handlers/convert_integer_literal.rs23
-rw-r--r--crates/syntax/src/ast/token_ext.rs63
2 files changed, 38 insertions, 48 deletions
diff --git a/crates/assists/src/handlers/convert_integer_literal.rs b/crates/assists/src/handlers/convert_integer_literal.rs
index 6d477c045..5957834d3 100644
--- a/crates/assists/src/handlers/convert_integer_literal.rs
+++ b/crates/assists/src/handlers/convert_integer_literal.rs
@@ -1,4 +1,4 @@
1use syntax::{ast, ast::Radix, AstNode}; 1use syntax::{ast, ast::Radix, AstToken};
2 2
3use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel}; 3use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
4 4
@@ -14,15 +14,13 @@ use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
14// const _: i32 = 0b1010; 14// const _: i32 = 0b1010;
15// ``` 15// ```
16pub(crate) fn convert_integer_literal(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 16pub(crate) fn convert_integer_literal(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
17 let literal = ctx.find_node_at_offset::<ast::Literal>()?; 17 let literal = ctx.find_node_at_offset::<ast::Literal>()?.as_int_number()?;
18 let (radix, value) = literal.as_int_number()?.value()?; 18 let radix = literal.radix();
19 let value = literal.value()?;
20 let suffix = literal.suffix();
19 21
20 let range = literal.syntax().text_range(); 22 let range = literal.syntax().text_range();
21 let group_id = GroupLabel("Convert integer base".into()); 23 let group_id = GroupLabel("Convert integer base".into());
22 let suffix = match literal.kind() {
23 ast::LiteralKind::IntNumber { suffix } => suffix,
24 _ => return None,
25 };
26 24
27 for &target_radix in Radix::ALL { 25 for &target_radix in Radix::ALL {
28 if target_radix == radix { 26 if target_radix == radix {
@@ -36,16 +34,11 @@ pub(crate) fn convert_integer_literal(acc: &mut Assists, ctx: &AssistContext) ->
36 Radix::Hexadecimal => format!("0x{:X}", value), 34 Radix::Hexadecimal => format!("0x{:X}", value),
37 }; 35 };
38 36
39 let label = format!( 37 let label = format!("Convert {} to {}{}", literal, converted, suffix.unwrap_or_default());
40 "Convert {} to {}{}",
41 literal,
42 converted,
43 suffix.as_deref().unwrap_or_default()
44 );
45 38
46 // Appends the type suffix back into the new literal if it exists. 39 // Appends the type suffix back into the new literal if it exists.
47 if let Some(suffix) = &suffix { 40 if let Some(suffix) = suffix {
48 converted.push_str(&suffix); 41 converted.push_str(suffix);
49 } 42 }
50 43
51 acc.add_group( 44 acc.add_group(
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs
index 5623799b4..8d3fad5a6 100644
--- a/crates/syntax/src/ast/token_ext.rs
+++ b/crates/syntax/src/ast/token_ext.rs
@@ -166,6 +166,7 @@ impl HasStringValue for ast::String {
166 } 166 }
167} 167}
168 168
169// FIXME: merge `ast::RawString` and `ast::String`.
169impl HasStringValue for ast::RawString { 170impl 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();
@@ -544,29 +545,46 @@ impl ast::IntNumber {
544 "i8", "i16", "i32", "i64", "i128", "isize", 545 "i8", "i16", "i32", "i64", "i128", "isize",
545 ]; 546 ];
546 547
547 // FIXME: should probably introduce string token type? 548 pub fn radix(&self) -> Radix {
548 // https://github.com/rust-analyzer/rust-analyzer/issues/6308 549 match self.text().get(..2).unwrap_or_default() {
549 pub fn value(&self) -> Option<(Radix, u128)> { 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> {
550 let token = self.syntax(); 558 let token = self.syntax();
551 559
552 let mut text = token.text().as_str(); 560 let mut text = token.text().as_str();
553 for suffix in ast::IntNumber::SUFFIXES { 561 if let Some(suffix) = self.suffix() {
554 if let Some(without_suffix) = text.strip_suffix(suffix) { 562 text = &text[..text.len() - suffix.len()]
555 text = without_suffix;
556 break;
557 }
558 } 563 }
559 564
565 let radix = self.radix();
566 text = &text[radix.prefix_len()..];
567
560 let buf; 568 let buf;
561 if text.contains("_") { 569 if text.contains("_") {
562 buf = text.replace('_', ""); 570 buf = text.replace('_', "");
563 text = buf.as_str(); 571 text = buf.as_str();
564 }; 572 };
565 573
566 let radix = Radix::identify(text)?; 574 let value = u128::from_str_radix(text, radix as u32).ok()?;
567 let digits = &text[radix.prefix_len()..]; 575 Some(value)
568 let value = u128::from_str_radix(digits, radix as u32).ok()?; 576 }
569 Some((radix, value)) 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 })
570 } 588 }
571} 589}
572 590
@@ -586,27 +604,6 @@ impl Radix {
586 pub const ALL: &'static [Radix] = 604 pub const ALL: &'static [Radix] =
587 &[Radix::Binary, Radix::Octal, Radix::Decimal, Radix::Hexadecimal]; 605 &[Radix::Binary, Radix::Octal, Radix::Decimal, Radix::Hexadecimal];
588 606
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 { 607 const fn prefix_len(&self) -> usize {
611 match self { 608 match self {
612 Self::Decimal => 0, 609 Self::Decimal => 0,