1 | // Copyright 2016 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 | package cfg |
6 | |
7 | import ( |
8 | "bytes" |
9 | "fmt" |
10 | "go/ast" |
11 | "go/parser" |
12 | "go/token" |
13 | "testing" |
14 | ) |
15 | |
16 | const src = `package main |
17 | |
18 | import "log" |
19 | |
20 | func f1() { |
21 | live() |
22 | return |
23 | dead() |
24 | } |
25 | |
26 | func f2() { |
27 | for { |
28 | live() |
29 | } |
30 | dead() |
31 | } |
32 | |
33 | func f3() { |
34 | if true { // even known values are ignored |
35 | return |
36 | } |
37 | for true { // even known values are ignored |
38 | live() |
39 | } |
40 | for { |
41 | live() |
42 | } |
43 | dead() |
44 | } |
45 | |
46 | func f4(x int) { |
47 | switch x { |
48 | case 1: |
49 | live() |
50 | fallthrough |
51 | case 2: |
52 | live() |
53 | log.Fatal() |
54 | default: |
55 | panic("oops") |
56 | } |
57 | dead() |
58 | } |
59 | |
60 | func f4(ch chan int) { |
61 | select { |
62 | case <-ch: |
63 | live() |
64 | return |
65 | default: |
66 | live() |
67 | panic("oops") |
68 | } |
69 | dead() |
70 | } |
71 | |
72 | func f5(unknown bool) { |
73 | for { |
74 | if unknown { |
75 | break |
76 | } |
77 | continue |
78 | dead() |
79 | } |
80 | live() |
81 | } |
82 | |
83 | func f6(unknown bool) { |
84 | outer: |
85 | for { |
86 | for { |
87 | break outer |
88 | dead() |
89 | } |
90 | dead() |
91 | } |
92 | live() |
93 | } |
94 | |
95 | func f7() { |
96 | for { |
97 | break nosuchlabel |
98 | dead() |
99 | } |
100 | dead() |
101 | } |
102 | |
103 | func f8() { |
104 | select{} |
105 | dead() |
106 | } |
107 | |
108 | func f9(ch chan int) { |
109 | select { |
110 | case <-ch: |
111 | return |
112 | } |
113 | dead() |
114 | } |
115 | |
116 | func f10(ch chan int) { |
117 | select { |
118 | case <-ch: |
119 | return |
120 | dead() |
121 | default: |
122 | } |
123 | live() |
124 | } |
125 | |
126 | func f11() { |
127 | goto; // mustn't crash |
128 | dead() |
129 | } |
130 | |
131 | ` |
132 | |
133 | func TestDeadCode(t *testing.T) { |
134 | // We'll use dead code detection to verify the CFG. |
135 | |
136 | fset := token.NewFileSet() |
137 | f, err := parser.ParseFile(fset, "dummy.go", src, parser.Mode(0)) |
138 | if err != nil { |
139 | t.Fatal(err) |
140 | } |
141 | for _, decl := range f.Decls { |
142 | if decl, ok := decl.(*ast.FuncDecl); ok { |
143 | g := New(decl.Body, mayReturn) |
144 | |
145 | // Print statements in unreachable blocks |
146 | // (in order determined by builder). |
147 | var buf bytes.Buffer |
148 | for _, b := range g.Blocks { |
149 | if !b.Live { |
150 | for _, n := range b.Nodes { |
151 | fmt.Fprintf(&buf, "\t%s\n", formatNode(fset, n)) |
152 | } |
153 | } |
154 | } |
155 | |
156 | // Check that the result contains "dead" at least once but not "live". |
157 | if !bytes.Contains(buf.Bytes(), []byte("dead")) || |
158 | bytes.Contains(buf.Bytes(), []byte("live")) { |
159 | t.Errorf("unexpected dead statements in function %s:\n%s", |
160 | decl.Name.Name, |
161 | &buf) |
162 | t.Logf("control flow graph:\n%s", g.Format(fset)) |
163 | } |
164 | } |
165 | } |
166 | } |
167 | |
168 | // A trivial mayReturn predicate that looks only at syntax, not types. |
169 | func mayReturn(call *ast.CallExpr) bool { |
170 | switch fun := call.Fun.(type) { |
171 | case *ast.Ident: |
172 | return fun.Name != "panic" |
173 | case *ast.SelectorExpr: |
174 | return fun.Sel.Name != "Fatal" |
175 | } |
176 | return true |
177 | } |
178 |
Members