//go:build ignore
// Copyright 2024 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.
// The cfg command prints the control-flow graph of the first function
// or method whose name matches 'funcname' in the specified package.
//
// Usage: cfg package funcname
//
// Example:
//
// $ go build -o cfg ./go/cfg/main.go
// $ cfg ./go/cfg stmt | dot -Tsvg > cfg.svg && open cfg.svg
package main
import (
"flag"
"fmt"
"go/ast"
"log"
"os"
"golang.org/x/tools/go/cfg"
"golang.org/x/tools/go/packages"
)
func main() {
flag.Parse()
if len(flag.Args()) != 2 {
log.Fatal("Usage: package funcname")
}
pattern, funcname := flag.Args()[0], flag.Args()[1]
pkgs, err := packages.Load(&packages.Config{Mode: packages.LoadSyntax}, pattern)
if err != nil {
log.Fatal(err)
}
if packages.PrintErrors(pkgs) > 0 {
os.Exit(1)
}
for _, pkg := range pkgs {
for _, f := range pkg.Syntax {
for _, decl := range f.Decls {
if decl, ok := decl.(*ast.FuncDecl); ok {
if decl.Name.Name == funcname {
g := cfg.New(decl.Body, mayReturn)
fmt.Println(g.Dot(pkg.Fset))
os.Exit(0)
}
}
}
}
}
log.Fatalf("no function %q found in %s", funcname, pattern)
}
// 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
}