@@ -9,14 +9,14 @@ use hir::{
99use ide_db:: {
1010 FxIndexSet , RootDatabase ,
1111 assists:: GroupLabel ,
12- defs:: { Definition , NameRefClass } ,
12+ defs:: Definition ,
1313 famous_defs:: FamousDefs ,
1414 helpers:: mod_path_to_ast,
1515 imports:: insert_use:: { ImportScope , insert_use} ,
1616 search:: { FileReference , ReferenceCategory , SearchScope } ,
1717 source_change:: SourceChangeBuilder ,
1818 syntax_helpers:: node_ext:: {
19- for_each_tail_expr, preorder_expr, walk_expr , walk_pat, walk_patterns_in_expr,
19+ for_each_tail_expr, preorder_expr, walk_pat, walk_patterns_in_expr,
2020 } ,
2121} ;
2222use itertools:: Itertools ;
@@ -687,29 +687,6 @@ impl FunctionBody {
687687 }
688688 }
689689
690- fn walk_expr ( & self , cb : & mut dyn FnMut ( ast:: Expr ) ) {
691- match self {
692- FunctionBody :: Expr ( expr) => walk_expr ( expr, cb) ,
693- FunctionBody :: Span { parent, text_range, .. } => {
694- parent
695- . statements ( )
696- . filter ( |stmt| text_range. contains_range ( stmt. syntax ( ) . text_range ( ) ) )
697- . filter_map ( |stmt| match stmt {
698- ast:: Stmt :: ExprStmt ( expr_stmt) => expr_stmt. expr ( ) ,
699- ast:: Stmt :: Item ( _) => None ,
700- ast:: Stmt :: LetStmt ( stmt) => stmt. initializer ( ) ,
701- } )
702- . for_each ( |expr| walk_expr ( & expr, cb) ) ;
703- if let Some ( expr) = parent
704- . tail_expr ( )
705- . filter ( |it| text_range. contains_range ( it. syntax ( ) . text_range ( ) ) )
706- {
707- walk_expr ( & expr, cb) ;
708- }
709- }
710- }
711- }
712-
713690 fn preorder_expr ( & self , cb : & mut dyn FnMut ( WalkEvent < ast:: Expr > ) -> bool ) {
714691 match self {
715692 FunctionBody :: Expr ( expr) => preorder_expr ( expr, cb) ,
@@ -718,10 +695,24 @@ impl FunctionBody {
718695 . statements ( )
719696 . filter ( |stmt| text_range. contains_range ( stmt. syntax ( ) . text_range ( ) ) )
720697 . filter_map ( |stmt| match stmt {
721- ast:: Stmt :: ExprStmt ( expr_stmt) => expr_stmt. expr ( ) ,
698+ ast:: Stmt :: ExprStmt ( expr_stmt) => expr_stmt. expr ( ) . map ( |e| vec ! [ e ] ) ,
722699 ast:: Stmt :: Item ( _) => None ,
723- ast:: Stmt :: LetStmt ( stmt) => stmt. initializer ( ) ,
700+ ast:: Stmt :: LetStmt ( stmt) => {
701+ let init = stmt. initializer ( ) ;
702+ let let_else = stmt
703+ . let_else ( )
704+ . and_then ( |le| le. block_expr ( ) )
705+ . map ( ast:: Expr :: BlockExpr ) ;
706+
707+ match ( init, let_else) {
708+ ( Some ( i) , Some ( le) ) => Some ( vec ! [ i, le] ) ,
709+ ( Some ( i) , _) => Some ( vec ! [ i] ) ,
710+ ( _, Some ( le) ) => Some ( vec ! [ le] ) ,
711+ _ => None ,
712+ }
713+ }
724714 } )
715+ . flatten ( )
725716 . for_each ( |expr| preorder_expr ( & expr, cb) ) ;
726717 if let Some ( expr) = parent
727718 . tail_expr ( )
@@ -799,22 +790,14 @@ impl FunctionBody {
799790 let mut self_param = None ;
800791 let mut res = FxIndexSet :: default ( ) ;
801792
802- fn local_from_name_ref (
803- sema : & Semantics < ' _ , RootDatabase > ,
804- name_ref : ast:: NameRef ,
805- ) -> Option < hir:: Local > {
806- match NameRefClass :: classify ( sema, & name_ref) {
807- Some (
808- NameRefClass :: Definition ( Definition :: Local ( local_ref) , _)
809- | NameRefClass :: FieldShorthand { local_ref, field_ref : _, adt_subst : _ } ,
810- ) => Some ( local_ref) ,
811- _ => None ,
812- }
813- }
793+ let ( text_range, element) = match self {
794+ FunctionBody :: Expr ( expr) => ( expr. syntax ( ) . text_range ( ) , Either :: Left ( expr) ) ,
795+ FunctionBody :: Span { parent, text_range, .. } => ( * text_range, Either :: Right ( parent) ) ,
796+ } ;
814797
815798 let mut add_name_if_local = |local_ref : Local | {
816- let InFile { file_id, value } = local_ref. primary_source ( sema. db ) . source ;
817799 // locals defined inside macros are not relevant to us
800+ let InFile { file_id, value } = local_ref. primary_source ( sema. db ) . source ;
818801 if !file_id. is_macro ( ) {
819802 match value {
820803 Either :: Right ( it) => {
@@ -826,59 +809,11 @@ impl FunctionBody {
826809 }
827810 }
828811 } ;
829- self . walk_expr ( & mut |expr| match expr {
830- ast:: Expr :: PathExpr ( path_expr) => {
831- if let Some ( local) = path_expr
832- . path ( )
833- . and_then ( |it| it. as_single_name_ref ( ) )
834- . and_then ( |name_ref| local_from_name_ref ( sema, name_ref) )
835- {
836- add_name_if_local ( local) ;
837- }
838- }
839- ast:: Expr :: ClosureExpr ( closure_expr) => {
840- if let Some ( body) = closure_expr. body ( ) {
841- body. syntax ( )
842- . descendants ( )
843- . filter_map ( ast:: NameRef :: cast)
844- . filter_map ( |name_ref| local_from_name_ref ( sema, name_ref) )
845- . for_each ( & mut add_name_if_local) ;
846- }
847- }
848- ast:: Expr :: MacroExpr ( expr) => {
849- if let Some ( tt) = expr. macro_call ( ) . and_then ( |call| call. token_tree ( ) ) {
850- tt. syntax ( )
851- . descendants_with_tokens ( )
852- . filter_map ( SyntaxElement :: into_token)
853- . filter ( |it| {
854- matches ! ( it. kind( ) , SyntaxKind :: STRING | SyntaxKind :: IDENT | T ![ self ] )
855- } )
856- . for_each ( |t| {
857- if ast:: String :: can_cast ( t. kind ( ) ) {
858- if let Some ( parts) =
859- ast:: String :: cast ( t) . and_then ( |s| sema. as_format_args_parts ( & s) )
860- {
861- parts
862- . into_iter ( )
863- . filter_map ( |( _, value) | value. and_then ( |it| it. left ( ) ) )
864- . filter_map ( |path| match path {
865- PathResolution :: Local ( local) => Some ( local) ,
866- _ => None ,
867- } )
868- . for_each ( & mut add_name_if_local) ;
869- }
870- } else {
871- sema. descend_into_macros_exact ( t)
872- . into_iter ( )
873- . filter_map ( |t| t. parent ( ) . and_then ( ast:: NameRef :: cast) )
874- . filter_map ( |name_ref| local_from_name_ref ( sema, name_ref) )
875- . for_each ( & mut add_name_if_local) ;
876- }
877- } ) ;
878- }
879- }
880- _ => ( ) ,
881- } ) ;
812+
813+ if let Some ( locals) = sema. locals_used ( element, text_range) {
814+ locals. into_iter ( ) . for_each ( & mut add_name_if_local) ;
815+ }
816+
882817 ( res, self_param)
883818 }
884819
@@ -6291,6 +6226,90 @@ fn foo() {
62916226
62926227fn $0fun_name(v: i32) {
62936228 print!("{v:?}{}", v == 123);
6229+ }"# ,
6230+ ) ;
6231+ }
6232+
6233+ #[ test]
6234+ fn no_parameter_for_variable_used_only_let_else ( ) {
6235+ check_assist (
6236+ extract_function,
6237+ r#"
6238+ fn foo() -> u32 {
6239+ let x = 5;
6240+
6241+ $0let Some(y) = Some(1) else {
6242+ return x * 2;
6243+ };$0
6244+
6245+ y
6246+ }"# ,
6247+ r#"
6248+ fn foo() -> u32 {
6249+ let x = 5;
6250+
6251+ let y = match fun_name(x) {
6252+ Ok(value) => value,
6253+ Err(value) => return value,
6254+ };
6255+
6256+ y
6257+ }
6258+
6259+ fn $0fun_name(x: u32) -> Result<_, u32> {
6260+ let Some(y) = Some(1) else {
6261+ return Err(x * 2);
6262+ };
6263+ Ok(y)
6264+ }"# ,
6265+ ) ;
6266+ }
6267+
6268+ #[ test]
6269+ fn deeply_nested_macros ( ) {
6270+ check_assist (
6271+ extract_function,
6272+ r#"
6273+ macro_rules! m {
6274+ ($val:ident) => { $val };
6275+ }
6276+
6277+ macro_rules! n {
6278+ ($v1:ident, $v2:ident) => { m!($v1) + $v2 };
6279+ }
6280+
6281+ macro_rules! o {
6282+ ($v1:ident, $v2:ident, $v3:ident) => { n!($v1, $v2) + $v3 };
6283+ }
6284+
6285+ fn foo() -> u32 {
6286+ let v1 = 1;
6287+ let v2 = 2;
6288+ $0let v3 = 3;
6289+ o!(v1, v2, v3)$0
6290+ }"# ,
6291+ r#"
6292+ macro_rules! m {
6293+ ($val:ident) => { $val };
6294+ }
6295+
6296+ macro_rules! n {
6297+ ($v1:ident, $v2:ident) => { m!($v1) + $v2 };
6298+ }
6299+
6300+ macro_rules! o {
6301+ ($v1:ident, $v2:ident, $v3:ident) => { n!($v1, $v2) + $v3 };
6302+ }
6303+
6304+ fn foo() -> u32 {
6305+ let v1 = 1;
6306+ let v2 = 2;
6307+ fun_name(v1, v2)
6308+ }
6309+
6310+ fn $0fun_name(v1: u32, v2: u32) -> u32 {
6311+ let v3 = 3;
6312+ o!(v1, v2, v3)
62946313}"# ,
62956314 ) ;
62966315 }
0 commit comments