Source file src/pkg/cmd/vendor/golang.org/x/tools/go/analysis/passes/bools/bools.go
1
2
3
4
5
6
7 package bools
8
9 import (
10 "go/ast"
11 "go/token"
12 "go/types"
13
14 "golang.org/x/tools/go/analysis"
15 "golang.org/x/tools/go/analysis/passes/inspect"
16 "golang.org/x/tools/go/analysis/passes/internal/analysisutil"
17 "golang.org/x/tools/go/ast/inspector"
18 )
19
20 var Analyzer = &analysis.Analyzer{
21 Name: "bools",
22 Doc: "check for common mistakes involving boolean operators",
23 Requires: []*analysis.Analyzer{inspect.Analyzer},
24 Run: run,
25 }
26
27 func run(pass *analysis.Pass) (interface{}, error) {
28 inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
29
30 nodeFilter := []ast.Node{
31 (*ast.BinaryExpr)(nil),
32 }
33 seen := make(map[*ast.BinaryExpr]bool)
34 inspect.Preorder(nodeFilter, func(n ast.Node) {
35 e := n.(*ast.BinaryExpr)
36 if seen[e] {
37
38 return
39 }
40
41 var op boolOp
42 switch e.Op {
43 case token.LOR:
44 op = or
45 case token.LAND:
46 op = and
47 default:
48 return
49 }
50
51 comm := op.commutativeSets(pass.TypesInfo, e, seen)
52 for _, exprs := range comm {
53 op.checkRedundant(pass, exprs)
54 op.checkSuspect(pass, exprs)
55 }
56 })
57 return nil, nil
58 }
59
60 type boolOp struct {
61 name string
62 tok token.Token
63 badEq token.Token
64 }
65
66 var (
67 or = boolOp{"or", token.LOR, token.NEQ}
68 and = boolOp{"and", token.LAND, token.EQL}
69 )
70
71
72
73
74
75
76 func (op boolOp) commutativeSets(info *types.Info, e *ast.BinaryExpr, seen map[*ast.BinaryExpr]bool) [][]ast.Expr {
77 exprs := op.split(e, seen)
78
79
80 i := 0
81 var sets [][]ast.Expr
82 for j := 0; j <= len(exprs); j++ {
83 if j == len(exprs) || hasSideEffects(info, exprs[j]) {
84 if i < j {
85 sets = append(sets, exprs[i:j])
86 }
87 i = j + 1
88 }
89 }
90
91 return sets
92 }
93
94
95
96
97
98 func (op boolOp) checkRedundant(pass *analysis.Pass, exprs []ast.Expr) {
99 seen := make(map[string]bool)
100 for _, e := range exprs {
101 efmt := analysisutil.Format(pass.Fset, e)
102 if seen[efmt] {
103 pass.Reportf(e.Pos(), "redundant %s: %s %s %s", op.name, efmt, op.tok, efmt)
104 } else {
105 seen[efmt] = true
106 }
107 }
108 }
109
110
111
112
113
114
115
116
117 func (op boolOp) checkSuspect(pass *analysis.Pass, exprs []ast.Expr) {
118
119 seen := make(map[string]string)
120
121 for _, e := range exprs {
122 bin, ok := e.(*ast.BinaryExpr)
123 if !ok || bin.Op != op.badEq {
124 continue
125 }
126
127
128
129
130
131
132
133
134 var x ast.Expr
135 switch {
136 case pass.TypesInfo.Types[bin.Y].Value != nil:
137 x = bin.X
138 case pass.TypesInfo.Types[bin.X].Value != nil:
139 x = bin.Y
140 default:
141 continue
142 }
143
144
145 xfmt := analysisutil.Format(pass.Fset, x)
146 efmt := analysisutil.Format(pass.Fset, e)
147 if prev, found := seen[xfmt]; found {
148
149 if efmt != prev {
150 pass.Reportf(e.Pos(), "suspect %s: %s %s %s", op.name, efmt, op.tok, prev)
151 }
152 } else {
153 seen[xfmt] = efmt
154 }
155 }
156 }
157
158
159 func hasSideEffects(info *types.Info, e ast.Expr) bool {
160 safe := true
161 ast.Inspect(e, func(node ast.Node) bool {
162 switch n := node.(type) {
163 case *ast.CallExpr:
164 typVal := info.Types[n.Fun]
165 switch {
166 case typVal.IsType():
167
168 case typVal.IsBuiltin():
169
170
171 safe = false
172 return false
173 default:
174
175
176
177 safe = false
178 return false
179 }
180 case *ast.UnaryExpr:
181 if n.Op == token.ARROW {
182 safe = false
183 return false
184 }
185 }
186 return true
187 })
188 return !safe
189 }
190
191
192
193
194
195 func (op boolOp) split(e ast.Expr, seen map[*ast.BinaryExpr]bool) (exprs []ast.Expr) {
196 for {
197 e = unparen(e)
198 if b, ok := e.(*ast.BinaryExpr); ok && b.Op == op.tok {
199 seen[b] = true
200 exprs = append(exprs, op.split(b.Y, seen)...)
201 e = b.X
202 } else {
203 exprs = append(exprs, e)
204 break
205 }
206 }
207 return
208 }
209
210
211 func unparen(e ast.Expr) ast.Expr {
212 for {
213 p, ok := e.(*ast.ParenExpr)
214 if !ok {
215 return e
216 }
217 e = p.X
218 }
219 }
220
View as plain text