github.com/jd-ly/tools@v0.5.7/go/cfg/cfg_test.go (about) 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 }