github.com/songshiyun/revive@v1.1.5-0.20220323112655-f8433a19b3c5/rule/range-val-in-closure.go (about) 1 package rule 2 3 import ( 4 "fmt" 5 "go/ast" 6 7 "github.com/songshiyun/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 // Find the variables updated by the loop statement. 39 var vars []*ast.Ident 40 addVar := func(expr ast.Expr) { 41 if id, ok := expr.(*ast.Ident); ok { 42 vars = append(vars, id) 43 } 44 } 45 var body *ast.BlockStmt 46 switch n := node.(type) { 47 case *ast.RangeStmt: 48 body = n.Body 49 addVar(n.Key) 50 addVar(n.Value) 51 case *ast.ForStmt: 52 body = n.Body 53 switch post := n.Post.(type) { 54 case *ast.AssignStmt: 55 // e.g. for p = head; p != nil; p = p.next 56 for _, lhs := range post.Lhs { 57 addVar(lhs) 58 } 59 case *ast.IncDecStmt: 60 // e.g. for i := 0; i < n; i++ 61 addVar(post.X) 62 } 63 } 64 if vars == nil { 65 return w 66 } 67 68 // Inspect a go or defer statement 69 // if it's the last one in the loop body. 70 // (We give up if there are following statements, 71 // because it's hard to prove go isn't followed by wait, 72 // or defer by return.) 73 if len(body.List) == 0 { 74 return w 75 } 76 var last *ast.CallExpr 77 switch s := body.List[len(body.List)-1].(type) { 78 case *ast.GoStmt: 79 last = s.Call 80 case *ast.DeferStmt: 81 last = s.Call 82 default: 83 return w 84 } 85 lit, ok := last.Fun.(*ast.FuncLit) 86 if !ok { 87 return w 88 } 89 90 if lit.Type == nil { 91 // Not referring to a variable (e.g. struct field name) 92 return w 93 } 94 95 var inspector func(n ast.Node) bool 96 inspector = func(n ast.Node) bool { 97 kv, ok := n.(*ast.KeyValueExpr) 98 if ok { 99 // do not check identifiers acting as key in key-value expressions (see issue #637) 100 ast.Inspect(kv.Value, inspector) 101 return false 102 } 103 id, ok := n.(*ast.Ident) 104 if !ok || id.Obj == nil { 105 return true 106 } 107 108 for _, v := range vars { 109 if v.Obj == id.Obj { 110 w.onFailure(lint.Failure{ 111 Confidence: 1, 112 Failure: fmt.Sprintf("loop variable %v captured by func literal", id.Name), 113 Node: n, 114 }) 115 } 116 } 117 return true 118 } 119 ast.Inspect(lit.Body, inspector) 120 return w 121 }