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  }