1+ use std:: collections:: BTreeSet ;
2+
13use ast:: make;
24use either:: Either ;
35use hir:: { db:: HirDatabase , PathResolution , Semantics , TypeInfo } ;
@@ -373,8 +375,44 @@ fn inline(
373375 } )
374376 }
375377 }
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+
376394 // Inline parameter expressions or generate `let` statements depending on whether inlining works or not.
377395 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+
378416 let inline_direct = |usage, replacement : & ast:: Expr | {
379417 if let Some ( field) = path_expr_as_record_field ( usage) {
380418 cov_mark:: hit!( inline_call_inline_direct_field) ;
@@ -383,9 +421,7 @@ fn inline(
383421 ted:: replace ( usage. syntax ( ) , & replacement. syntax ( ) . clone_for_update ( ) ) ;
384422 }
385423 } ;
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+
389425 match usages {
390426 // inline single use closure arguments
391427 [ usage]
@@ -408,18 +444,11 @@ fn inline(
408444 }
409445 // can't inline, emit a let statement
410446 _ => {
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 ( ) ;
420448 }
421449 }
422450 }
451+
423452 if let Some ( generic_arg_list) = generic_arg_list. clone ( ) {
424453 if let Some ( ( target, source) ) = & sema. scope ( node. syntax ( ) ) . zip ( sema. scope ( fn_body. syntax ( ) ) )
425454 {
@@ -1256,4 +1285,37 @@ impl A {
12561285"# ,
12571286 )
12581287 }
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+ }
12591321}
0 commit comments