~ubuntu-branches/ubuntu/utopic/golang/utopic

« back to all changes in this revision

Viewing changes to src/cmd/vet/deadcode.go

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2013-08-20 14:06:23 UTC
  • mfrom: (14.1.23 saucy-proposed)
  • Revision ID: package-import@ubuntu.com-20130820140623-b414jfxi3m0qkmrq
Tags: 2:1.1.2-2ubuntu1
* Merge from Debian unstable (LP: #1211749, #1202027). Remaining changes:
  - 016-armhf-elf-header.patch: Use correct ELF header for armhf binaries.
  - d/control,control.cross: Update Breaks/Replaces for Ubuntu
    versions to ensure smooth upgrades, regenerate control file.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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.
 
4
 
 
5
// Check for syntactically unreachable code.
 
6
 
 
7
package main
 
8
 
 
9
import (
 
10
        "go/ast"
 
11
        "go/token"
 
12
)
 
13
 
 
14
type deadState struct {
 
15
        f           *File
 
16
        hasBreak    map[ast.Stmt]bool
 
17
        hasGoto     map[string]bool
 
18
        labels      map[string]ast.Stmt
 
19
        breakTarget ast.Stmt
 
20
 
 
21
        reachable bool
 
22
}
 
23
 
 
24
// checkUnreachable checks a function body for dead code.
 
25
func (f *File) checkUnreachable(body *ast.BlockStmt) {
 
26
        if !vet("unreachable") || body == nil {
 
27
                return
 
28
        }
 
29
 
 
30
        d := &deadState{
 
31
                f:        f,
 
32
                hasBreak: make(map[ast.Stmt]bool),
 
33
                hasGoto:  make(map[string]bool),
 
34
                labels:   make(map[string]ast.Stmt),
 
35
        }
 
36
 
 
37
        d.findLabels(body)
 
38
 
 
39
        d.reachable = true
 
40
        d.findDead(body)
 
41
}
 
42
 
 
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) {
 
47
        default:
 
48
                d.f.Warnf(x.Pos(), "internal error in findLabels: unexpected statement %T", x)
 
49
 
 
50
        case *ast.AssignStmt,
 
51
                *ast.BadStmt,
 
52
                *ast.DeclStmt,
 
53
                *ast.DeferStmt,
 
54
                *ast.EmptyStmt,
 
55
                *ast.ExprStmt,
 
56
                *ast.GoStmt,
 
57
                *ast.IncDecStmt,
 
58
                *ast.ReturnStmt,
 
59
                *ast.SendStmt:
 
60
                // no statements inside
 
61
 
 
62
        case *ast.BlockStmt:
 
63
                for _, stmt := range x.List {
 
64
                        d.findLabels(stmt)
 
65
                }
 
66
 
 
67
        case *ast.BranchStmt:
 
68
                switch x.Tok {
 
69
                case token.GOTO:
 
70
                        d.hasGoto[x.Label.Name] = true
 
71
 
 
72
                case token.BREAK:
 
73
                        stmt := d.breakTarget
 
74
                        if x.Label != nil {
 
75
                                stmt = d.labels[x.Label.Name]
 
76
                        }
 
77
                        if stmt != nil {
 
78
                                d.hasBreak[stmt] = true
 
79
                        }
 
80
                }
 
81
 
 
82
        case *ast.IfStmt:
 
83
                d.findLabels(x.Body)
 
84
                if x.Else != nil {
 
85
                        d.findLabels(x.Else)
 
86
                }
 
87
 
 
88
        case *ast.LabeledStmt:
 
89
                d.labels[x.Label.Name] = x.Stmt
 
90
                d.findLabels(x.Stmt)
 
91
 
 
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
 
94
        // be merged.
 
95
        case *ast.ForStmt:
 
96
                outer := d.breakTarget
 
97
                d.breakTarget = x
 
98
                d.findLabels(x.Body)
 
99
                d.breakTarget = outer
 
100
 
 
101
        case *ast.RangeStmt:
 
102
                outer := d.breakTarget
 
103
                d.breakTarget = x
 
104
                d.findLabels(x.Body)
 
105
                d.breakTarget = outer
 
106
 
 
107
        case *ast.SelectStmt:
 
108
                outer := d.breakTarget
 
109
                d.breakTarget = x
 
110
                d.findLabels(x.Body)
 
111
                d.breakTarget = outer
 
112
 
 
113
        case *ast.SwitchStmt:
 
114
                outer := d.breakTarget
 
115
                d.breakTarget = x
 
116
                d.findLabels(x.Body)
 
117
                d.breakTarget = outer
 
118
 
 
119
        case *ast.TypeSwitchStmt:
 
120
                outer := d.breakTarget
 
121
                d.breakTarget = x
 
122
                d.findLabels(x.Body)
 
123
                d.breakTarget = outer
 
124
 
 
125
        case *ast.CommClause:
 
126
                for _, stmt := range x.Body {
 
127
                        d.findLabels(stmt)
 
128
                }
 
129
 
 
130
        case *ast.CaseClause:
 
131
                for _, stmt := range x.Body {
 
132
                        d.findLabels(stmt)
 
133
                }
 
134
        }
 
