github.com/Johnny2210/revive@v1.0.8-0.20210625134200-febf37ccd0f5/rule/range-val-address.go (about) 1 package rule 2 3 import ( 4 "fmt" 5 "go/ast" 6 "go/token" 7 8 "github.com/mgechev/revive/lint" 9 ) 10 11 // RangeValAddress lints 12 type RangeValAddress struct{} 13 14 // Apply applies the rule to given file. 15 func (r *RangeValAddress) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { 16 var failures []lint.Failure 17 18 walker := rangeValAddress{ 19 onFailure: func(failure lint.Failure) { 20 failures = append(failures, failure) 21 }, 22 } 23 24 ast.Walk(walker, file.AST) 25 26 return failures 27 } 28 29 // Name returns the rule name. 30 func (r *RangeValAddress) Name() string { 31 return "range-val-address" 32 } 33 34 type rangeValAddress struct { 35 onFailure func(lint.Failure) 36 } 37 38 func (w rangeValAddress) Visit(node ast.Node) ast.Visitor { 39 n, ok := node.(*ast.RangeStmt) 40 if !ok { 41 return w 42 } 43 44 value, ok := n.Value.(*ast.Ident) 45 if !ok { 46 return w 47 } 48 49 ast.Walk(rangeBodyVisitor{ 50 valueID: value.Obj, 51 onFailure: w.onFailure, 52 }, n.Body) 53 54 return w 55 } 56 57 type rangeBodyVisitor struct { 58 valueID *ast.Object 59 onFailure func(lint.Failure) 60 } 61 62 func (bw rangeBodyVisitor) Visit(node ast.Node) ast.Visitor { 63 asgmt, ok := node.(*ast.AssignStmt) 64 if !ok { 65 return bw 66 } 67 68 for _, exp := range asgmt.Lhs { 69 e, ok := exp.(*ast.IndexExpr) 70 if !ok { 71 continue 72 } 73 if bw.isAccessingRangeValueAddress(e.Index) { // e.g. a[&value]... 74 bw.onFailure(bw.newFailure(e.Index)) 75 } 76 } 77 78 for _, exp := range asgmt.Rhs { 79 switch e := exp.(type) { 80 case *ast.UnaryExpr: // e.g. ...&value, ...&value.id 81 if bw.isAccessingRangeValueAddress(e) { 82 bw.onFailure(bw.newFailure(e)) 83 } 84 case *ast.CallExpr: 85 if fun, ok := e.Fun.(*ast.Ident); ok && fun.Name == "append" { // e.g. ...append(arr, &value) 86 for _, v := range e.Args { 87 if bw.isAccessingRangeValueAddress(v) { 88 bw.onFailure(bw.newFailure(e)) 89 } 90 } 91 } 92 } 93 } 94 return bw 95 } 96 97 func (bw rangeBodyVisitor) isAccessingRangeValueAddress(exp ast.Expr) bool { 98 u, ok := exp.(*ast.UnaryExpr) 99 if !ok { 100 return false 101 } 102 103 if u.Op != token.AND { 104 return false 105 } 106 107 v, ok := u.X.(*ast.Ident) 108 if !ok { 109 var s *ast.SelectorExpr 110 s, ok = u.X.(*ast.SelectorExpr) 111 if !ok { 112 return false 113 } 114 v, ok = s.X.(*ast.Ident) 115 } 116 117 return ok && v.Obj == bw.valueID 118 } 119 120 func (bw rangeBodyVisitor) newFailure(node ast.Node) lint.Failure { 121 return lint.Failure{ 122 Node: node, 123 Confidence: 1, 124 Failure: fmt.Sprintf("suspicious assignment of '%s'. range-loop variables always have the same address", bw.valueID.Name), 125 } 126 }