@@ -30,66 +30,50 @@ import (
30
30
// is declared implicitly before executing the post statement and initialized to the
31
31
// value of the previous iteration's variable at that moment.")
32
32
func forvar (pass * analysis.Pass ) {
33
- info := pass .TypesInfo
34
-
35
33
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 )
37
36
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.
42
37
loop := curLoop .Node ().(* ast.RangeStmt )
43
38
if loop .Tok != token .DEFINE {
44
39
continue
45
40
}
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
53
49
}
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
+ }
70
76
}
71
- pass .Report (diag )
72
77
}
73
78
}
74
79
}
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