135
}
 
136
 
 
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
 
146
        //      L: goto L
 
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] {
 
150
                d.reachable = true
 
151
        }
 
152
 
 
153
        if !d.reachable {
 
154
                switch stmt.(type) {
 
155
                case *ast.EmptyStmt:
 
156
                        // do not warn about unreachable empty statements
 
157
                default:
 
158
                        d.f.Warnf(stmt.Pos(), "unreachable code")
 
159
                        d.reachable = true // silence error about next statement
 
160
                }
 
161
        }
 
162
 
 
163
        switch x := stmt.(type) {
 
164
        default:
 
165
                d.f.Warnf(x.Pos(), "internal error in findDead: unexpected statement %T", x)
 
166
 
 
167
        case *ast.AssignStmt,
 
168
                *ast.BadStmt,
 
169
                *ast.DeclStmt,
 
170
                *ast.DeferStmt,
 
171
                *ast.EmptyStmt,
 
172
                *ast.GoStmt,
 
173
                *ast.IncDecStmt,
 
174
                *ast.SendStmt:
 
175
                // no control flow
 
176
 
 
177
        case *ast.BlockStmt:
 
178
                for _, stmt := range x.List {
 
179
                        d.findDead(stmt)
 
180
                }
 
181
 
 
182
        case *ast.BranchStmt:
 
183
                switch x.Tok {
 
184
                case token.BREAK, token.GOTO, token.FALLTHROUGH:
 
185
                        d.reachable = false
 
186
                case token.CONTINUE:
 
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.
 
193
                        d.reachable = false
 
194
                }
 
195
 
 
196
        case *ast.ExprStmt:
 
197
                // Call to panic?
 
198
                call, ok := x.X.(*ast.CallExpr)
 
199
                if ok {
 
200
                        name, ok := call.Fun.(*ast.Ident)
 
201
                        if ok && name.Name == "panic" && name.Obj == nil {
 
202
                                d.reachable = false
 
203
                        }
 
204
                }
 
205
 
 
206
        case *ast.ForStmt:
 
207
                d.findDead(x.Body)
 
208
                d.reachable = x.Cond != nil || d.hasBreak[x]
 
209
 
 
210
        case *ast.IfStmt:
 
211
                d.findDead(x.Body)
 
212
                if x.Else != nil {
 
213
                        r := d.reachable
 
214
                        d.reachable = true
 
215
                        d.findDead(x.Else)
 
216
                        d.reachable = d.reachable || r
 
217
                } else {
 
218
                        // might not have executed if statement
 
219
                        d.reachable = true
 
220
                }
 
221
 
 
222
        case *ast.LabeledStmt:
 
223
                d.findDead(x.Stmt)
 
224
 
 
225
        case *ast.RangeStmt:
 
226
                d.findDead(x.Body)
 
227
                d.reachable = true
 
228
 
 
229
        case *ast.ReturnStmt:
 
230
                d.reachable = false
 
231
 
 
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 {
 
240
                        d.reachable = true
 
241
                        for _, stmt := range comm.(*ast.CommClause).Body {
 
242
                                d.findDead(stmt)
 
243
                        }
 
244
                        anyReachable = anyReachable || d.reachable
 
245
                }
 
246
                d.reachable = anyReachable || d.hasBreak[x]
 
247
 
 
248
        case *ast.SwitchStmt:
 
249
                anyReachable := false
 
250
                hasDefault := false
 
251
                for _, cas := range x.Body.List {
 
252
                        cc := cas.(*ast.CaseClause)
 
253
                        if cc.List == nil {
 
254
                                hasDefault = true
 
255
                        }
 
256
                        d.reachable = true
 
257
                        for _, stmt := range cc.Body {
 
258
                                d.findDead(stmt)
 
259
                        }
 
260
                        anyReachable = anyReachable || d.reachable
 
261
                }
 
262
                d.reachable = anyReachable || d.hasBreak[x] || !hasDefault
 
263
 
 
264
        case *ast.TypeSwitchStmt:
 
265
                anyReachable := false
 
266
                hasDefault := false
 
267
                for _, cas := range x.Body.List {
 
268
                        cc := cas.(*ast.CaseClause)
 
269
                        if cc.List == nil {
 
270
                                hasDefault = true
 
271
                        }
 
272
                        d.reachable = true
 
273
                        for _, stmt := range cc.Body {
 
274
                                d.findDead(stmt)
 
275
                        }
 
276
                        anyReachable = anyReachable || d.reachable
 
277
                }
 
278
                d.reachable = anyReachable || d.hasBreak[x] || !hasDefault
 
279
        }
 
280
}