@@ -30,66 +30,50 @@ import (
3030// is declared implicitly before executing the post statement and initialized to the
3131// value of the previous iteration's variable at that moment.")
3232func forvar (pass * analysis.Pass ) {
33- info := pass .TypesInfo
34-
3533 inspect := pass .ResultOf [inspect .Analyzer ].(* inspector.Inspector )
36- for curFile := range filesUsing (inspect , info , "go1.22" ) {
34+ for curFile := range filesUsing (inspect , pass .TypesInfo , "go1.22" ) {
35+ astFile := curFile .Node ().(* ast.File )
3736 for curLoop := range curFile .Preorder ((* ast .RangeStmt )(nil )) {
38- // in a range loop. Is the first statement var := var?
39- // if so, is var one of the range vars, and is it defined
40- // in the for statement?
41- // If so, decide how much to delete.
4237 loop := curLoop .Node ().(* ast.RangeStmt )
4338 if loop .Tok != token .DEFINE {
4439 continue
4540 }
46- v , stmt := loopVarRedecl (loop .Body )
47- if v == nil {
48- continue // index is not redeclared
49- }
50- if (loop .Key == nil || ! equalSyntax (loop .Key , v )) &&
51- (loop .Value == nil || ! equalSyntax (loop .Value , v )) {
52- continue
41+ isLoopVarRedecl := func (assign * ast.AssignStmt ) bool {
42+ for i , lhs := range assign .Lhs {
43+ if ! (equalSyntax (lhs , assign .Rhs [i ]) &&
44+ (equalSyntax (lhs , loop .Key ) || equalSyntax (lhs , loop .Value ))) {
45+ return false
46+ }
47+ }
48+ return true
5349 }
54- astFile := curFile .Node ().(* ast.File )
55- edits := analysisinternal .DeleteStmt (pass .Fset , astFile , stmt , bug .Reportf )
56- if len (edits ) == 0 {
57- bug .Reportf ("forvar failed to delete statement" )
58- continue
59- }
60- remove := edits [0 ]
61- diag := analysis.Diagnostic {
62- Pos : remove .Pos ,
63- End : remove .End ,
64- Category : "forvar" ,
65- Message : "copying variable is unneeded" ,
66- SuggestedFixes : []analysis.SuggestedFix {{
67- Message : "Remove unneeded redeclaration" ,
68- TextEdits : []analysis.TextEdit {remove },
69- }},
50+ // Have: for k, v := range x { stmts }
51+ //
52+ // Delete the prefix of stmts that are
53+ // of the form k := k; v := v; k, v := k, v; v, k := v, k.
54+ for _ , stmt := range loop .Body .List {
55+ if assign , ok := stmt .(* ast.AssignStmt ); ok &&
56+ assign .Tok == token .DEFINE &&
57+ len (assign .Lhs ) == len (assign .Rhs ) &&
58+ isLoopVarRedecl (assign ) {
59+
60+ edits := analysisinternal .DeleteStmt (pass .Fset , astFile , stmt , bug .Reportf )
61+ if len (edits ) > 0 {
62+ pass .Report (analysis.Diagnostic {
63+ Pos : stmt .Pos (),
64+ End : stmt .End (),
65+ Category : "forvar" ,
66+ Message : "copying variable is unneeded" ,
67+ SuggestedFixes : []analysis.SuggestedFix {{
68+ Message : "Remove unneeded redeclaration" ,
69+ TextEdits : edits ,
70+ }},
71+ })
72+ }
73+ } else {
74+ break // stop at first other statement
75+ }
7076 }
71- pass .Report (diag )
7277 }
7378 }
7479}
75-
76- // if the first statement is var := var, return var and the stmt
77- func loopVarRedecl (body * ast.BlockStmt ) (* ast.Ident , * ast.AssignStmt ) {
78- if len (body .List ) < 1 {
79- return nil , nil
80- }
81- stmt , ok := body .List [0 ].(* ast.AssignStmt )
82- if ! ok || ! isSimpleAssign (stmt ) || stmt .Tok != token .DEFINE {
83- return nil , nil
84- }
85- if _ , ok := stmt .Lhs [0 ].(* ast.Ident ); ! ok {
86- return nil , nil
87- }
88- if _ , ok := stmt .Rhs [0 ].(* ast.Ident ); ! ok {
89- return nil , nil
90- }
91- if stmt .Lhs [0 ].(* ast.Ident ).Name == stmt .Rhs [0 ].(* ast.Ident ).Name {
92- return stmt .Lhs [0 ].(* ast.Ident ), stmt
93- }
94- return nil , nil
95- }
0 commit comments