@@ -42,11 +42,27 @@ pub(crate) fn inline_const_as_literal(
4242 // FIXME: Add support to handle type aliases for builtin scalar types.
4343 validate_type_recursively ( ctx, Some ( & konst_ty) , false , fuel) ?;
4444
45- let value = konst
45+ let mut value = konst
4646 . eval ( ctx. sema . db )
4747 . ok ( ) ?
4848 . render ( ctx. sema . db , konst. krate ( ctx. sema . db ) . to_display_target ( ctx. sema . db ) ) ;
4949
50+ // Preserve the numeric type by appending a suffix when the const has a known
51+ // scalar numeric type. Without this, inlining `const N: i32 = 24;` followed by
52+ // `N.wrapping_add(7i32)` would yield `24.wrapping_add(7i32)`, which fails to
53+ // type-check (E0689) because `24` is left as an ambiguous `{integer}`.
54+ //
55+ // Only attach the suffix when the rendered value ends in a digit. Special float
56+ // values such as `inf` or `NaN` are skipped, since `inff32` is not a valid
57+ // literal; these cases were already producing non-literal output before this
58+ // change.
59+ if let Some ( suffix) = numeric_type_suffix ( & konst_ty)
60+ && value. as_bytes ( ) . last ( ) . is_some_and ( u8:: is_ascii_digit)
61+ && !value. ends_with ( suffix)
62+ {
63+ value. push_str ( suffix) ;
64+ }
65+
5066 let id = AssistId :: refactor_inline ( "inline_const_as_literal" ) ;
5167
5268 let label = "Inline const as literal" . to_owned ( ) ;
@@ -59,6 +75,36 @@ pub(crate) fn inline_const_as_literal(
5975 None
6076}
6177
78+ /// Returns the literal-suffix string (e.g. `"i32"`, `"u64"`, `"f32"`) for a scalar
79+ /// numeric type, otherwise `None`.
80+ fn numeric_type_suffix ( ty : & hir:: Type < ' _ > ) -> Option < & ' static str > {
81+ let builtin = ty. as_builtin ( ) ?;
82+ if !( builtin. is_int ( ) || builtin. is_uint ( ) || builtin. is_float ( ) ) {
83+ return None ;
84+ }
85+ // The builtin type's name matches its literal suffix for every scalar numeric
86+ // type (`i32`, `u64`, `f32`, ...), so look the suffix up by name.
87+ Some ( match builtin. name ( ) . as_str ( ) {
88+ "i8" => "i8" ,
89+ "i16" => "i16" ,
90+ "i32" => "i32" ,
91+ "i64" => "i64" ,
92+ "i128" => "i128" ,
93+ "isize" => "isize" ,
94+ "u8" => "u8" ,
95+ "u16" => "u16" ,
96+ "u32" => "u32" ,
97+ "u64" => "u64" ,
98+ "u128" => "u128" ,
99+ "usize" => "usize" ,
100+ "f16" => "f16" ,
101+ "f32" => "f32" ,
102+ "f64" => "f64" ,
103+ "f128" => "f128" ,
104+ _ => return None ,
105+ } )
106+ }
107+
62108fn validate_type_recursively (
63109 ctx : & AssistContext < ' _ , ' _ > ,
64110 ty_hir : Option < & hir:: Type < ' _ > > ,
@@ -126,6 +172,13 @@ mod tests {
126172 ( "char" , "'c'" , CHAR ) ,
127173 ] ;
128174
175+ /// Renders the literal that the assist is expected to inline. For scalar numeric
176+ /// types we now annotate the literal with its type suffix to preserve type info
177+ /// across the inlining (see `numeric_type_suffix`).
178+ fn expected_inlined ( ty : & str , val : & str , kind : u8 ) -> String {
179+ if kind == NUMBER { format ! ( "{val}{ty}" ) } else { val. to_string ( ) }
180+ }
181+
129182 // -----------Not supported-----------
130183 #[ test]
131184 fn inline_const_as_literal_const_fn_call_slice ( ) {
@@ -231,7 +284,8 @@ mod tests {
231284
232285 #[ test]
233286 fn inline_const_as_literal_const_expr ( ) {
234- TEST_PAIRS . iter ( ) . for_each ( |( ty, val, _) | {
287+ TEST_PAIRS . iter ( ) . for_each ( |( ty, val, kind) | {
288+ let out = expected_inlined ( ty, val, * kind) ;
235289 check_assist (
236290 inline_const_as_literal,
237291 & format ! (
@@ -243,7 +297,7 @@ mod tests {
243297 & format ! (
244298 r#"
245299 const ABC: {ty} = {val};
246- fn a() {{ {val } }}
300+ fn a() {{ {out } }}
247301 "#
248302 ) ,
249303 ) ;
@@ -252,7 +306,8 @@ mod tests {
252306
253307 #[ test]
254308 fn inline_const_as_literal_const_block_expr ( ) {
255- TEST_PAIRS . iter ( ) . for_each ( |( ty, val, _) | {
309+ TEST_PAIRS . iter ( ) . for_each ( |( ty, val, kind) | {
310+ let out = expected_inlined ( ty, val, * kind) ;
256311 check_assist (
257312 inline_const_as_literal,
258313 & format ! (
@@ -264,7 +319,7 @@ mod tests {
264319 & format ! (
265320 r#"
266321 const ABC: {ty} = {{ {val} }};
267- fn a() {{ {val } }}
322+ fn a() {{ {out } }}
268323 "#
269324 ) ,
270325 ) ;
@@ -273,7 +328,8 @@ mod tests {
273328
274329 #[ test]
275330 fn inline_const_as_literal_const_block_eval_expr ( ) {
276- TEST_PAIRS . iter ( ) . for_each ( |( ty, val, _) | {
331+ TEST_PAIRS . iter ( ) . for_each ( |( ty, val, kind) | {
332+ let out = expected_inlined ( ty, val, * kind) ;
277333 check_assist (
278334 inline_const_as_literal,
279335 & format ! (
@@ -285,7 +341,7 @@ mod tests {
285341 & format ! (
286342 r#"
287343 const ABC: {ty} = {{ true; {val} }};
288- fn a() {{ {val } }}
344+ fn a() {{ {out } }}
289345 "#
290346 ) ,
291347 ) ;
@@ -294,7 +350,8 @@ mod tests {
294350
295351 #[ test]
296352 fn inline_const_as_literal_const_block_eval_block_expr ( ) {
297- TEST_PAIRS . iter ( ) . for_each ( |( ty, val, _) | {
353+ TEST_PAIRS . iter ( ) . for_each ( |( ty, val, kind) | {
354+ let out = expected_inlined ( ty, val, * kind) ;
298355 check_assist (
299356 inline_const_as_literal,
300357 & format ! (
@@ -306,7 +363,7 @@ mod tests {
306363 & format ! (
307364 r#"
308365 const ABC: {ty} = {{ true; {{ {val} }} }};
309- fn a() {{ {val } }}
366+ fn a() {{ {out } }}
310367 "#
311368 ) ,
312369 ) ;
@@ -315,7 +372,8 @@ mod tests {
315372
316373 #[ test]
317374 fn inline_const_as_literal_const_fn_call_block_nested_builtin ( ) {
318- TEST_PAIRS . iter ( ) . for_each ( |( ty, val, _) | {
375+ TEST_PAIRS . iter ( ) . for_each ( |( ty, val, kind) | {
376+ let out = expected_inlined ( ty, val, * kind) ;
319377 check_assist (
320378 inline_const_as_literal,
321379 & format ! (
@@ -329,7 +387,7 @@ mod tests {
329387 r#"
330388 const fn abc() -> {ty} {{ {{ {{ {{ {val} }} }} }} }}
331389 const ABC: {ty} = abc();
332- fn a() {{ {val } }}
390+ fn a() {{ {out } }}
333391 "#
334392 ) ,
335393 ) ;
@@ -361,7 +419,8 @@ mod tests {
361419
362420 #[ test]
363421 fn inline_const_as_literal_const_fn_call_builtin ( ) {
364- TEST_PAIRS . iter ( ) . for_each ( |( ty, val, _) | {
422+ TEST_PAIRS . iter ( ) . for_each ( |( ty, val, kind) | {
423+ let out = expected_inlined ( ty, val, * kind) ;
365424 check_assist (
366425 inline_const_as_literal,
367426 & format ! (
@@ -375,7 +434,7 @@ mod tests {
375434 r#"
376435 const fn abc() -> {ty} {{ {val} }}
377436 const ABC: {ty} = abc();
378- fn a() {{ {val } }}
437+ fn a() {{ {out } }}
379438 "#
380439 ) ,
381440 ) ;
@@ -392,7 +451,7 @@ mod tests {
392451 "# ,
393452 r#"
394453 const ABC: i32 = 1 + 2 + 3;
395- fn a() { 6 }
454+ fn a() { 6i32 }
396455 "# ,
397456 ) ;
398457 }
@@ -406,7 +465,7 @@ mod tests {
406465 "# ,
407466 r#"
408467 const ABC: i32 = { 1 + 2 + 3 };
409- fn a() { 6 }
468+ fn a() { 6i32 }
410469 "# ,
411470 ) ;
412471 }
@@ -421,7 +480,7 @@ mod tests {
421480 "# ,
422481 r#"
423482 const ABC: i32 = { (1 + 2 + 3) };
424- fn a() { 6 }
483+ fn a() { 6i32 }
425484 "# ,
426485 ) ;
427486 }
@@ -710,4 +769,37 @@ mod tests {
710769 "# ,
711770 ) ;
712771 }
772+
773+ // Regression test for #22051: the inlined numeric literal must carry the
774+ // const's type suffix, otherwise method calls on the receiver fail to
775+ // type-check (E0689) because the literal is left as `{integer}`.
776+ #[ test]
777+ fn inline_const_as_literal_preserves_int_suffix_on_method_receiver ( ) {
778+ check_assist (
779+ inline_const_as_literal,
780+ r#"
781+ const BASE: i32 = 24;
782+ fn main() { let _ = BAS$0E.wrapping_add(7i32); }
783+ "# ,
784+ r#"
785+ const BASE: i32 = 24;
786+ fn main() { let _ = 24i32.wrapping_add(7i32); }
787+ "# ,
788+ ) ;
789+ }
790+
791+ #[ test]
792+ fn inline_const_as_literal_preserves_float_suffix ( ) {
793+ check_assist (
794+ inline_const_as_literal,
795+ r#"
796+ const BASE: f32 = 1.5;
797+ fn main() { let _ = BAS$0E; }
798+ "# ,
799+ r#"
800+ const BASE: f32 = 1.5;
801+ fn main() { let _ = 1.5f32; }
802+ "# ,
803+ ) ;
804+ }
713805}
0 commit comments