| // Copyright 2016 The Go Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style | 
 | // license that can be found in the LICENSE file. | 
 |  | 
 | package cfg | 
 |  | 
 | import ( | 
 | 	"bytes" | 
 | 	"fmt" | 
 | 	"go/ast" | 
 | 	"go/parser" | 
 | 	"go/token" | 
 | 	"testing" | 
 | ) | 
 |  | 
 | const src = `package main | 
 |  | 
 | import "log" | 
 |  | 
 | func f1() { | 
 | 	live() | 
 | 	return | 
 | 	dead() | 
 | } | 
 |  | 
 | func f2() { | 
 | 	for { | 
 | 		live() | 
 | 	} | 
 | 	dead() | 
 | } | 
 |  | 
 | func f3() { | 
 | 	if true { // even known values are ignored | 
 | 		return | 
 | 	} | 
 | 	for true { // even known values are ignored | 
 | 		live() | 
 | 	} | 
 | 	for { | 
 | 		live() | 
 | 	} | 
 | 	dead() | 
 | } | 
 |  | 
 | func f4(x int) { | 
 | 	switch x { | 
 | 	case 1: | 
 | 		live() | 
 | 		fallthrough | 
 | 	case 2: | 
 | 		live() | 
 | 		log.Fatal() | 
 | 	default: | 
 | 		panic("oops") | 
 | 	} | 
 | 	dead() | 
 | } | 
 |  | 
 | func f4(ch chan int) { | 
 | 	select { | 
 | 	case <-ch: | 
 | 		live() | 
 | 		return | 
 | 	default: | 
 | 		live() | 
 | 		panic("oops") | 
 | 	} | 
 | 	dead() | 
 | } | 
 |  | 
 | func f5(unknown bool) { | 
 | 	for { | 
 | 		if unknown { | 
 | 			break | 
 | 		} | 
 | 		continue | 
 | 		dead() | 
 | 	} | 
 | 	live() | 
 | } | 
 |  | 
 | func f6(unknown bool) { | 
 | outer: | 
 | 	for { | 
 | 		for { | 
 | 			break outer | 
 | 			dead() | 
 | 		} | 
 | 		dead() | 
 | 	} | 
 | 	live() | 
 | } | 
 |  | 
 | func f7() { | 
 | 	for { | 
 | 		break nosuchlabel | 
 | 		dead() | 
 | 	} | 
 | 	dead() | 
 | } | 
 |  | 
 | func f8() { | 
 | 	select{} | 
 | 	dead() | 
 | } | 
 |  | 
 | func f9(ch chan int) { | 
 | 	select { | 
 | 	case <-ch: | 
 | 		return | 
 | 	} | 
 | 	dead() | 
 | } | 
 |  | 
 | func f10(ch chan int) { | 
 | 	select { | 
 | 	case <-ch: | 
 | 		return | 
 | 		dead() | 
 | 	default: | 
 | 	} | 
 | 	live() | 
 | } | 
 |  | 
 | func f11() { | 
 | 	goto; // mustn't crash | 
 | 	dead() | 
 | } | 
 |  | 
 | ` | 
 |  | 
 | func TestDeadCode(t *testing.T) { | 
 | 	// We'll use dead code detection to verify the CFG. | 
 |  | 
 | 	fset := token.NewFileSet() | 
 | 	f, err := parser.ParseFile(fset, "dummy.go", src, parser.Mode(0)) | 
 | 	if err != nil { | 
 | 		t.Fatal(err) | 
 | 	} | 
 | 	for _, decl := range f.Decls { | 
 | 		if decl, ok := decl.(*ast.FuncDecl); ok { | 
 | 			g := New(decl.Body, mayReturn) | 
 |  | 
 | 			// Print statements in unreachable blocks | 
 | 			// (in order determined by builder). | 
 | 			var buf bytes.Buffer | 
 | 			for _, b := range g.Blocks { | 
 | 				if !b.Live { | 
 | 					for _, n := range b.Nodes { | 
 | 						fmt.Fprintf(&buf, "\t%s\n", formatNode(fset, n)) | 
 | 					} | 
 | 				} | 
 | 			} | 
 |  | 
 | 			// Check that the result contains "dead" at least once but not "live". | 
 | 			if !bytes.Contains(buf.Bytes(), []byte("dead")) || | 
 | 				bytes.Contains(buf.Bytes(), []byte("live")) { | 
 | 				t.Errorf("unexpected dead statements in function %s:\n%s", | 
 | 					decl.Name.Name, | 
 | 					&buf) | 
 | 				t.Logf("control flow graph:\n%s", g.Format(fset)) | 
 | 			} | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | // A trivial mayReturn predicate that looks only at syntax, not types. | 
 | func mayReturn(call *ast.CallExpr) bool { | 
 | 	switch fun := call.Fun.(type) { | 
 | 	case *ast.Ident: | 
 | 		return fun.Name != "panic" | 
 | 	case *ast.SelectorExpr: | 
 | 		return fun.Sel.Name != "Fatal" | 
 | 	} | 
 | 	return true | 
 | } |