Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / go / analysis / passes / bools / bools.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201105173854-bc9fc8d8c4bc/go/analysis/passes/bools/bools.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201105173854-bc9fc8d8c4bc/go/analysis/passes/bools/bools.go
new file mode 100644 (file)
index 0000000..5ae47d8
--- /dev/null
@@ -0,0 +1,221 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package bools defines an Analyzer that detects common mistakes
+// involving boolean operators.
+package bools
+
+import (
+       "go/ast"
+       "go/token"
+       "go/types"
+
+       "golang.org/x/tools/go/analysis"
+       "golang.org/x/tools/go/analysis/passes/inspect"
+       "golang.org/x/tools/go/analysis/passes/internal/analysisutil"
+       "golang.org/x/tools/go/ast/inspector"
+)
+
+const Doc = "check for common mistakes involving boolean operators"
+
+var Analyzer = &analysis.Analyzer{
+       Name:     "bools",
+       Doc:      Doc,
+       Requires: []*analysis.Analyzer{inspect.Analyzer},
+       Run:      run,
+}
+
+func run(pass *analysis.Pass) (interface{}, error) {
+       inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
+
+       nodeFilter := []ast.Node{
+               (*ast.BinaryExpr)(nil),
+       }
+       seen := make(map[*ast.BinaryExpr]bool)
+       inspect.Preorder(nodeFilter, func(n ast.Node) {
+               e := n.(*ast.BinaryExpr)
+               if seen[e] {
+                       // Already processed as a subexpression of an earlier node.
+                       return
+               }
+
+               var op boolOp
+               switch e.Op {
+               case token.LOR:
+                       op = or
+               case token.LAND:
+                       op = and
+               default:
+                       return
+               }
+
+               comm := op.commutativeSets(pass.TypesInfo, e, seen)
+               for _, exprs := range comm {
+                       op.checkRedundant(pass, exprs)
+                       op.checkSuspect(pass, exprs)
+               }
+       })
+       return nil, nil
+}
+
+type boolOp struct {
+       name  string
+       tok   token.Token // token corresponding to this operator
+       badEq token.Token // token corresponding to the equality test that should not be used with this operator
+}
+
+var (
+       or  = boolOp{"or", token.LOR, token.NEQ}
+       and = boolOp{"and", token.LAND, token.EQL}
+)
+
+// commutativeSets returns all side effect free sets of
+// expressions in e that are connected by op.
+// For example, given 'a || b || f() || c || d' with the or op,
+// commutativeSets returns {{b, a}, {d, c}}.
+// commutativeSets adds any expanded BinaryExprs to seen.
+func (op boolOp) commutativeSets(info *types.Info, e *ast.BinaryExpr, seen map[*ast.BinaryExpr]bool) [][]ast.Expr {
+       exprs := op.split(e, seen)
+
+       // Partition the slice of expressions into commutative sets.
+       i := 0
+       var sets [][]ast.Expr
+       for j := 0; j <= len(exprs); j++ {
+               if j == len(exprs) || hasSideEffects(info, exprs[j]) {
+                       if i < j {
+                               sets = append(sets, exprs[i:j])
+                       }
+                       i = j + 1
+               }
+       }
+
+       return sets
+}
+
+// checkRedundant checks for expressions of the form
+//   e && e
+//   e || e
+// Exprs must contain only side effect free expressions.
+func (op boolOp) checkRedundant(pass *analysis.Pass, exprs []ast.Expr) {
+       seen := make(map[string]bool)
+       for _, e := range exprs {
+               efmt := analysisutil.Format(pass.Fset, e)
+               if seen[efmt] {
+                       pass.ReportRangef(e, "redundant %s: %s %s %s", op.name, efmt, op.tok, efmt)
+               } else {
+                       seen[efmt] = true
+               }
+       }
+}
+
+// checkSuspect checks for expressions of the form
+//   x != c1 || x != c2
+//   x == c1 && x == c2
+// where c1 and c2 are constant expressions.
+// If c1 and c2 are the same then it's redundant;
+// if c1 and c2 are different then it's always true or always false.
+// Exprs must contain only side effect free expressions.
+func (op boolOp) checkSuspect(pass *analysis.Pass, exprs []ast.Expr) {
+       // seen maps from expressions 'x' to equality expressions 'x != c'.
+       seen := make(map[string]string)
+
+       for _, e := range exprs {
+               bin, ok := e.(*ast.BinaryExpr)
+               if !ok || bin.Op != op.badEq {
+                       continue
+               }
+
+               // In order to avoid false positives, restrict to cases
+               // in which one of the operands is constant. We're then
+               // interested in the other operand.
+               // In the rare case in which both operands are constant
+               // (e.g. runtime.GOOS and "windows"), we'll only catch
+               // mistakes if the LHS is repeated, which is how most
+               // code is written.
+               var x ast.Expr
+               switch {
+               case pass.TypesInfo.Types[bin.Y].Value != nil:
+                       x = bin.X
+               case pass.TypesInfo.Types[bin.X].Value != nil:
+                       x = bin.Y
+               default:
+                       continue
+               }
+
+               // e is of the form 'x != c' or 'x == c'.
+               xfmt := analysisutil.Format(pass.Fset, x)
+               efmt := analysisutil.Format(pass.Fset, e)
+               if prev, found := seen[xfmt]; found {
+                       // checkRedundant handles the case in which efmt == prev.
+                       if efmt != prev {
+                               pass.ReportRangef(e, "suspect %s: %s %s %s", op.name, efmt, op.tok, prev)
+                       }
+               } else {
+                       seen[xfmt] = efmt
+               }
+       }
+}
+
+// hasSideEffects reports whether evaluation of e has side effects.
+func hasSideEffects(info *types.Info, e ast.Expr) bool {
+       safe := true
+       ast.Inspect(e, func(node ast.Node) bool {
+               switch n := node.(type) {
+               case *ast.CallExpr:
+                       typVal := info.Types[n.Fun]
+                       switch {
+                       case typVal.IsType():
+                               // Type conversion, which is safe.
+                       case typVal.IsBuiltin():
+                               // Builtin func, conservatively assumed to not
+                               // be safe for now.
+                               safe = false
+                               return false
+                       default:
+                               // A non-builtin func or method call.
+                               // Conservatively assume that all of them have
+                               // side effects for now.
+                               safe = false
+                               return false
+                       }
+               case *ast.UnaryExpr:
+                       if n.Op == token.ARROW {
+                               safe = false
+                               return false
+                       }
+               }
+               return true
+       })
+       return !safe
+}
+
+// split returns a slice of all subexpressions in e that are connected by op.
+// For example, given 'a || (b || c) || d' with the or op,
+// split returns []{d, c, b, a}.
+// seen[e] is already true; any newly processed exprs are added to seen.
+func (op boolOp) split(e ast.Expr, seen map[*ast.BinaryExpr]bool) (exprs []ast.Expr) {
+       for {
+               e = unparen(e)
+               if b, ok := e.(*ast.BinaryExpr); ok && b.Op == op.tok {
+                       seen[b] = true
+                       exprs = append(exprs, op.split(b.Y, seen)...)
+                       e = b.X
+               } else {
+                       exprs = append(exprs, e)
+                       break
+               }
+       }
+       return
+}
+
+// unparen returns e with any enclosing parentheses stripped.
+func unparen(e ast.Expr) ast.Expr {
+       for {
+               p, ok := e.(*ast.ParenExpr)
+               if !ok {
+                       return e
+               }
+               e = p.X
+       }
+}