aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-11-03 18:59:15 +0000
committerGitHub <[email protected]>2020-11-03 18:59:15 +0000
commit7c94f1cb5e86739864871dc56fcbd7a6de116315 (patch)
tree0d8b672609e730efdf45b38c0955d9a26fc620ed /crates
parent060c8b2c96a0de4a131c4d780d2aac80afe13de8 (diff)
parent9349353e044c06a5baa9cfbcfd98e7d329b0bc0e (diff)
Merge #6454
6454: Fix overflow panic in convert_interger_literal assist r=matklad a=matklad bors r+ 🤖 Co-authored-by: Aleksey Kladov <[email protected]>
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)]