GoPLS Viewer

Home|gopls/go/cfg/cfg.go
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 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.
39package cfg
40
41import (
42    "bytes"
43    "fmt"
44    "go/ast"
45    "go/format"
46    "go/token"
47)
48
49// A CFG represents the control-flow graph of a single function.
50//
51// The entry point is Blocks[0]; there may be multiple return blocks.
52type CFG struct {
53    Blocks []*Block // block[0] is entry; order otherwise undefined
54}
55
56// A Block represents a basic block: a list of statements and
57// expressions that are always evaluated sequentially.
58//
59// A block may have 0-2 successors: zero for a return block or a block
60// that calls a function such as panic that never returns; one for a
61// normal (jump) block; and two for a conditional (if) block.
62type Block struct {
63    Nodes []ast.Node // statements, expressions, and ValueSpecs
64    Succs []*Block   // successor nodes in the graph
65    Index int32      // index within CFG.Blocks
66    Live  bool       // block is reachable from entry
67
68    comment string    // for debugging
69    succs2  [2]*Block // underlying array for Succs
70}
71
72// New returns a new control-flow graph for the specified function body,
73// which must be non-nil.
74//
75// The CFG builder calls mayReturn to determine whether a given function
76// call may return.  For example, calls to panic, os.Exit, and log.Fatal
77// do not return, so the builder can remove infeasible graph edges
78// following such calls.  The builder calls mayReturn only for a
79// CallExpr beneath an ExprStmt.
80func New(body *ast.BlockStmtmayReturn func(*ast.CallExprbool) *CFG {
81    b := builder{
82        mayReturnmayReturn,
83        cfg:       new(CFG),
84    }
85    b.current = b.newBlock("entry")
86    b.stmt(body)
87
88    // Compute liveness (reachability from entry point), breadth-first.
89    q := make([]*Block0len(b.cfg.Blocks))
90    q = append(qb.cfg.Blocks[0]) // entry point
91    for len(q) > 0 {
92        b := q[len(q)-1]
93        q = q[:len(q)-1]
94
95        if !b.Live {
96            b.Live = true
97            q = append(qb.Succs...)
98        }
99    }
100
101    // Does control fall off the end of the function's body?
102    // Make implicit return explicit.
103    if b.current != nil && b.current.Live {
104        b.add(&ast.ReturnStmt{
105            Returnbody.End() - 1,
106        })
107    }
108
109    return b.cfg
110}
111
112func (b *BlockString() string {
113    return fmt.Sprintf("block %d (%s)"b.Indexb.comment)
114}
115
116// Return returns the return statement at the end of this block if present, nil otherwise.
117func (b *BlockReturn() (ret *ast.ReturnStmt) {
118    if len(b.Nodes) > 0 {
119        ret_ = b.Nodes[len(b.Nodes)-1].(*ast.ReturnStmt)
120    }
121    return
122}
123
124// Format formats the control-flow graph for ease of debugging.
125func (g *CFGFormat(fset *token.FileSetstring {
126    var buf bytes.Buffer
127    for _b := range g.Blocks {
128        fmt.Fprintf(&buf".%d: # %s\n"b.Indexb.comment)
129        for _n := range b.Nodes {
130            fmt.Fprintf(&buf"\t%s\n"formatNode(fsetn))
131        }
132        if len(b.Succs) > 0 {
133            fmt.Fprintf(&buf"\tsuccs:")
134            for _succ := range b.Succs {
135                fmt.Fprintf(&buf" %d"succ.Index)
136            }
137            buf.WriteByte('\n')
138        }
139        buf.WriteByte('\n')
140    }
141    return buf.String()
142}
143
144func formatNode(fset *token.FileSetn ast.Nodestring {
145    var buf bytes.Buffer
146    format.Node(&buffsetn)
147    // Indent secondary lines by a tab.
148    return string(bytes.Replace(buf.Bytes(), []byte("\n"), []byte("\n\t"), -1))
149}
150
MembersX
CFG.Format.RangeStmt_3689.b
formatNode
Block.Index
New
New.mayReturn
New.q
CFG.Format.buf
CFG.Format.RangeStmt_3689.BlockStmt.RangeStmt_3775.n
bytes
CFG.Blocks
Block.String
CFG.Format.g
CFG.Format
Block
Block.Nodes
Block.Live
New.b
Block.succs2
Block.Return.ret
CFG.Format.RangeStmt_3689.BlockStmt.BlockStmt.RangeStmt_3919.succ
formatNode.buf
format
CFG
Block.comment
Block.String.b
Block.Succs
New.body
Block.Return.b
CFG.Format.fset
formatNode.n
Block.Return
formatNode.fset
Members
X