1use hir::HasCrate;
2use syntax::{AstNode, ast};
3
4use crate::{AssistContext, AssistId, Assists};
5
6pub(crate) fn inline_const_as_literal(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
26 let variable = ctx.find_node_at_offset::<ast::PathExpr>()?;
27
28 if let hir::PathResolution::Def(hir::ModuleDef::Const(konst)) =
29 ctx.sema.resolve_path(&variable.path()?)?
30 {
31 let konst_ty = konst.ty(ctx.sema.db);
32
33 let fuel = 20;
35
36 validate_type_recursively(ctx, Some(&konst_ty), false, fuel)?;
41
42 let value = konst
43 .eval(ctx.sema.db)
44 .ok()?
45 .render(ctx.sema.db, konst.krate(ctx.sema.db).to_display_target(ctx.sema.db));
46
47 let id = AssistId::refactor_inline("inline_const_as_literal");
48
49 let label = "Inline const as literal".to_owned();
50 let target = variable.syntax().text_range();
51
52 return acc.add(id, label, target, |edit| {
53 edit.replace(variable.syntax().text_range(), value);
54 });
55 }
56 None
57}
58
59fn validate_type_recursively(
60 ctx: &AssistContext<'_>,
61 ty_hir: Option<&hir::Type<'_>>,
62 refed: bool,
63 fuel: i32,
64) -> Option<()> {
65 match (fuel > 0, ty_hir) {
66 (true, Some(ty)) if ty.is_reference() => validate_type_recursively(
67 ctx,
68 ty.as_reference().map(|(ty, _)| ty).as_ref(),
69 true,
70 if refed { fuel } else { fuel - 1 },
72 ),
73 (true, Some(ty)) if ty.is_array() => validate_type_recursively(
74 ctx,
75 ty.as_array(ctx.db()).map(|(ty, _)| ty).as_ref(),
76 false,
77 fuel - 1,
78 ),
79 (true, Some(ty)) if ty.is_tuple() => ty
80 .tuple_fields(ctx.db())
81 .iter()
82 .all(|ty| validate_type_recursively(ctx, Some(ty), false, fuel - 1).is_some())
83 .then_some(()),
84 (true, Some(ty)) if refed && ty.is_slice() => {
85 validate_type_recursively(ctx, ty.as_slice().as_ref(), false, fuel - 1)
86 }
87 (_, Some(ty)) => match ty.as_builtin() {
88 Some(builtin) if refed || !builtin.is_str() => Some(()),
90 _ => None,
91 },
92 _ => None,
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99 use crate::tests::{check_assist, check_assist_not_applicable};
100
101 const NUMBER: u8 = 1;
102 const BOOL: u8 = 2;
103 const STR: u8 = 4;
104 const CHAR: u8 = 8;
105
106 const TEST_PAIRS: &[(&str, &str, u8)] = &[
107 ("u8", "0", NUMBER),
108 ("u16", "0", NUMBER),
109 ("u32", "0", NUMBER),
110 ("u64", "0", NUMBER),
111 ("u128", "0", NUMBER),
112 ("usize", "0", NUMBER),
113 ("usize", "16", NUMBER),
114 ("i8", "0", NUMBER),
115 ("i16", "0", NUMBER),
116 ("i32", "0", NUMBER),
117 ("i64", "0", NUMBER),
118 ("i128", "0", NUMBER),
119 ("isize", "0", NUMBER),
120 ("isize", "16", NUMBER),
121 ("bool", "false", BOOL),
122 ("&str", "\"str\"", STR),
123 ("char", "'c'", CHAR),
124 ];
125
126 #[test]
128 fn inline_const_as_literal_const_fn_call_slice() {
129 TEST_PAIRS.iter().for_each(|(ty, val, _)| {
130 check_assist_not_applicable(
131 inline_const_as_literal,
132 &format!(
133 r#"
134 const fn abc() -> &[{ty}] {{ &[{val}] }}
135 const ABC: &[{ty}] = abc();
136 fn a() {{ A$0BC }}
137 "#
138 ),
139 );
140 });
141 }
142
143 #[test]
144 fn inline_const_as_literal_expr_as_str_lit_not_applicable_const() {
145 check_assist_not_applicable(
146 inline_const_as_literal,
147 r#"
148 const STR$0ING: &str = "Hello, World!";
149
150 fn something() -> &'static str {
151 STRING
152 }
153 "#,
154 );
155 }
156
157 #[test]
158 fn inline_const_as_struct_() {
159 check_assist_not_applicable(
160 inline_const_as_literal,
161 r#"
162 struct A;
163 const STRUKT: A = A;
164
165 fn something() -> A {
166 STRU$0KT
167 }
168 "#,
169 );
170 }
171
172 #[test]
173 fn inline_const_as_enum_() {
174 check_assist_not_applicable(
175 inline_const_as_literal,
176 r#"
177 enum A { A, B, C }
178 const ENUM: A = A::A;
179
180 fn something() -> A {
181 EN$0UM
182 }
183 "#,
184 );
185 }
186
187 #[test]
188 fn inline_const_as_tuple_closure() {
189 check_assist_not_applicable(
190 inline_const_as_literal,
191 r#"
192 const CLOSURE: (&dyn Fn(i32) -> i32) = (&|num| -> i32 { num });
193 fn something() -> (&dyn Fn(i32) -> i32) {
194 STRU$0KT
195 }
196 "#,
197 );
198 }
199
200 #[test]
201 fn inline_const_as_closure_() {
202 check_assist_not_applicable(
203 inline_const_as_literal,
204 r#"
205 const CLOSURE: &dyn Fn(i32) -> i32 = &|num| -> i32 { num };
206 fn something() -> &dyn Fn(i32) -> i32 {
207 STRU$0KT
208 }
209 "#,
210 );
211 }
212
213 #[test]
214 fn inline_const_as_fn_() {
215 check_assist_not_applicable(
216 inline_const_as_literal,
217 r#"
218 struct S(i32);
219 const CON: fn(i32) -> S = S;
220 fn something() {
221 let x = CO$0N;
222 }
223 "#,
224 );
225 }
226
227 #[test]
230 fn inline_const_as_literal_const_expr() {
231 TEST_PAIRS.iter().for_each(|(ty, val, _)| {
232 check_assist(
233 inline_const_as_literal,
234 &format!(
235 r#"
236 const ABC: {ty} = {val};
237 fn a() {{ A$0BC }}
238 "#
239 ),
240 &format!(
241 r#"
242 const ABC: {ty} = {val};
243 fn a() {{ {val} }}
244 "#
245 ),
246 );
247 });
248 }
249
250 #[test]
251 fn inline_const_as_literal_const_block_expr() {
252 TEST_PAIRS.iter().for_each(|(ty, val, _)| {
253 check_assist(
254 inline_const_as_literal,
255 &format!(
256 r#"
257 const ABC: {ty} = {{ {val} }};
258 fn a() {{ A$0BC }}
259 "#
260 ),
261 &format!(
262 r#"
263 const ABC: {ty} = {{ {val} }};
264 fn a() {{ {val} }}
265 "#
266 ),
267 );
268 });
269 }
270
271 #[test]
272 fn inline_const_as_literal_const_block_eval_expr() {
273 TEST_PAIRS.iter().for_each(|(ty, val, _)| {
274 check_assist(
275 inline_const_as_literal,
276 &format!(
277 r#"
278 const ABC: {ty} = {{ true; {val} }};
279 fn a() {{ A$0BC }}
280 "#
281 ),
282 &format!(
283 r#"
284 const ABC: {ty} = {{ true; {val} }};
285 fn a() {{ {val} }}
286 "#
287 ),
288 );
289 });
290 }
291
292 #[test]
293 fn inline_const_as_literal_const_block_eval_block_expr() {
294 TEST_PAIRS.iter().for_each(|(ty, val, _)| {
295 check_assist(
296 inline_const_as_literal,
297 &format!(
298 r#"
299 const ABC: {ty} = {{ true; {{ {val} }} }};
300 fn a() {{ A$0BC }}
301 "#
302 ),
303 &format!(
304 r#"
305 const ABC: {ty} = {{ true; {{ {val} }} }};
306 fn a() {{ {val} }}
307 "#
308 ),
309 );
310 });
311 }
312
313 #[test]
314 fn inline_const_as_literal_const_fn_call_block_nested_builtin() {
315 TEST_PAIRS.iter().for_each(|(ty, val, _)| {
316 check_assist(
317 inline_const_as_literal,
318 &format!(
319 r#"
320 const fn abc() -> {ty} {{ {{ {{ {{ {val} }} }} }} }}
321 const ABC: {ty} = abc();
322 fn a() {{ A$0BC }}
323 "#
324 ),
325 &format!(
326 r#"
327 const fn abc() -> {ty} {{ {{ {{ {{ {val} }} }} }} }}
328 const ABC: {ty} = abc();
329 fn a() {{ {val} }}
330 "#
331 ),
332 );
333 });
334 }
335
336 #[test]
337 fn inline_const_as_literal_const_fn_call_tuple() {
338 TEST_PAIRS.iter().for_each(|(ty, val, _)| {
339 check_assist(
340 inline_const_as_literal,
341 &format!(
342 r#"
343 const fn abc() -> ({ty}, {ty}) {{ ({val}, {val}) }}
344 const ABC: ({ty}, {ty}) = abc();
345 fn a() {{ A$0BC }}
346 "#
347 ),
348 &format!(
349 r#"
350 const fn abc() -> ({ty}, {ty}) {{ ({val}, {val}) }}
351 const ABC: ({ty}, {ty}) = abc();
352 fn a() {{ ({val}, {val}) }}
353 "#
354 ),
355 );
356 });
357 }
358
359 #[test]
360 fn inline_const_as_literal_const_fn_call_builtin() {
361 TEST_PAIRS.iter().for_each(|(ty, val, _)| {
362 check_assist(
363 inline_const_as_literal,
364 &format!(
365 r#"
366 const fn abc() -> {ty} {{ {val} }}
367 const ABC: {ty} = abc();
368 fn a() {{ A$0BC }}
369 "#
370 ),
371 &format!(
372 r#"
373 const fn abc() -> {ty} {{ {val} }}
374 const ABC: {ty} = abc();
375 fn a() {{ {val} }}
376 "#
377 ),
378 );
379 });
380 }
381
382 #[test]
383 fn inline_const_as_literal_scalar_operators() {
384 check_assist(
385 inline_const_as_literal,
386 r#"
387 const ABC: i32 = 1 + 2 + 3;
388 fn a() { A$0BC }
389 "#,
390 r#"
391 const ABC: i32 = 1 + 2 + 3;
392 fn a() { 6 }
393 "#,
394 );
395 }
396 #[test]
397 fn inline_const_as_literal_block_scalar_calculate_expr() {
398 check_assist(
399 inline_const_as_literal,
400 r#"
401 const ABC: i32 = { 1 + 2 + 3 };
402 fn a() { A$0BC }
403 "#,
404 r#"
405 const ABC: i32 = { 1 + 2 + 3 };
406 fn a() { 6 }
407 "#,
408 );
409 }
410
411 #[test]
412 fn inline_const_as_literal_block_scalar_calculate_param_expr() {
413 check_assist(
414 inline_const_as_literal,
415 r#"
416 const ABC: i32 = { (1 + 2 + 3) };
417 fn a() { A$0BC }
418 "#,
419 r#"
420 const ABC: i32 = { (1 + 2 + 3) };
421 fn a() { 6 }
422 "#,
423 );
424 }
425
426 #[test]
427 fn inline_const_as_literal_block_tuple_scalar_calculate_block_expr() {
428 check_assist(
429 inline_const_as_literal,
430 r#"
431 const ABC: (i32, i32) = { (1, { 2 + 3 }) };
432 fn a() { A$0BC }
433 "#,
434 r#"
435 const ABC: (i32, i32) = { (1, { 2 + 3 }) };
436 fn a() { (1, 5) }
437 "#,
438 );
439 }
440
441 #[test]
443 fn inline_const_as_literal_block_slice() {
444 check_assist_not_applicable(
445 inline_const_as_literal,
446 r#"
447 const ABC: &[&[&[&[&[&[i32]]]]]] = { &[&[&[&[&[&[10, 20, 30]]]]]] };
448 fn a() { A$0BC }
449 "#,
450 );
451 }
452
453 #[test]
457 fn inline_const_as_literal_block_tuple() {
458 check_assist(
459 inline_const_as_literal,
460 r#"
461 const ABC: (([i32; 3]), (i32), ((&str, i32), i32), i32) = { (([1, 2, 3]), (10), (("hello", 10), 20), 30) };
462 fn a() { A$0BC }
463 "#,
464 r#"
465 const ABC: (([i32; 3]), (i32), ((&str, i32), i32), i32) = { (([1, 2, 3]), (10), (("hello", 10), 20), 30) };
466 fn a() { ([1, 2, 3], 10, (("hello", 10), 20), 30) }
467 "#,
468 );
469 }
470
471 #[test]
472 fn inline_const_as_literal_block_slice_single() {
473 check_assist(
474 inline_const_as_literal,
475 r#"
476 const ABC: [i32; 1] = { [10] };
477 fn a() { A$0BC }
478 "#,
479 r#"
480 const ABC: [i32; 1] = { [10] };
481 fn a() { [10] }
482 "#,
483 );
484 }
485
486 #[test]
487 fn inline_const_as_literal_block_array() {
488 check_assist(
489 inline_const_as_literal,
490 r#"
491 const ABC: [[[i32; 1]; 1]; 1] = { [[[10]]] };
492 fn a() { A$0BC }
493 "#,
494 r#"
495 const ABC: [[[i32; 1]; 1]; 1] = { [[[10]]] };
496 fn a() { [[[10]]] }
497 "#,
498 );
499 }
500
501 #[test]
502 fn inline_const_as_literal_block_recursive() {
503 check_assist(
504 inline_const_as_literal,
505 r#"
506 const ABC: &str = { { { { "hello" } } } };
507 fn a() { A$0BC }
508 "#,
509 r#"
510 const ABC: &str = { { { { "hello" } } } };
511 fn a() { "hello" }
512 "#,
513 );
514 }
515
516 #[test]
517 fn inline_const_as_literal_expr_as_str_lit() {
518 check_assist(
519 inline_const_as_literal,
520 r#"
521 const STRING: &str = "Hello, World!";
522
523 fn something() -> &'static str {
524 STR$0ING
525 }
526 "#,
527 r#"
528 const STRING: &str = "Hello, World!";
529
530 fn something() -> &'static str {
531 "Hello, World!"
532 }
533 "#,
534 );
535 }
536
537 #[test]
538 fn inline_const_as_literal_eval_const_block_expr_to_str_lit() {
539 check_assist(
540 inline_const_as_literal,
541 r#"
542 const STRING: &str = {
543 let x = 9;
544 if x + 10 == 21 {
545 "Hello, World!"
546 } else {
547 "World, Hello!"
548 }
549 };
550
551 fn something() -> &'static str {
552 STR$0ING
553 }
554 "#,
555 r#"
556 const STRING: &str = {
557 let x = 9;
558 if x + 10 == 21 {
559 "Hello, World!"
560 } else {
561 "World, Hello!"
562 }
563 };
564
565 fn something() -> &'static str {
566 "World, Hello!"
567 }
568 "#,
569 );
570 }
571
572 #[test]
573 fn inline_const_as_literal_eval_const_block_macro_expr_to_str_lit() {
574 check_assist(
575 inline_const_as_literal,
576 r#"
577 macro_rules! co {() => {"World, Hello!"};}
578 const STRING: &str = { co!() };
579
580 fn something() -> &'static str {
581 STR$0ING
582 }
583 "#,
584 r#"
585 macro_rules! co {() => {"World, Hello!"};}
586 const STRING: &str = { co!() };
587
588 fn something() -> &'static str {
589 "World, Hello!"
590 }
591 "#,
592 );
593 }
594
595 #[test]
596 fn inline_const_as_literal_eval_const_match_expr_to_str_lit() {
597 check_assist(
598 inline_const_as_literal,
599 r#"
600 const STRING: &str = match 9 + 10 {
601 0..18 => "Hello, World!",
602 _ => "World, Hello!"
603 };
604
605 fn something() -> &'static str {
606 STR$0ING
607 }
608 "#,
609 r#"
610 const STRING: &str = match 9 + 10 {
611 0..18 => "Hello, World!",
612 _ => "World, Hello!"
613 };
614
615 fn something() -> &'static str {
616 "World, Hello!"
617 }
618 "#,
619 );
620 }
621
622 #[test]
623 fn inline_const_as_literal_eval_const_if_expr_to_str_lit() {
624 check_assist(
625 inline_const_as_literal,
626 r#"
627 const STRING: &str = if 1 + 2 == 4 {
628 "Hello, World!"
629 } else {
630 "World, Hello!"
631 }
632
633 fn something() -> &'static str {
634 STR$0ING
635 }
636 "#,
637 r#"
638 const STRING: &str = if 1 + 2 == 4 {
639 "Hello, World!"
640 } else {
641 "World, Hello!"
642 }
643
644 fn something() -> &'static str {
645 "World, Hello!"
646 }
647 "#,
648 );
649 }
650
651 #[test]
652 fn inline_const_as_literal_eval_const_macro_expr_to_str_lit() {
653 check_assist(
654 inline_const_as_literal,
655 r#"
656 macro_rules! co {() => {"World, Hello!"};}
657 const STRING: &str = co!();
658
659 fn something() -> &'static str {
660 STR$0ING
661 }
662 "#,
663 r#"
664 macro_rules! co {() => {"World, Hello!"};}
665 const STRING: &str = co!();
666
667 fn something() -> &'static str {
668 "World, Hello!"
669 }
670 "#,
671 );
672 }
673
674 #[test]
675 fn inline_const_as_literal_eval_const_call_expr_to_str_lit() {
676 check_assist(
677 inline_const_as_literal,
678 r#"
679 const fn const_call() -> &'static str {"World, Hello!"}
680 const STRING: &str = const_call();
681
682 fn something() -> &'static str {
683 STR$0ING
684 }
685 "#,
686 r#"
687 const fn const_call() -> &'static str {"World, Hello!"}
688 const STRING: &str = const_call();
689
690 fn something() -> &'static str {
691 "World, Hello!"
692 }
693 "#,
694 );
695 }
696
697 #[test]
698 fn inline_const_as_literal_expr_as_str_lit_not_applicable() {
699 check_assist_not_applicable(
700 inline_const_as_literal,
701 r#"
702 const STRING: &str = "Hello, World!";
703
704 fn something() -> &'static str {
705 STRING $0
706 }
707 "#,
708 );
709 }
710}