1
// Copyright 2013 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
// Check for syntactically unreachable code.
14
type deadState struct {
16
hasBreak map[ast.Stmt]bool
17
hasGoto map[string]bool
18
labels map[string]ast.Stmt
24
// checkUnreachable checks a function body for dead code.
25
func (f *File) checkUnreachable(body *ast.BlockStmt) {
26
if !vet("unreachable") || body == nil {
32
hasBreak: make(map[ast.Stmt]bool),
33
hasGoto: make(map[string]bool),
34
labels: make(map[string]ast.Stmt),
43
// findLabels gathers information about the labels defined and used by stmt
44
// and about which statements break, whether a label is involved or not.
45
func (d *deadState) findLabels(stmt ast.Stmt) {
46
switch x := stmt.(type) {
48
d.f.Warnf(x.Pos(), "internal error in findLabels: unexpected statement %T", x)
60
// no statements inside
63
for _, stmt := range x.List {
70
d.hasGoto[x.Label.Name] = true
75
stmt = d.labels[x.Label.Name]
78
d.hasBreak[stmt] = true
88
case *ast.LabeledStmt:
89
d.labels[x.Label.Name] = x.Stmt
92
// These cases are all the same, but the x.Body only works
93
// when the specific type of x is known, so the cases cannot
96
outer := d.breakTarget
102
outer := d.breakTarget
105
d.breakTarget = outer
107
case *ast.SelectStmt:
108
outer := d.breakTarget
111
d.breakTarget = outer
113
case *ast.SwitchStmt:
114
outer := d.breakTarget
117
d.breakTarget = outer
119
case *ast.TypeSwitchStmt:
120
outer := d.breakTarget
123
d.breakTarget = outer
125
case *ast.CommClause:
126
for _, stmt := range x.Body {
130
case *ast.CaseClause:
131
for _, stmt := range x.Body {
137
// findDead walks the statement looking for dead code.
138
// If d.reachable is false on entry, stmt itself is dead.
139
// When findDead returns, d.reachable tells whether the
140
// statement following stmt is reachable.
141
func (d *deadState) findDead(stmt ast.Stmt) {
142
// Is this a labeled goto target?
143
// If so, assume it is reachable due to the goto.
144
// This is slightly conservative, in that we don't
145
// check that the goto is reachable, so
147
// will not provoke a warning.
148
// But it's good enough.
149
if x, isLabel := stmt.(*ast.LabeledStmt); isLabel && d.hasGoto[x.Label.Name] {
156
// do not warn about unreachable empty statements
158
d.f.Warnf(stmt.Pos(), "unreachable code")
159
d.reachable = true // silence error about next statement
163
switch x := stmt.(type) {
165
d.f.Warnf(x.Pos(), "internal error in findDead: unexpected statement %T", x)
167
case *ast.AssignStmt,
178
for _, stmt := range x.List {
182
case *ast.BranchStmt:
184
case token.BREAK, token.GOTO, token.FALLTHROUGH:
187
// NOTE: We accept "continue" statements as terminating.
188
// They are not necessary in the spec definition of terminating,
189
// because a continue statement cannot be the final statement
190
// before a return. But for the more general problem of syntactically
191
// identifying dead code, continue redirects control flow just
192
// like the other terminating statements.
198
call, ok := x.X.(*ast.CallExpr)
200
name, ok := call.Fun.(*ast.Ident)
201
if ok && name.Name == "panic" && name.Obj == nil {
208
d.reachable = x.Cond != nil || d.hasBreak[x]
216
d.reachable = d.reachable || r
218
// might not have executed if statement
222
case *ast.LabeledStmt:
229
case *ast.ReturnStmt:
232
case *ast.SelectStmt:
233
// NOTE: Unlike switch and type switch below, we don't care
234
// whether a select has a default, because a select without a
235
// default blocks until one of the cases can run. That's different
236
// from a switch without a default, which behaves like it has
237
// a default with an empty body.
238
anyReachable := false
239
for _, comm := range x.Body.List {
241
for _, stmt := range comm.(*ast.CommClause).Body {
244
anyReachable = anyReachable || d.reachable
246
d.reachable = anyReachable || d.hasBreak[x]
248
case *ast.SwitchStmt:
249
anyReachable := false
251
for _, cas := range x.Body.List {
252
cc := cas.(*ast.CaseClause)
257
for _, stmt := range cc.Body {
260
anyReachable = anyReachable || d.reachable
262
d.reachable = anyReachable || d.hasBreak[x] || !hasDefault
264
case *ast.TypeSwitchStmt:
265
anyReachable := false
267
for _, cas := range x.Body.List {
268
cc := cas.(*ast.CaseClause)
273
for _, stmt := range cc.Body {
276
anyReachable = anyReachable || d.reachable
278
d.reachable = anyReachable || d.hasBreak[x] || !hasDefault