github.com/Johnny2210/revive@v1.0.8-0.20210625134200-febf37ccd0f5/rule/range-val-in-closure.go (about) 1 package rule 2 3 import ( 4 "fmt" 5 "go/ast" 6 7 "github.com/mgechev/revive/lint" 8 ) 9 10 // RangeValInClosureRule lints given else constructs. 11 type RangeValInClosureRule struct{} 12 13 // Apply applies the rule to given file. 14 func (r *RangeValInClosureRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { 15 var failures []lint.Failure 16 17 walker := rangeValInClosure{ 18 onFailure: func(failure lint.Failure) { 19 failures = append(failures, failure) 20 }, 21 } 22 23 ast.Walk(walker, file.AST) 24 25 return failures 26 } 27 28 // Name returns the rule name. 29 func (r *RangeValInClosureRule) Name() string { 30 return "range-val-in-closure" 31 } 32 33 type rangeValInClosure struct { 34 onFailure func(lint.Failure) 35 } 36 37 func (w rangeValInClosure) Visit(node ast.Node) ast.Visitor { 38 39 // Find the variables updated by the loop statement. 40 var vars []*ast.Ident 41 addVar := func(expr ast.Expr) { 42 if id, ok := expr.(*ast.Ident); ok { 43 vars = append(vars, id) 44 } 45 } 46 var body *ast.BlockStmt 47 switch n := node.(type) { 48 case *ast.RangeStmt: 49 body = n.Body 50 addVar(n.Key) 51 addVar(n.Value) 52 case *ast.ForStmt: 53 body = n.Body 54 switch post := n.Post.(type) { 55 case *ast.AssignStmt: 56 // e.g. for p = head; p != nil; p = p.next 57 for _, lhs := range post.Lhs { 58 addVar(lhs) 59 } 60 case *ast.IncDecStmt: 61 // e.g. for i := 0; i < n; i++ 62 addVar(post.X) 63 } 64 } 65 if vars == nil { 66 return w 67 } 68 69 // Inspect a go or defer statement 70 // if it's the last one in the loop body. 71 // (We give up if there are following statements, 72 // because it's hard to prove go isn't followed by wait, 73 // or defer by return.) 74 if len(body.List) == 0 { 75 return w 76 } 77 var last *ast.CallExpr 78 switch s := body.List[len(body.List)-1].(type) { 79 case *ast.GoStmt: 80 last = s.Call 81 case *ast.DeferStmt: 82 last = s.Call 83 default: 84 return w 85 } 86 lit, ok := last.Fun.(*ast.FuncLit) 87 if !ok { 88 return w 89 } 90 if lit.Type == nil { 91 // Not referring to a variable (e.g. struct field name) 92 return w 93 } 94 ast.Inspect(lit.Body, func(n ast.Node) bool { 95 id, ok := n.(*ast.Ident) 96 if !ok || id.Obj == nil { 97 return true 98 } 99 for _, v := range vars { 100 if v.Obj == id.Obj { 101 w.onFailure(lint.Failure{ 102 Confidence: 1, 103 Failure: fmt.Sprintf("loop variable %v captured by func literal", id.Name), 104 Node: n, 105 }) 106 } 107 } 108 return true 109 }) 110 return w 111 }