aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2020-11-03 18:49:15 +0000
committerAleksey Kladov <[email protected]>2020-11-03 18:57:57 +0000
commit9349353e044c06a5baa9cfbcfd98e7d329b0bc0e (patch)
tree77aa0c2867b354294ea8a53ef85045a31f4cfa1f /crates
parent5e622332774fbd57c12addd46b058c8feb2b08a6 (diff)
Fix overflow panic in convert_interger_literal assist
This also seizes the opportunity to move integer literal parsing to the syntax crate, were it logically belongs. Note though that this is still done in an ad hoc manner -- we probably should split kitchen sink ast::Literal into a separate APIs for strings, ints, etc
Diffstat (limited to 'crates')
-rw-r--r--crates/assists/src/handlers/convert_integer_literal.rs206
-rw-r--r--crates/syntax/src/ast.rs2
-rw-r--r--crates/syntax/src/ast/expr_ext.rs65
3 files changed, 137 insertions, 136 deletions
diff --git a/crates/assists/src/handlers/convert_integer_literal.rs b/crates/assists/src/handlers/convert_integer_literal.rs
index ea35e833a..c8af80701 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, AstNode, SmolStr}; 1use syntax::{ast, ast::Radix, AstNode};
2 2
3use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel}; 3use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
4 4
@@ -15,37 +15,34 @@ use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
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>()?;
18 let (radix, value) = literal.int_value()?;
19
18 let range = literal.syntax().text_range(); 20 let range = literal.syntax().text_range();
19 let group_id = GroupLabel("Convert integer base".into()); 21 let group_id = GroupLabel("Convert integer base".into());
20
21 let suffix = match literal.kind() { 22 let suffix = match literal.kind() {
22 ast::LiteralKind::IntNumber { suffix } => suffix, 23 ast::LiteralKind::IntNumber { suffix } => suffix,
23 _ => return None, 24 _ => return None,
24 }; 25 };
25 let suffix_len = suffix.as_ref().map(|s| s.len()).unwrap_or(0); 26
26 let raw_literal_text = literal.syntax().to_string(); 27 for &target_radix in Radix::ALL {
27 28 if target_radix == radix {
28 // Gets the literal's text without the type suffix and without underscores.
29 let literal_text = raw_literal_text
30 .chars()
31 .take(raw_literal_text.len() - suffix_len)
32 .filter(|c| *c != '_')
33 .collect::<SmolStr>();
34 let literal_base = IntegerLiteralBase::identify(&literal_text)?;
35
36 for base in IntegerLiteralBase::bases() {
37 if *base == literal_base {
38 continue; 29 continue;
39 } 30 }
40 31
41 let mut converted = literal_base.convert(&literal_text, base); 32 let mut converted = match target_radix {
42 33 Radix::Binary => format!("0b{:b}", value),
43 let label = if let Some(suffix) = &suffix { 34 Radix::Octal => format!("0o{:o}", value),
44 format!("Convert {} ({}) to {}", &literal_text, suffix, &converted) 35 Radix::Decimal => value.to_string(),
45 } else { 36 Radix::Hexadecimal => format!("0x{:X}", value),
46 format!("Convert {} to {}", &literal_text, &converted)
47 }; 37 };
48 38
39 let label = format!(
40 "Convert {} to {}{}",
41 literal,
42 converted,
43 suffix.as_deref().unwrap_or_default()
44 );
45
49 // Appends the type suffix back into the new literal if it exists. 46 // Appends the type suffix back into the new literal if it exists.
50 if let Some(suffix) = &suffix { 47 if let Some(suffix) = &suffix {
51 converted.push_str(&suffix); 48 converted.push_str(&suffix);
@@ -63,79 +60,11 @@ pub(crate) fn convert_integer_literal(acc: &mut Assists, ctx: &AssistContext) ->
63 Some(()) 60 Some(())
64} 61}
65 62
66#[derive(Debug, PartialEq, Eq)]
67enum IntegerLiteralBase {
68 Binary,
69 Octal,
70 Decimal,
71 Hexadecimal,
72}
73
74impl IntegerLiteralBase {
75 fn identify(literal_text: &str) -> Option<Self> {
76 // We cannot express a literal in anything other than decimal in under 3 characters, so we return here if possible.
77 if literal_text.len() < 3 && literal_text.chars().all(|c| c.is_digit(10)) {
78 return Some(Self::Decimal);
79 }
80
81 let base = match &literal_text[..2] {
82 "0b" => Self::Binary,
83 "0o" => Self::Octal,
84 "0x" => Self::Hexadecimal,
85 _ => Self::Decimal,
86 };
87
88 // Checks that all characters after the base prefix are all valid digits for that base.
89 if literal_text[base.prefix_len()..].chars().all(|c| c.is_digit(base.base())) {
90 Some(base)
91 } else {
92 None
93 }
94 }
95
96 fn convert(&self, literal_text: &str, to: &IntegerLiteralBase) -> String {
97 let digits = &literal_text[self.prefix_len()..];
98 let value = u128::from_str_radix(digits, self.base()).unwrap();
99
100 match to {
101 Self::Binary => format!("0b{:b}", value),
102 Self::Octal => format!("0o{:o}", value),
103 Self::Decimal => value.to_string(),
104 Self::Hexadecimal => format!("0x{:X}", value),
105 }
106 }
107
108 const fn base(&self) -> u32 {
109 match self {
110 Self::Binary => 2,
111 Self::Octal => 8,
112 Self::Decimal => 10,
113 Self::Hexadecimal => 16,
114 }
115 }
116
117 const fn prefix_len(&self) -> usize {
118 match self {
119 Self::Decimal => 0,
120 _ => 2,
121 }
122 }
123
124 const fn bases() -> &'static [IntegerLiteralBase] {
125 &[
126 IntegerLiteralBase::Binary,
127 IntegerLiteralBase::Octal,
128 IntegerLiteralBase::Decimal,
129 IntegerLiteralBase::Hexadecimal,
130 ]
131 }
132}
133
134#[cfg(test)] 63#[cfg(test)]
135mod tests { 64mod tests {
65 use crate::tests::{check_assist_by_label, check_assist_not_applicable, check_assist_target};
136 66
137 use super::*; 67 use super::*;
138 use crate::tests::{check_assist_by_label, check_assist_target};
139 68
140 #[test] 69 #[test]
141 fn binary_target() { 70 fn binary_target() {
@@ -317,21 +246,21 @@ mod tests {
317 convert_integer_literal, 246 convert_integer_literal,
318 before, 247 before,
319 "const _: i32 = 0b1111101000;", 248 "const _: i32 = 0b1111101000;",
320 "Convert 1000 to 0b1111101000", 249 "Convert 1_00_0 to 0b1111101000",
321 ); 250 );
322 251
323 check_assist_by_label( 252 check_assist_by_label(
324 convert_integer_literal, 253 convert_integer_literal,
325 before, 254 before,
326 "const _: i32 = 0o1750;", 255 "const _: i32 = 0o1750;",
327 "Convert 1000 to 0o1750", 256 "Convert 1_00_0 to 0o1750",
328 ); 257 );
329 258
330 check_assist_by_label( 259 check_assist_by_label(
331 convert_integer_literal, 260 convert_integer_literal,
332 before, 261 before,
333 "const _: i32 = 0x3E8;", 262 "const _: i32 = 0x3E8;",
334 "Convert 1000 to 0x3E8", 263 "Convert 1_00_0 to 0x3E8",
335 ); 264 );
336 } 265 }
337 266
@@ -343,21 +272,21 @@ mod tests {
343 convert_integer_literal, 272 convert_integer_literal,
344 before, 273 before,
345 "const _: i32 = 0b1010;", 274 "const _: i32 = 0b1010;",
346 "Convert 10 to 0b1010", 275 "Convert 1_0 to 0b1010",
347 ); 276 );
348 277
349 check_assist_by_label( 278 check_assist_by_label(
350 convert_integer_literal, 279 convert_integer_literal,
351 before, 280 before,
352 "const _: i32 = 0o12;", 281 "const _: i32 = 0o12;",
353 "Convert 10 to 0o12", 282 "Convert 1_0 to 0o12",
354 ); 283 );
355 284
356 check_assist_by_label( 285 check_assist_by_label(
357 convert_integer_literal, 286 convert_integer_literal,
358 before, 287 before,
359 "const _: i32 = 0xA;", 288 "const _: i32 = 0xA;",
360 "Convert 10 to 0xA", 289 "Convert 1_0 to 0xA",
361 ); 290 );
362 } 291 }
363 292
@@ -369,21 +298,21 @@ mod tests {
369 convert_integer_literal, 298 convert_integer_literal,
370 before, 299 before,
371 "const _: i32 = 0b11111111;", 300 "const _: i32 = 0b11111111;",
372 "Convert 0xFF to 0b11111111", 301 "Convert 0x_F_F to 0b11111111",
373 ); 302 );
374 303
375 check_assist_by_label( 304 check_assist_by_label(
376 convert_integer_literal, 305 convert_integer_literal,
377 before, 306 before,
378 "const _: i32 = 0o377;", 307 "const _: i32 = 0o377;",
379 "Convert 0xFF to 0o377", 308 "Convert 0x_F_F to 0o377",
380 ); 309 );
381 310
382 check_assist_by_label( 311 check_assist_by_label(
383 convert_integer_literal, 312 convert_integer_literal,
384 before, 313 before,
385 "const _: i32 = 255;", 314 "const _: i32 = 255;",
386 "Convert 0xFF to 255", 315 "Convert 0x_F_F to 255",
387 ); 316 );
388 } 317 }
389 318
@@ -395,21 +324,21 @@ mod tests {
395 convert_integer_literal, 324 convert_integer_literal,
396 before, 325 before,
397 "const _: i32 = 0o377;", 326 "const _: i32 = 0o377;",
398 "Convert 0b11111111 to 0o377", 327 "Convert 0b1111_1111 to 0o377",
399 ); 328 );
400 329
401 check_assist_by_label( 330 check_assist_by_label(
402 convert_integer_literal, 331 convert_integer_literal,
403 before, 332 before,
404 "const _: i32 = 255;", 333 "const _: i32 = 255;",
405 "Convert 0b11111111 to 255", 334 "Convert 0b1111_1111 to 255",
406 ); 335 );
407 336
408 check_assist_by_label( 337 check_assist_by_label(
409 convert_integer_literal, 338 convert_integer_literal,
410 before, 339 before,
411 "const _: i32 = 0xFF;", 340 "const _: i32 = 0xFF;",
412 "Convert 0b11111111 to 0xFF", 341 "Convert 0b1111_1111 to 0xFF",
413 ); 342 );
414 } 343 }
415 344
@@ -421,21 +350,21 @@ mod tests {
421 convert_integer_literal, 350 convert_integer_literal,
422 before, 351 before,
423 "const _: i32 = 0b11111111;", 352 "const _: i32 = 0b11111111;",
424 "Convert 0o377 to 0b11111111", 353 "Convert 0o3_77 to 0b11111111",
425 ); 354 );
426 355
427 check_assist_by_label( 356 check_assist_by_label(
428 convert_integer_literal, 357 convert_integer_literal,
429 before, 358 before,
430 "const _: i32 = 255;", 359 "const _: i32 = 255;",
431 "Convert 0o377 to 255", 360 "Convert 0o3_77 to 255",
432 ); 361 );
433 362
434 check_assist_by_label( 363 check_assist_by_label(
435 convert_integer_literal, 364 convert_integer_literal,
436 before, 365 before,
437 "const _: i32 = 0xFF;", 366 "const _: i32 = 0xFF;",
438 "Convert 0o377 to 0xFF", 367 "Convert 0o3_77 to 0xFF",
439 ); 368 );
440 } 369 }
441 370
@@ -447,21 +376,21 @@ mod tests {
447 convert_integer_literal, 376 convert_integer_literal,
448 before, 377 before,
449 "const _: i32 = 0b1111101000i32;", 378 "const _: i32 = 0b1111101000i32;",
450 "Convert 1000 (i32) to 0b1111101000", 379 "Convert 1000i32 to 0b1111101000i32",
451 ); 380 );
452 381
453 check_assist_by_label( 382 check_assist_by_label(
454 convert_integer_literal, 383 convert_integer_literal,
455 before, 384 before,
456 "const _: i32 = 0o1750i32;", 385 "const _: i32 = 0o1750i32;",
457 "Convert 1000 (i32) to 0o1750", 386 "Convert 1000i32 to 0o1750i32",
458 ); 387 );
459 388
460 check_assist_by_label( 389 check_assist_by_label(
461 convert_integer_literal, 390 convert_integer_literal,
462 before, 391 before,
463 "const _: i32 = 0x3E8i32;", 392 "const _: i32 = 0x3E8i32;",
464 "Convert 1000 (i32) to 0x3E8", 393 "Convert 1000i32 to 0x3E8i32",
465 ); 394 );
466 } 395 }
467 396
@@ -473,21 +402,21 @@ mod tests {
473 convert_integer_literal, 402 convert_integer_literal,
474 before, 403 before,
475 "const _: i32 = 0b1010i32;", 404 "const _: i32 = 0b1010i32;",
476 "Convert 10 (i32) to 0b1010", 405 "Convert 10i32 to 0b1010i32",
477 ); 406 );
478 407
479 check_assist_by_label( 408 check_assist_by_label(
480 convert_integer_literal, 409 convert_integer_literal,
481 before, 410 before,
482 "const _: i32 = 0o12i32;", 411 "const _: i32 = 0o12i32;",
483 "Convert 10 (i32) to 0o12", 412 "Convert 10i32 to 0o12i32",
484 ); 413 );
485 414
486 check_assist_by_label( 415 check_assist_by_label(
487 convert_integer_literal, 416 convert_integer_literal,
488 before, 417 before,
489 "const _: i32 = 0xAi32;", 418 "const _: i32 = 0xAi32;",
490 "Convert 10 (i32) to 0xA", 419 "Convert 10i32 to 0xAi32",
491 ); 420 );
492 } 421 }
493 422
@@ -499,21 +428,21 @@ mod tests {
499 convert_integer_literal, 428 convert_integer_literal,
500 before, 429 before,
501 "const _: i32 = 0b11111111i32;", 430 "const _: i32 = 0b11111111i32;",
502 "Convert 0xFF (i32) to 0b11111111", 431 "Convert 0xFFi32 to 0b11111111i32",
503 ); 432 );
504 433
505 check_assist_by_label( 434 check_assist_by_label(
506 convert_integer_literal, 435 convert_integer_literal,
507 before, 436 before,
508 "const _: i32 = 0o377i32;", 437 "const _: i32 = 0o377i32;",
509 "Convert 0xFF (i32) to 0o377", 438 "Convert 0xFFi32 to 0o377i32",
510 ); 439 );
511 440
512 check_assist_by_label( 441 check_assist_by_label(
513 convert_integer_literal, 442 convert_integer_literal,
514 before, 443 before,
515 "const _: i32 = 255i32;", 444 "const _: i32 = 255i32;",
516 "Convert 0xFF (i32) to 255", 445 "Convert 0xFFi32 to 255i32",
517 ); 446 );
518 } 447 }
519 448
@@ -525,21 +454,21 @@ mod tests {
525 convert_integer_literal, 454 convert_integer_literal,
526 before, 455 before,
527 "const _: i32 = 0o377i32;", 456 "const _: i32 = 0o377i32;",
528 "Convert 0b11111111 (i32) to 0o377", 457 "Convert 0b11111111i32 to 0o377i32",
529 ); 458 );
530 459
531 check_assist_by_label( 460 check_assist_by_label(
532 convert_integer_literal, 461 convert_integer_literal,
533 before, 462 before,
534 "const _: i32 = 255i32;", 463 "const _: i32 = 255i32;",
535 "Convert 0b11111111 (i32) to 255", 464 "Convert 0b11111111i32 to 255i32",
536 ); 465 );
537 466
538 check_assist_by_label( 467 check_assist_by_label(
539 convert_integer_literal, 468 convert_integer_literal,
540 before, 469 before,
541 "const _: i32 = 0xFFi32;", 470 "const _: i32 = 0xFFi32;",
542 "Convert 0b11111111 (i32) to 0xFF", 471 "Convert 0b11111111i32 to 0xFFi32",
543 ); 472 );
544 } 473 }
545 474
@@ -551,21 +480,21 @@ mod tests {
551 convert_integer_literal, 480 convert_integer_literal,
552 before, 481 before,
553 "const _: i32 = 0b11111111i32;", 482 "const _: i32 = 0b11111111i32;",
554 "Convert 0o377 (i32) to 0b11111111", 483 "Convert 0o377i32 to 0b11111111i32",
555 ); 484 );
556 485
557 check_assist_by_label( 486 check_assist_by_label(
558 convert_integer_literal, 487 convert_integer_literal,
559 before, 488 before,
560 "const _: i32 = 255i32;", 489 "const _: i32 = 255i32;",
561 "Convert 0o377 (i32) to 255", 490 "Convert 0o377i32 to 255i32",
562 ); 491 );
563 492
564 check_assist_by_label( 493 check_assist_by_label(
565 convert_integer_literal, 494 convert_integer_literal,
566 before, 495 before,
567 "const _: i32 = 0xFFi32;", 496 "const _: i32 = 0xFFi32;",
568 "Convert 0o377 (i32) to 0xFF", 497 "Convert 0o377i32 to 0xFFi32",
569 ); 498 );
570 } 499 }
571 500
@@ -577,21 +506,21 @@ mod tests {
577 convert_integer_literal, 506 convert_integer_literal,
578 before, 507 before,
579 "const _: i32 = 0b1111101000i32;", 508 "const _: i32 = 0b1111101000i32;",
580 "Convert 1000 (i32) to 0b1111101000", 509 "Convert 1_00_0i32 to 0b1111101000i32",
581 ); 510 );
582 511
583 check_assist_by_label( 512 check_assist_by_label(
584 convert_integer_literal, 513 convert_integer_literal,
585 before, 514 before,
586 "const _: i32 = 0o1750i32;", 515 "const _: i32 = 0o1750i32;",
587 "Convert 1000 (i32) to 0o1750", 516 "Convert 1_00_0i32 to 0o1750i32",
588 ); 517 );
589 518
590 check_assist_by_label( 519 check_assist_by_label(
591 convert_integer_literal, 520 convert_integer_literal,
592 before, 521 before,
593 "const _: i32 = 0x3E8i32;", 522 "const _: i32 = 0x3E8i32;",
594 "Convert 1000 (i32) to 0x3E8", 523 "Convert 1_00_0i32 to 0x3E8i32",
595 ); 524 );
596 } 525 }
597 526
@@ -603,21 +532,21 @@ mod tests {
603 convert_integer_literal, 532 convert_integer_literal,
604 before, 533 before,
605 "const _: i32 = 0b1010i32;", 534 "const _: i32 = 0b1010i32;",
606 "Convert 10 (i32) to 0b1010", 535 "Convert 1_0i32 to 0b1010i32",
607 ); 536 );
608 537
609 check_assist_by_label( 538 check_assist_by_label(
610 convert_integer_literal, 539 convert_integer_literal,
611 before, 540 before,
612 "const _: i32 = 0o12i32;", 541 "const _: i32 = 0o12i32;",
613 "Convert 10 (i32) to 0o12", 542 "Convert 1_0i32 to 0o12i32",
614 ); 543 );
615 544
616 check_assist_by_label( 545 check_assist_by_label(
617 convert_integer_literal, 546 convert_integer_literal,
618 before, 547 before,
619 "const _: i32 = 0xAi32;", 548 "const _: i32 = 0xAi32;",
620 "Convert 10 (i32) to 0xA", 549 "Convert 1_0i32 to 0xAi32",
621 ); 550 );
622 } 551 }
623 552
@@ -629,21 +558,21 @@ mod tests {
629 convert_integer_literal, 558 convert_integer_literal,
630 before, 559 before,
631 "const _: i32 = 0b11111111i32;", 560 "const _: i32 = 0b11111111i32;",
632 "Convert 0xFF (i32) to 0b11111111", 561 "Convert 0x_F_Fi32 to 0b11111111i32",
633 ); 562 );
634 563
635 check_assist_by_label( 564 check_assist_by_label(
636 convert_integer_literal, 565 convert_integer_literal,
637 before, 566 before,
638 "const _: i32 = 0o377i32;", 567 "const _: i32 = 0o377i32;",
639 "Convert 0xFF (i32) to 0o377", 568 "Convert 0x_F_Fi32 to 0o377i32",
640 ); 569 );
641 570
642 check_assist_by_label( 571 check_assist_by_label(
643 convert_integer_literal, 572 convert_integer_literal,
644 before, 573 before,
645 "const _: i32 = 255i32;", 574 "const _: i32 = 255i32;",
646 "Convert 0xFF (i32) to 255", 575 "Convert 0x_F_Fi32 to 255i32",
647 ); 576 );
648 } 577 }
649 578
@@ -655,21 +584,21 @@ mod tests {
655 convert_integer_literal, 584 convert_integer_literal,
656 before, 585 before,
657 "const _: i32 = 0o377i32;", 586 "const _: i32 = 0o377i32;",
658 "Convert 0b11111111 (i32) to 0o377", 587 "Convert 0b1111_1111i32 to 0o377i32",
659 ); 588 );
660 589
661 check_assist_by_label( 590 check_assist_by_label(
662 convert_integer_literal, 591 convert_integer_literal,
663 before, 592 before,
664 "const _: i32 = 255i32;", 593 "const _: i32 = 255i32;",
665 "Convert 0b11111111 (i32) to 255", 594 "Convert 0b1111_1111i32 to 255i32",
666 ); 595 );
667 596
668 check_assist_by_label( 597 check_assist_by_label(
669 convert_integer_literal, 598 convert_integer_literal,
670 before, 599 before,
671 "const _: i32 = 0xFFi32;", 600 "const _: i32 = 0xFFi32;",
672 "Convert 0b11111111 (i32) to 0xFF", 601 "Convert 0b1111_1111i32 to 0xFFi32",
673 ); 602 );
674 } 603 }
675 604
@@ -681,21 +610,28 @@ mod tests {
681 convert_integer_literal, 610 convert_integer_literal,
682 before, 611 before,
683 "const _: i32 = 0b11111111i32;", 612 "const _: i32 = 0b11111111i32;",
684 "Convert 0o377 (i32) to 0b11111111", 613 "Convert 0o3_77i32 to 0b11111111i32",
685 ); 614 );
686 615
687 check_assist_by_label( 616 check_assist_by_label(
688 convert_integer_literal, 617 convert_integer_literal,
689 before, 618 before,
690 "const _: i32 = 255i32;", 619 "const _: i32 = 255i32;",
691 "Convert 0o377 (i32) to 255", 620 "Convert 0o3_77i32 to 255i32",
692 ); 621 );
693 622
694 check_assist_by_label( 623 check_assist_by_label(
695 convert_integer_literal, 624 convert_integer_literal,
696 before, 625 before,
697 "const _: i32 = 0xFFi32;", 626 "const _: i32 = 0xFFi32;",
698 "Convert 0o377 (i32) to 0xFF", 627 "Convert 0o3_77i32 to 0xFFi32",
699 ); 628 );
700 } 629 }
630
631 #[test]
632 fn convert_overflowing_literal() {
633 let before = "const _: i32 =
634 111111111111111111111111111111111111111111111111111111111111111111111111<|>;";
635 check_assist_not_applicable(convert_integer_literal, before);
636 }
701} 637}
diff --git a/crates/syntax/src/ast.rs b/crates/syntax/src/ast.rs
index 8a0e3d27b..a16ac6a7c 100644
--- a/crates/syntax/src/ast.rs
+++ b/crates/syntax/src/ast.rs
@@ -16,7 +16,7 @@ use crate::{
16}; 16};
17 17
18pub use self::{ 18pub use self::{
19 expr_ext::{ArrayExprKind, BinOp, Effect, ElseBranch, LiteralKind, PrefixOp, RangeOp}, 19 expr_ext::{ArrayExprKind, BinOp, Effect, ElseBranch, LiteralKind, PrefixOp, Radix, RangeOp},
20 generated::{nodes::*, tokens::*}, 20 generated::{nodes::*, tokens::*},
21 node_ext::{ 21 node_ext::{
22 AttrKind, FieldKind, NameOrNameRef, PathSegmentKind, SelfParamKind, SlicePatComponents, 22 AttrKind, FieldKind, NameOrNameRef, PathSegmentKind, SelfParamKind, SlicePatComponents,
diff --git a/crates/syntax/src/ast/expr_ext.rs b/crates/syntax/src/ast/expr_ext.rs
index f5ba87223..3aff01e83 100644
--- a/crates/syntax/src/ast/expr_ext.rs
+++ b/crates/syntax/src/ast/expr_ext.rs
@@ -358,6 +358,71 @@ impl ast::Literal {
358 _ => unreachable!(), 358 _ => unreachable!(),
359 } 359 }
360 } 360 }
361
362 // FIXME: should probably introduce string token type?
363 // https://github.com/rust-analyzer/rust-analyzer/issues/6308
364 pub fn int_value(&self) -> Option<(Radix, u128)> {
365 let suffix = match self.kind() {
366 LiteralKind::IntNumber { suffix } => suffix,
367 _ => return None,
368 };
369
370 let token = self.token();
371 let mut text = token.text().as_str();
372 text = &text[..text.len() - suffix.map_or(0, |it| it.len())];
373
374 let buf;
375 if text.contains("_") {
376 buf = text.replace('_', "");
377 text = buf.as_str();
378 };
379
380 let radix = Radix::identify(text)?;
381 let digits = &text[radix.prefix_len()..];
382 let value = u128::from_str_radix(digits, radix as u32).ok()?;
383 Some((radix, value))
384 }
385}
386
387#[derive(Debug, PartialEq, Eq, Copy, Clone)]
388pub enum Radix {
389 Binary = 2,
390 Octal = 8,
391 Decimal = 10,
392 Hexadecimal = 16,
393}
394
395impl Radix {
396 pub const ALL: &'static [Radix] =
397 &[Radix::Binary, Radix::Octal, Radix::Decimal, Radix::Hexadecimal];
398
399 fn identify(literal_text: &str) -> Option<Self> {
400 // We cannot express a literal in anything other than decimal in under 3 characters, so we return here if possible.
401 if literal_text.len() < 3 && literal_text.chars().all(|c| c.is_digit(10)) {
402 return Some(Self::Decimal);
403 }
404
405 let res = match &literal_text[..2] {
406 "0b" => Radix::Binary,
407 "0o" => Radix::Octal,
408 "0x" => Radix::Hexadecimal,
409 _ => Radix::Decimal,
410 };
411
412 // Checks that all characters after the base prefix are all valid digits for that base.
413 if literal_text[res.prefix_len()..].chars().all(|c| c.is_digit(res as u32)) {
414 Some(res)
415 } else {
416 None
417 }
418 }
419
420 const fn prefix_len(&self) -> usize {
421 match self {
422 Self::Decimal => 0,
423 _ => 2,
424 }
425 }
361} 426}
362 427
363#[derive(Debug, Clone, PartialEq, Eq)] 428#[derive(Debug, Clone, PartialEq, Eq)]