summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/mgechev/revive/rule/range-val-address.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/mgechev/revive/rule/range-val-address.go')
-rw-r--r--vendor/github.com/mgechev/revive/rule/range-val-address.go113
1 files changed, 113 insertions, 0 deletions
diff --git a/vendor/github.com/mgechev/revive/rule/range-val-address.go b/vendor/github.com/mgechev/revive/rule/range-val-address.go
new file mode 100644
index 0000000000..18554825a8
--- /dev/null
+++ b/vendor/github.com/mgechev/revive/rule/range-val-address.go
@@ -0,0 +1,113 @@
+package rule
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+
+ "github.com/mgechev/revive/lint"
+)
+
+// RangeValAddress lints
+type RangeValAddress struct{}
+
+// Apply applies the rule to given file.
+func (r *RangeValAddress) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
+ var failures []lint.Failure
+
+ walker := rangeValAddress{
+ onFailure: func(failure lint.Failure) {
+ failures = append(failures, failure)
+ },
+ }
+
+ ast.Walk(walker, file.AST)
+
+ return failures
+}
+
+// Name returns the rule name.
+func (r *RangeValAddress) Name() string {
+ return "range-val-address"
+}
+
+type rangeValAddress struct {
+ onFailure func(lint.Failure)
+}
+
+func (w rangeValAddress) Visit(node ast.Node) ast.Visitor {
+ n, ok := node.(*ast.RangeStmt)
+ if !ok {
+ return w
+ }
+
+ value, ok := n.Value.(*ast.Ident)
+ if !ok {
+ return w
+ }
+
+ ast.Walk(rangeBodyVisitor{
+ valueID: value.Obj,
+ onFailure: w.onFailure,
+ }, n.Body)
+
+ return w
+}
+
+type rangeBodyVisitor struct {
+ valueID *ast.Object
+ onFailure func(lint.Failure)
+}
+
+func (bw rangeBodyVisitor) Visit(node ast.Node) ast.Visitor {
+ asgmt, ok := node.(*ast.AssignStmt)
+ if !ok {
+ return bw
+ }
+
+ for _, exp := range asgmt.Lhs {
+ e, ok := exp.(*ast.IndexExpr)
+ if !ok {
+ continue
+ }
+ if bw.isAccessingRangeValueAddress(e.Index) { // e.g. a[&value]...
+ bw.onFailure(bw.newFailure(e.Index))
+ }
+ }
+
+ for _, exp := range asgmt.Rhs {
+ switch e := exp.(type) {
+ case *ast.UnaryExpr: // e.g. ...&value
+ if bw.isAccessingRangeValueAddress(e) {
+ bw.onFailure(bw.newFailure(e))
+ }
+ case *ast.CallExpr:
+ if fun, ok := e.Fun.(*ast.Ident); ok && fun.Name == "append" { // e.g. ...append(arr, &value)
+ for _, v := range e.Args {
+ if bw.isAccessingRangeValueAddress(v) {
+ bw.onFailure(bw.newFailure(e))
+ }
+ }
+ }
+ }
+ }
+ return bw
+}
+
+func (bw rangeBodyVisitor) isAccessingRangeValueAddress(exp ast.Expr) bool {
+ u, ok := exp.(*ast.UnaryExpr)
+ if !ok {
+ return false
+ }
+
+ v, ok := u.X.(*ast.Ident)
+ return ok && u.Op == token.AND && v.Obj == bw.valueID
+}
+
+func (bw rangeBodyVisitor) newFailure(node ast.Node) lint.Failure {
+ return lint.Failure{
+ Node: node,
+ Confidence: 1,
+ Failure: fmt.Sprintf("suspicious assignment of '%s'. range-loop variables always have the same address", bw.valueID.Name),
+ }
+}