1
+ use std:: collections:: BTreeSet ;
2
+
1
3
use ast:: make;
2
4
use either:: Either ;
3
5
use hir:: { db:: HirDatabase , PathResolution , Semantics , TypeInfo } ;
@@ -373,8 +375,44 @@ fn inline(
373
375
} )
374
376
}
375
377
}
378
+
379
+ let mut func_let_vars: BTreeSet < String > = BTreeSet :: new ( ) ;
380
+
381
+ // grab all of the local variable declarations in the function
382
+ for stmt in fn_body. statements ( ) {
383
+ if let Some ( let_stmt) = ast:: LetStmt :: cast ( stmt. syntax ( ) . to_owned ( ) ) {
384
+ for has_token in let_stmt. syntax ( ) . children_with_tokens ( ) {
385
+ if let Some ( node) = has_token. as_node ( ) {
386
+ if let Some ( ident_pat) = ast:: IdentPat :: cast ( node. to_owned ( ) ) {
387
+ func_let_vars. insert ( ident_pat. syntax ( ) . text ( ) . to_string ( ) ) ;
388
+ }
389
+ }
390
+ }
391
+ }
392
+ }
393
+
376
394
// Inline parameter expressions or generate `let` statements depending on whether inlining works or not.
377
395
for ( ( pat, param_ty, _) , usages, expr) in izip ! ( params, param_use_nodes, arguments) . rev ( ) {
396
+ // izip confuses RA due to our lack of hygiene info currently losing us type info causing incorrect errors
397
+ let usages: & [ ast:: PathExpr ] = & * usages;
398
+ let expr: & ast:: Expr = expr;
399
+
400
+ let insert_let_stmt = || {
401
+ let ty = sema. type_of_expr ( expr) . filter ( TypeInfo :: has_adjustment) . and ( param_ty. clone ( ) ) ;
402
+ if let Some ( stmt_list) = body. stmt_list ( ) {
403
+ stmt_list. push_front (
404
+ make:: let_stmt ( pat. clone ( ) , ty, Some ( expr. clone ( ) ) ) . clone_for_update ( ) . into ( ) ,
405
+ )
406
+ }
407
+ } ;
408
+
409
+ // check if there is a local var in the function that conflicts with parameter
410
+ // if it does then emit a let statement and continue
411
+ if func_let_vars. contains ( & expr. syntax ( ) . text ( ) . to_string ( ) ) {
412
+ insert_let_stmt ( ) ;
413
+ continue ;
414
+ }
415
+
378
416
let inline_direct = |usage, replacement : & ast:: Expr | {
379
417
if let Some ( field) = path_expr_as_record_field ( usage) {
380
418
cov_mark:: hit!( inline_call_inline_direct_field) ;
@@ -383,9 +421,7 @@ fn inline(
383
421
ted:: replace ( usage. syntax ( ) , & replacement. syntax ( ) . clone_for_update ( ) ) ;
384
422
}
385
423
} ;
386
- // izip confuses RA due to our lack of hygiene info currently losing us type info causing incorrect errors
387
- let usages: & [ ast:: PathExpr ] = & * usages;
388
- let expr: & ast:: Expr = expr;
424
+
389
425
match usages {
390
426
// inline single use closure arguments
391
427
[ usage]
@@ -408,18 +444,11 @@ fn inline(
408
444
}
409
445
// can't inline, emit a let statement
410
446
_ => {
411
- let ty =
412
- sema. type_of_expr ( expr) . filter ( TypeInfo :: has_adjustment) . and ( param_ty. clone ( ) ) ;
413
- if let Some ( stmt_list) = body. stmt_list ( ) {
414
- stmt_list. push_front (
415
- make:: let_stmt ( pat. clone ( ) , ty, Some ( expr. clone ( ) ) )
416
- . clone_for_update ( )
417
- . into ( ) ,
418
- )
419
- }
447
+ insert_let_stmt ( ) ;
420
448
}
421
449
}
422
450
}
451
+
423
452
if let Some ( generic_arg_list) = generic_arg_list. clone ( ) {
424
453
if let Some ( ( target, source) ) = & sema. scope ( node. syntax ( ) ) . zip ( sema. scope ( fn_body. syntax ( ) ) )
425
454
{
@@ -1256,4 +1285,37 @@ impl A {
1256
1285
"# ,
1257
1286
)
1258
1287
}
1288
+
1289
+ #[ test]
1290
+ fn local_variable_shadowing_callers_argument ( ) {
1291
+ check_assist (
1292
+ inline_call,
1293
+ r#"
1294
+ fn foo(bar: u32, baz: u32) -> u32 {
1295
+ let a = 1;
1296
+ bar * baz * a * 6
1297
+ }
1298
+ fn main() {
1299
+ let a = 7;
1300
+ let b = 1;
1301
+ let res = foo$0(a, b);
1302
+ }
1303
+ "# ,
1304
+ r#"
1305
+ fn foo(bar: u32, baz: u32) -> u32 {
1306
+ let a = 1;
1307
+ bar * baz * a * 6
1308
+ }
1309
+ fn main() {
1310
+ let a = 7;
1311
+ let b = 1;
1312
+ let res = {
1313
+ let bar = a;
1314
+ let a = 1;
1315
+ bar * b * a * 6
1316
+ };
1317
+ }
1318
+ "# ,
1319
+ ) ;
1320
+ }
1259
1321
}
0 commit comments