Simplify gcov_histogram as it's used only for ARCS counters.
[official-gcc.git] / libgo / go / cmd / vet / bool.go
blob07c2a93dffa3c2218d86244f1d8f2fed90b4aa88
1 // Copyright 2014 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // This file contains boolean condition tests.
7 package main
9 import (
10 "go/ast"
11 "go/token"
14 func init() {
15 register("bool",
16 "check for mistakes involving boolean operators",
17 checkBool,
18 binaryExpr)
21 func checkBool(f *File, n ast.Node) {
22 e := n.(*ast.BinaryExpr)
24 var op boolOp
25 switch e.Op {
26 case token.LOR:
27 op = or
28 case token.LAND:
29 op = and
30 default:
31 return
34 comm := op.commutativeSets(e)
35 for _, exprs := range comm {
36 op.checkRedundant(f, exprs)
37 op.checkSuspect(f, exprs)
41 type boolOp struct {
42 name string
43 tok token.Token // token corresponding to this operator
44 badEq token.Token // token corresponding to the equality test that should not be used with this operator
47 var (
48 or = boolOp{"or", token.LOR, token.NEQ}
49 and = boolOp{"and", token.LAND, token.EQL}
52 // commutativeSets returns all side effect free sets of
53 // expressions in e that are connected by op.
54 // For example, given 'a || b || f() || c || d' with the or op,
55 // commutativeSets returns {{b, a}, {d, c}}.
56 func (op boolOp) commutativeSets(e *ast.BinaryExpr) [][]ast.Expr {
57 exprs := op.split(e)
59 // Partition the slice of expressions into commutative sets.
60 i := 0
61 var sets [][]ast.Expr
62 for j := 0; j <= len(exprs); j++ {
63 if j == len(exprs) || hasSideEffects(exprs[j]) {
64 if i < j {
65 sets = append(sets, exprs[i:j])
67 i = j + 1
71 return sets
74 // checkRedundant checks for expressions of the form
75 // e && e
76 // e || e
77 // Exprs must contain only side effect free expressions.
78 func (op boolOp) checkRedundant(f *File, exprs []ast.Expr) {
79 seen := make(map[string]bool)
80 for _, e := range exprs {
81 efmt := f.gofmt(e)
82 if seen[efmt] {
83 f.Badf(e.Pos(), "redundant %s: %s %s %s", op.name, efmt, op.tok, efmt)
84 } else {
85 seen[efmt] = true
90 // checkSuspect checks for expressions of the form
91 // x != c1 || x != c2
92 // x == c1 && x == c2
93 // where c1 and c2 are constant expressions.
94 // If c1 and c2 are the same then it's redundant;
95 // if c1 and c2 are different then it's always true or always false.
96 // Exprs must contain only side effect free expressions.
97 func (op boolOp) checkSuspect(f *File, exprs []ast.Expr) {
98 // seen maps from expressions 'x' to equality expressions 'x != c'.
99 seen := make(map[string]string)
101 for _, e := range exprs {
102 bin, ok := e.(*ast.BinaryExpr)
103 if !ok || bin.Op != op.badEq {
104 continue
107 // In order to avoid false positives, restrict to cases
108 // in which one of the operands is constant. We're then
109 // interested in the other operand.
110 // In the rare case in which both operands are constant
111 // (e.g. runtime.GOOS and "windows"), we'll only catch
112 // mistakes if the LHS is repeated, which is how most
113 // code is written.
114 var x ast.Expr
115 switch {
116 case f.pkg.types[bin.Y].Value != nil:
117 x = bin.X
118 case f.pkg.types[bin.X].Value != nil:
119 x = bin.Y
120 default:
121 continue
124 // e is of the form 'x != c' or 'x == c'.
125 xfmt := f.gofmt(x)
126 efmt := f.gofmt(e)
127 if prev, found := seen[xfmt]; found {
128 // checkRedundant handles the case in which efmt == prev.
129 if efmt != prev {
130 f.Badf(e.Pos(), "suspect %s: %s %s %s", op.name, efmt, op.tok, prev)
132 } else {
133 seen[xfmt] = efmt
138 // hasSideEffects reports whether evaluation of e has side effects.
139 func hasSideEffects(e ast.Expr) bool {
140 safe := true
141 ast.Inspect(e, func(node ast.Node) bool {
142 switch n := node.(type) {
143 // Using CallExpr here will catch conversions
144 // as well as function and method invocations.
145 // We'll live with the false negatives for now.
146 case *ast.CallExpr:
147 safe = false
148 return false
149 case *ast.UnaryExpr:
150 if n.Op == token.ARROW {
151 safe = false
152 return false
155 return true
157 return !safe
160 // split returns a slice of all subexpressions in e that are connected by op.
161 // For example, given 'a || (b || c) || d' with the or op,
162 // split returns []{d, c, b, a}.
163 func (op boolOp) split(e ast.Expr) (exprs []ast.Expr) {
164 for {
165 e = unparen(e)
166 if b, ok := e.(*ast.BinaryExpr); ok && b.Op == op.tok {
167 exprs = append(exprs, op.split(b.Y)...)
168 e = b.X
169 } else {
170 exprs = append(exprs, e)
171 break
174 return
177 // unparen returns e with any enclosing parentheses stripped.
178 func unparen(e ast.Expr) ast.Expr {
179 for {
180 p, ok := e.(*ast.ParenExpr)
181 if !ok {
182 return e
184 e = p.X