Documentation ¶
Overview ¶
Package dataflow provides data flow analyses that can be performed on a previously constructed control flow graph, including a reaching definitions analysis and a live variables analysis for local variables.
Index ¶
- func DefUse(cfg *cfg.CFG, info *packages.Package) map[ast.Stmt]map[ast.Stmt]struct{}
- func DefsReaching(stmt ast.Stmt, cfg *cfg.CFG, info *packages.Package) map[ast.Stmt]struct{}
- func LiveVars(cfg *cfg.CFG, info *packages.Package) (in, out map[ast.Stmt]map[*types.Var]struct{})
- func PrintDefUseDot(f io.Writer, fset *token.FileSet, info *packages.Package, cfg *cfg.CFG)
- func PrintLiveVarsDot(f io.Writer, fset *token.FileSet, info *packages.Package, cfg *cfg.CFG)
- func ReferencedVars(stmts []ast.Stmt, info *packages.Package) (asgt, updt, decl, use map[*types.Var]struct{})
- func Vars(node ast.Node, info *packages.Package) map[*types.Var]struct{}
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func DefUse ¶
DefUse builds reaching definitions for a given control flow graph, returning a map that maps each statement that defines a variable (i.e., declares or assigns it) to the set of statements that use that variable.
Note: An assignment to a struct field or array element is treated as both a use and a definition of that variable, since only part of its value is assigned. For analysis purposes, it's treated as though the entire value is read, then part of it is modified, then the entire value is assigned back to the variable. (This is necessary for the analysis to produce correct results.)
No nodes from the cfg.Defers list will be returned in the output of this function as they are disjoint from a cfg's blocks. For analyzing the statements in the cfg.Defers list, each defer should be treated as though it has the same in and out sets as the cfg.Exit node.
func DefsReaching ¶
DefsReaching builds reaching definitions for a given control flow graph, returning the set of statements that define a variable (i.e., declare or assign it) where that definition reaches the given statement.
Example ¶
package main import ( "fmt" "go/ast" "github.com/godoctor/godoctor/analysis/cfg" "github.com/godoctor/godoctor/analysis/dataflow" "github.com/godoctor/godoctor/analysis/loader" "golang.org/x/tools/go/packages" ) func main() { src := ` package main import "fmt" func main() { a := 1 b := 2 c := 3 a := b a, b := b, a c := a + b } ` // can ignore overlay stuff, you can just load from disk normally var config packages.Config config.Dir = "testdata" config.Overlay = map[string][]byte{"main.go": []byte(src)} var laterError error prog, err := loader.Load(&config, func(err error) { laterError = err }) if err != nil { return } if laterError != nil { return } var info *packages.Package for _, info = range prog.AllPackages { if info.Name == "main" { break } } funcOne := info.Syntax[0].Decls[1].(*ast.FuncDecl) c := cfg.FromFunc(funcOne) du := dataflow.DefUse(c, info) ast.Inspect(info.Syntax[0], func(n ast.Node) bool { switch stmt := n.(type) { case ast.Stmt: fmt.Println(len(du[stmt])) // do as you please } return true }) }
Output:
func LiveVars ¶
LiveAt returns the in and out set of live variables for each block in a given control flow graph (cfg) in the context of a loader.Program, including the cfg.Entry and cfg.Exit nodes.
The traditional approach of holding the live variables at the exit node to the empty set has been deviated from in order to handle defers. The live variables in set of the cfg.Exit node will be set to the variables used in all cfg.Defers. No liveness is analyzed for the cfg.Defers themselves.
More formally:
IN[EXIT] = USE(each d in cfg.Defers) OUT[EXIT] = {}
Example ¶
package main import ( "go/ast" "github.com/godoctor/godoctor/analysis/cfg" "github.com/godoctor/godoctor/analysis/dataflow" "github.com/godoctor/godoctor/analysis/loader" "golang.org/x/tools/go/packages" ) func main() { src := ` package main import "fmt" func main() { a := 1 b := 2 c := 3 a := b a, b := b, a c := a + b } ` // can ignore overlay stuff, you can just load from disk normally var config packages.Config config.Dir = "testdata" config.Overlay = map[string][]byte{"main.go": []byte(src)} var laterError error prog, err := loader.Load(&config, func(err error) { laterError = err }) if err != nil { return } if laterError != nil { return } var info *packages.Package for _, info = range prog.AllPackages { if info.Name == "main" { break } } funcOne := info.Syntax[0].Decls[1].(*ast.FuncDecl) cfg := cfg.FromFunc(funcOne) in, out := dataflow.LiveVars(cfg, info) ast.Inspect(info.Syntax[0], func(n ast.Node) bool { switch stmt := n.(type) { case ast.Stmt: _, _ = in[stmt], out[stmt] // do as you please } return true }) }
Output:
func PrintDefUseDot ¶
PrintDefUseDot prints a GraphViz DOT file showing the control flow graph with definition-use links superimposed.
This is used by the debug refactoring.
func PrintLiveVarsDot ¶
PrintLiveVarsDot prints a GraphViz DOT file showing the control flow graph with information about the liveness of local variables superimposed.
This is used by the debug refactoring.
Types ¶
This section is empty.