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.
16 "check for mistakes involving boolean operators",
21 func checkBool(f
*File
, n ast
.Node
) {
22 e
:= n
.(*ast
.BinaryExpr
)
34 comm
:= op
.commutativeSets(e
)
35 for _
, exprs
:= range comm
{
36 op
.checkRedundant(f
, exprs
)
37 op
.checkSuspect(f
, exprs
)
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
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
{
59 // Partition the slice of expressions into commutative sets.
62 for j
:= 0; j
<= len(exprs
); j
++ {
63 if j
== len(exprs
) ||
hasSideEffects(exprs
[j
]) {
65 sets
= append(sets
, exprs
[i
:j
])
74 // checkRedundant checks for expressions of the form
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
{
83 f
.Badf(e
.Pos(), "redundant %s: %s %s %s", op
.name
, efmt
, op
.tok
, efmt
)
90 // checkSuspect checks for expressions of the form
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
{
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
116 case f
.pkg
.types
[bin
.Y
].Value
!= nil:
118 case f
.pkg
.types
[bin
.X
].Value
!= nil:
124 // e is of the form 'x != c' or 'x == c'.
127 if prev
, found
:= seen
[xfmt
]; found
{
128 // checkRedundant handles the case in which efmt == prev.
130 f
.Badf(e
.Pos(), "suspect %s: %s %s %s", op
.name
, efmt
, op
.tok
, prev
)
138 // hasSideEffects reports whether evaluation of e has side effects.
139 func hasSideEffects(e ast
.Expr
) bool {
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.
150 if n
.Op
== token
.ARROW
{
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
) {
166 if b
, ok
:= e
.(*ast
.BinaryExpr
); ok
&& b
.Op
== op
.tok
{
167 exprs
= append(exprs
, op
.split(b
.Y
)...)
170 exprs
= append(exprs
, e
)
177 // unparen returns e with any enclosing parentheses stripped.
178 func unparen(e ast
.Expr
) ast
.Expr
{
180 p
, ok
:= e
.(*ast
.ParenExpr
)