|
1 | 1 | use std::borrow::Cow;
|
2 | 2 |
|
3 |
| -use syntax::{ast, ast::IsString, AstToken, TextRange, TextSize}; |
| 3 | +use ide_db::syntax_helpers::insert_whitespace_into_node::insert_ws_into; |
| 4 | +use syntax::{ast, ast::IsString, AstNode, AstToken, TextRange, TextSize}; |
4 | 5 |
|
5 | 6 | use crate::{utils::required_hashes, AssistContext, AssistId, AssistKind, Assists};
|
6 | 7 |
|
@@ -156,11 +157,76 @@ pub(crate) fn remove_hash(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
|
156 | 157 | })
|
157 | 158 | }
|
158 | 159 |
|
| 160 | +// Assist: inline_str_literal |
| 161 | +// |
| 162 | +// Inline const variable as static str literal. |
| 163 | +// |
| 164 | +// ``` |
| 165 | +// const STRING: &str = "Hello, World!"; |
| 166 | +// |
| 167 | +// fn something() -> &'static str { |
| 168 | +// STR$0ING |
| 169 | +// } |
| 170 | +// ``` |
| 171 | +// -> |
| 172 | +// ``` |
| 173 | +// const STRING: &str = "Hello, World!"; |
| 174 | +// |
| 175 | +// fn something() -> &'static str { |
| 176 | +// "Hello, World!" |
| 177 | +// } |
| 178 | +// ``` |
| 179 | +pub(crate) fn inline_str_literal(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { |
| 180 | + let variable = ctx.find_node_at_offset::<ast::PathExpr>()?; |
| 181 | + |
| 182 | + if let hir::PathResolution::Def(hir::ModuleDef::Const(konst)) = |
| 183 | + ctx.sema.resolve_path(&variable.path()?)? |
| 184 | + { |
| 185 | + if !konst.ty(ctx.sema.db).as_reference()?.0.as_builtin()?.is_str() { |
| 186 | + return None; |
| 187 | + } |
| 188 | + |
| 189 | + // FIXME: Make sure it's not possible to eval during diagnostic error |
| 190 | + let value = match konst.value(ctx.sema.db)? { |
| 191 | + ast::Expr::Literal(lit) => lit.to_string(), |
| 192 | + ast::Expr::BlockExpr(_) |
| 193 | + | ast::Expr::IfExpr(_) |
| 194 | + | ast::Expr::MatchExpr(_) |
| 195 | + | ast::Expr::CallExpr(_) => match konst.render_eval(ctx.sema.db) { |
| 196 | + Ok(result) => result, |
| 197 | + Err(_) => return None, |
| 198 | + }, |
| 199 | + ast::Expr::MacroExpr(makro) => { |
| 200 | + let makro_call = makro.syntax().children().find_map(ast::MacroCall::cast)?; |
| 201 | + let makro_hir = ctx.sema.resolve_macro_call(&makro_call)?; |
| 202 | + |
| 203 | + // This should not be necessary because of the `makro_call` check |
| 204 | + if !makro_hir.is_fn_like(ctx.sema.db) { |
| 205 | + return None; |
| 206 | + } |
| 207 | + |
| 208 | + // FIXME: Make procedural/build-in macro tests |
| 209 | + insert_ws_into(ctx.sema.expand(&makro_call)?).to_string() |
| 210 | + } |
| 211 | + _ => return None, |
| 212 | + }; |
| 213 | + |
| 214 | + let id = AssistId("inline_str_literal", AssistKind::RefactorInline); |
| 215 | + let label = "Inline as static `&str` literal"; |
| 216 | + let target = variable.syntax().text_range(); |
| 217 | + |
| 218 | + acc.add(id, label, target, |edit| { |
| 219 | + edit.replace(variable.syntax().text_range(), value); |
| 220 | + }); |
| 221 | + } |
| 222 | + |
| 223 | + Some(()) |
| 224 | +} |
| 225 | + |
159 | 226 | #[cfg(test)]
|
160 | 227 | mod tests {
|
161 |
| - use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; |
162 |
| - |
163 | 228 | use super::*;
|
| 229 | + use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; |
164 | 230 |
|
165 | 231 | #[test]
|
166 | 232 | fn make_raw_string_target() {
|
@@ -483,4 +549,213 @@ string"###;
|
483 | 549 | "#,
|
484 | 550 | );
|
485 | 551 | }
|
| 552 | + |
| 553 | + #[test] |
| 554 | + fn inline_expr_as_str_lit() { |
| 555 | + check_assist( |
| 556 | + inline_str_literal, |
| 557 | + r#" |
| 558 | + const STRING: &str = "Hello, World!"; |
| 559 | +
|
| 560 | + fn something() -> &'static str { |
| 561 | + STR$0ING |
| 562 | + } |
| 563 | + "#, |
| 564 | + r#" |
| 565 | + const STRING: &str = "Hello, World!"; |
| 566 | +
|
| 567 | + fn something() -> &'static str { |
| 568 | + "Hello, World!" |
| 569 | + } |
| 570 | + "#, |
| 571 | + ); |
| 572 | + } |
| 573 | + |
| 574 | + #[test] |
| 575 | + fn inline_eval_const_block_expr_to_str_lit() { |
| 576 | + check_assist( |
| 577 | + inline_str_literal, |
| 578 | + r#" |
| 579 | + const STRING: &str = { |
| 580 | + let x = 9; |
| 581 | + if x + 10 == 21 { |
| 582 | + "Hello, World!" |
| 583 | + } else { |
| 584 | + "World, Hello!" |
| 585 | + } |
| 586 | + }; |
| 587 | +
|
| 588 | + fn something() -> &'static str { |
| 589 | + STR$0ING |
| 590 | + } |
| 591 | + "#, |
| 592 | + r#" |
| 593 | + const STRING: &str = { |
| 594 | + let x = 9; |
| 595 | + if x + 10 == 21 { |
| 596 | + "Hello, World!" |
| 597 | + } else { |
| 598 | + "World, Hello!" |
| 599 | + } |
| 600 | + }; |
| 601 | +
|
| 602 | + fn something() -> &'static str { |
| 603 | + "World, Hello!" |
| 604 | + } |
| 605 | + "#, |
| 606 | + ); |
| 607 | + } |
| 608 | + |
| 609 | + #[test] |
| 610 | + fn inline_eval_const_block_macro_expr_to_str_lit() { |
| 611 | + check_assist( |
| 612 | + inline_str_literal, |
| 613 | + r#" |
| 614 | + macro_rules! co {() => {"World, Hello!"};} |
| 615 | + const STRING: &str = { co!() }; |
| 616 | +
|
| 617 | + fn something() -> &'static str { |
| 618 | + STR$0ING |
| 619 | + } |
| 620 | + "#, |
| 621 | + r#" |
| 622 | + macro_rules! co {() => {"World, Hello!"};} |
| 623 | + const STRING: &str = { co!() }; |
| 624 | +
|
| 625 | + fn something() -> &'static str { |
| 626 | + "World, Hello!" |
| 627 | + } |
| 628 | + "#, |
| 629 | + ); |
| 630 | + } |
| 631 | + |
| 632 | + #[test] |
| 633 | + fn inline_eval_const_match_expr_to_str_lit() { |
| 634 | + check_assist( |
| 635 | + inline_str_literal, |
| 636 | + r#" |
| 637 | + const STRING: &str = match 9 + 10 { |
| 638 | + 0..18 => "Hello, World!", |
| 639 | + _ => "World, Hello!" |
| 640 | + }; |
| 641 | +
|
| 642 | + fn something() -> &'static str { |
| 643 | + STR$0ING |
| 644 | + } |
| 645 | + "#, |
| 646 | + r#" |
| 647 | + const STRING: &str = match 9 + 10 { |
| 648 | + 0..18 => "Hello, World!", |
| 649 | + _ => "World, Hello!" |
| 650 | + }; |
| 651 | +
|
| 652 | + fn something() -> &'static str { |
| 653 | + "World, Hello!" |
| 654 | + } |
| 655 | + "#, |
| 656 | + ); |
| 657 | + } |
| 658 | + |
| 659 | + #[test] |
| 660 | + fn inline_eval_const_if_expr_to_str_lit() { |
| 661 | + check_assist( |
| 662 | + inline_str_literal, |
| 663 | + r#" |
| 664 | + const STRING: &str = if 1 + 2 == 4 { |
| 665 | + "Hello, World!" |
| 666 | + } else { |
| 667 | + "World, Hello!" |
| 668 | + } |
| 669 | +
|
| 670 | + fn something() -> &'static str { |
| 671 | + STR$0ING |
| 672 | + } |
| 673 | + "#, |
| 674 | + r#" |
| 675 | + const STRING: &str = if 1 + 2 == 4 { |
| 676 | + "Hello, World!" |
| 677 | + } else { |
| 678 | + "World, Hello!" |
| 679 | + } |
| 680 | +
|
| 681 | + fn something() -> &'static str { |
| 682 | + "World, Hello!" |
| 683 | + } |
| 684 | + "#, |
| 685 | + ); |
| 686 | + } |
| 687 | + |
| 688 | + #[test] |
| 689 | + fn inline_eval_const_macro_expr_to_str_lit() { |
| 690 | + check_assist( |
| 691 | + inline_str_literal, |
| 692 | + r#" |
| 693 | + macro_rules! co {() => {"World, Hello!"};} |
| 694 | + const STRING: &str = co!(); |
| 695 | +
|
| 696 | + fn something() -> &'static str { |
| 697 | + STR$0ING |
| 698 | + } |
| 699 | + "#, |
| 700 | + r#" |
| 701 | + macro_rules! co {() => {"World, Hello!"};} |
| 702 | + const STRING: &str = co!(); |
| 703 | +
|
| 704 | + fn something() -> &'static str { |
| 705 | + "World, Hello!" |
| 706 | + } |
| 707 | + "#, |
| 708 | + ); |
| 709 | + } |
| 710 | + |
| 711 | + #[test] |
| 712 | + fn inline_eval_const_call_expr_to_str_lit() { |
| 713 | + check_assist( |
| 714 | + inline_str_literal, |
| 715 | + r#" |
| 716 | + const fn const_call() -> &'static str {"World, Hello!"} |
| 717 | + const STRING: &str = const_call(); |
| 718 | +
|
| 719 | + fn something() -> &'static str { |
| 720 | + STR$0ING |
| 721 | + } |
| 722 | + "#, |
| 723 | + r#" |
| 724 | + const fn const_call() -> &'static str {"World, Hello!"} |
| 725 | + const STRING: &str = const_call(); |
| 726 | +
|
| 727 | + fn something() -> &'static str { |
| 728 | + "World, Hello!" |
| 729 | + } |
| 730 | + "#, |
| 731 | + ); |
| 732 | + } |
| 733 | + |
| 734 | + #[test] |
| 735 | + fn inline_expr_as_str_lit_not_applicable() { |
| 736 | + check_assist_not_applicable( |
| 737 | + inline_str_literal, |
| 738 | + r#" |
| 739 | + const STRING: &str = "Hello, World!"; |
| 740 | +
|
| 741 | + fn something() -> &'static str { |
| 742 | + STRING $0 |
| 743 | + } |
| 744 | + "#, |
| 745 | + ); |
| 746 | + } |
| 747 | + |
| 748 | + #[test] |
| 749 | + fn inline_expr_as_str_lit_not_applicable_const() { |
| 750 | + check_assist_not_applicable( |
| 751 | + inline_str_literal, |
| 752 | + r#" |
| 753 | + const STR$0ING: &str = "Hello, World!"; |
| 754 | +
|
| 755 | + fn something() -> &'static str { |
| 756 | + STRING |
| 757 | + } |
| 758 | + "#, |
| 759 | + ); |
| 760 | + } |
486 | 761 | }
|
0 commit comments