github.com/likebike/go--@v0.0.0-20190911215757-0bd925d16e96/go/src/cmd/vet/internal/cfg/cfg.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 // This package constructs a simple control-flow graph (CFG) of the 6 // statements and expressions within a single function. 7 // 8 // Use cfg.New to construct the CFG for a function body. 9 // 10 // The blocks of the CFG contain all the function's non-control 11 // statements. The CFG does not contain control statements such as If, 12 // Switch, Select, and Branch, but does contain their subexpressions. 13 // For example, this source code: 14 // 15 // if x := f(); x != nil { 16 // T() 17 // } else { 18 // F() 19 // } 20 // 21 // produces this CFG: 22 // 23 // 1: x := f() 24 // x != nil 25 // succs: 2, 3 26 // 2: T() 27 // succs: 4 28 // 3: F() 29 // succs: 4 30 // 4: 31 // 32 // The CFG does contain Return statements; even implicit returns are 33 // materialized (at the position of the function's closing brace). 34 // 35 // The CFG does not record conditions associated with conditional branch 36 // edges, nor the short-circuit semantics of the && and || operators, 37 // nor abnormal control flow caused by panic. If you need this 38 // information, use golang.org/x/tools/go/ssa instead. 39 // 40 package cfg 41 42 // Although the vet tool has type information, it is often extremely 43 // fragmentary, so for simplicity this package does not depend on 44 // go/types. Consequently control-flow conditions are ignored even 45 // when constant, and "mayReturn" information must be provided by the 46 // client. 47 import ( 48 "bytes" 49 "fmt" 50 "go/ast" 51 "go/format" 52 "go/token" 53 ) 54 55 // A CFG represents the control-flow graph of a single function. 56 // 57 // The entry point is Blocks[0]; there may be multiple return blocks. 58 type CFG struct { 59 Blocks []*Block // block[0] is entry; order otherwise undefined 60 } 61 62 // A Block represents a basic block: a list of statements and 63 // expressions that are always evaluated sequentially. 64 // 65 // A block may have 0-2 successors: zero for a return block or a block 66 // that calls a function such as panic that never returns; one for a 67 // normal (jump) block; and two for a conditional (if) block. 68 type Block struct { 69 Nodes []ast.Node // statements, expressions, and ValueSpecs 70 Succs []*Block // successor nodes in the graph 71 72 comment string // for debugging 73 index int32 // index within CFG.Blocks 74 unreachable bool // is block of stmts following return/panic/for{} 75 succs2 [2]*Block // underlying array for Succs 76 } 77 78 // New returns a new control-flow graph for the specified function body, 79 // which must be non-nil. 80 // 81 // The CFG builder calls mayReturn to determine whether a given function 82 // call may return. For example, calls to panic, os.Exit, and log.Fatal 83 // do not return, so the builder can remove infeasible graph edges 84 // following such calls. The builder calls mayReturn only for a 85 // CallExpr beneath an ExprStmt. 86 func New(body *ast.BlockStmt, mayReturn func(*ast.CallExpr) bool) *CFG { 87 b := builder{ 88 mayReturn: mayReturn, 89 cfg: new(CFG), 90 } 91 b.current = b.newBlock("entry") 92 b.stmt(body) 93 94 // Does control fall off the end of the function's body? 95 // Make implicit return explicit. 96 if b.current != nil && !b.current.unreachable { 97 b.add(&ast.ReturnStmt{ 98 Return: body.End() - 1, 99 }) 100 } 101 102 return b.cfg 103 } 104 105 func (b *Block) String() string { 106 return fmt.Sprintf("block %d (%s)", b.index, b.comment) 107 } 108 109 // Return returns the return statement at the end of this block if present, nil otherwise. 110 func (b *Block) Return() (ret *ast.ReturnStmt) { 111 if len(b.Nodes) > 0 { 112 ret, _ = b.Nodes[len(b.Nodes)-1].(*ast.ReturnStmt) 113 } 114 return 115 } 116 117 // Format formats the control-flow graph for ease of debugging. 118 func (g *CFG) Format(fset *token.FileSet) string { 119 var buf bytes.Buffer 120 for _, b := range g.Blocks { 121 fmt.Fprintf(&buf, ".%d: # %s\n", b.index, b.comment) 122 for _, n := range b.Nodes { 123 fmt.Fprintf(&buf, "\t%s\n", formatNode(fset, n)) 124 } 125 if len(b.Succs) > 0 { 126 fmt.Fprintf(&buf, "\tsuccs:") 127 for _, succ := range b.Succs { 128 fmt.Fprintf(&buf, " %d", succ.index) 129 } 130 buf.WriteByte('\n') 131 } 132 buf.WriteByte('\n') 133 } 134 return buf.String() 135 } 136 137 func formatNode(fset *token.FileSet, n ast.Node) string { 138 var buf bytes.Buffer 139 format.Node(&buf, fset, n) 140 // Indent secondary lines by a tab. 141 return string(bytes.Replace(buf.Bytes(), []byte("\n"), []byte("\n\t"), -1)) 142 }