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