Skip to content

Commit 094c1e7

Browse files
committed
feat: inline const expr as static str
1 parent d42d55f commit 094c1e7

File tree

3 files changed

+300
-3
lines changed

3 files changed

+300
-3
lines changed

crates/ide-assists/src/handlers/raw_string.rs

Lines changed: 278 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::borrow::Cow;
22

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};
45

56
use crate::{utils::required_hashes, AssistContext, AssistId, AssistKind, Assists};
67

@@ -156,11 +157,76 @@ pub(crate) fn remove_hash(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
156157
})
157158
}
158159

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+
159226
#[cfg(test)]
160227
mod tests {
161-
use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
162-
163228
use super::*;
229+
use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
164230

165231
#[test]
166232
fn make_raw_string_target() {
@@ -483,4 +549,213 @@ string"###;
483549
"#,
484550
);
485551
}
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+
}
486761
}

crates/ide-assists/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@ mod handlers {
288288
raw_string::add_hash,
289289
raw_string::make_usual_string,
290290
raw_string::remove_hash,
291+
raw_string::inline_str_literal,
291292
remove_mut::remove_mut,
292293
remove_unused_param::remove_unused_param,
293294
remove_parentheses::remove_parentheses,

crates/ide-assists/src/tests/generated.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1567,6 +1567,27 @@ fn main() {
15671567
)
15681568
}
15691569

1570+
#[test]
1571+
fn doctest_inline_str_literal() {
1572+
check_doc_test(
1573+
"inline_str_literal",
1574+
r#####"
1575+
const STRING: &str = "Hello, World!";
1576+
1577+
fn something() -> &'static str {
1578+
STR$0ING
1579+
}
1580+
"#####,
1581+
r#####"
1582+
const STRING: &str = "Hello, World!";
1583+
1584+
fn something() -> &'static str {
1585+
"Hello, World!"
1586+
}
1587+
"#####,
1588+
)
1589+
}
1590+
15701591
#[test]
15711592
fn doctest_inline_type_alias() {
15721593
check_doc_test(

0 commit comments

Comments
 (0)