--- /dev/null
+// Copyright 2013 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.
+
+// guru: a tool for answering questions about Go source code.
+//
+// http://golang.org/s/using-guru
+//
+// Run with -help flag or help subcommand for usage information.
+//
+package main // import "golang.org/x/tools/cmd/guru"
+
+import (
+ "bufio"
+ "flag"
+ "fmt"
+ "go/build"
+ "go/token"
+ "io"
+ "log"
+ "os"
+ "path/filepath"
+ "runtime"
+ "runtime/pprof"
+ "strings"
+ "sync"
+
+ "golang.org/x/tools/go/buildutil"
+)
+
+// flags
+var (
+ modifiedFlag = flag.Bool("modified", false, "read archive of modified files from standard input")
+ scopeFlag = flag.String("scope", "", "comma-separated list of `packages` the analysis should be limited to")
+ ptalogFlag = flag.String("ptalog", "", "write points-to analysis log to `file`")
+ jsonFlag = flag.Bool("json", false, "emit output in JSON format")
+ reflectFlag = flag.Bool("reflect", false, "analyze reflection soundly (slow)")
+ cpuprofileFlag = flag.String("cpuprofile", "", "write CPU profile to `file`")
+)
+
+func init() {
+ flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc)
+
+ // gccgo does not provide a GOROOT with standard library sources.
+ // If we have one in the environment, force gc mode.
+ if build.Default.Compiler == "gccgo" {
+ if _, err := os.Stat(filepath.Join(runtime.GOROOT(), "src", "runtime", "runtime.go")); err == nil {
+ build.Default.Compiler = "gc"
+ }
+ }
+}
+
+const useHelp = "Run 'guru -help' for more information.\n"
+
+const helpMessage = `Go source code guru.
+Usage: guru [flags] <mode> <position>
+
+The mode argument determines the query to perform:
+
+ callees show possible targets of selected function call
+ callers show possible callers of selected function
+ callstack show path from callgraph root to selected function
+ definition show declaration of selected identifier
+ describe describe selected syntax: definition, methods, etc
+ freevars show free variables of selection
+ implements show 'implements' relation for selected type or method
+ peers show send/receive corresponding to selected channel op
+ pointsto show variables the selected pointer may point to
+ referrers show all refs to entity denoted by selected identifier
+ what show basic information about the selected syntax node
+ whicherrs show possible values of the selected error variable
+
+The position argument specifies the filename and byte offset (or range)
+of the syntax element to query. For example:
+
+ foo.go:#123,#128
+ bar.go:#123
+
+The -json flag causes guru to emit output in JSON format;
+ golang.org/x/tools/cmd/guru/serial defines its schema.
+ Otherwise, the output is in an editor-friendly format in which
+ every line has the form "pos: text", where pos is "-" if unknown.
+
+The -modified flag causes guru to read an archive from standard input.
+ Files in this archive will be used in preference to those in
+ the file system. In this way, a text editor may supply guru
+ with the contents of its unsaved buffers. Each archive entry
+ consists of the file name, a newline, the decimal file size,
+ another newline, and the contents of the file.
+
+The -scope flag restricts analysis to the specified packages.
+ Its value is a comma-separated list of patterns of these forms:
+ golang.org/x/tools/cmd/guru # a single package
+ golang.org/x/tools/... # all packages beneath dir
+ ... # the entire workspace.
+ A pattern preceded by '-' is negative, so the scope
+ encoding/...,-encoding/xml
+ matches all encoding packages except encoding/xml.
+
+User manual: http://golang.org/s/using-guru
+
+Example: describe syntax at offset 530 in this file (an import spec):
+
+ $ guru describe src/golang.org/x/tools/cmd/guru/main.go:#530
+`
+
+func printHelp() {
+ fmt.Fprintln(os.Stderr, helpMessage)
+ fmt.Fprintln(os.Stderr, "Flags:")
+ flag.PrintDefaults()
+}
+
+func main() {
+ log.SetPrefix("guru: ")
+ log.SetFlags(0)
+
+ // Don't print full help unless -help was requested.
+ // Just gently remind users that it's there.
+ flag.Usage = func() { fmt.Fprint(os.Stderr, useHelp) }
+ flag.CommandLine.Init(os.Args[0], flag.ContinueOnError) // hack
+ if err := flag.CommandLine.Parse(os.Args[1:]); err != nil {
+ // (err has already been printed)
+ if err == flag.ErrHelp {
+ printHelp()
+ }
+ os.Exit(2)
+ }
+
+ args := flag.Args()
+ if len(args) != 2 {
+ flag.Usage()
+ os.Exit(2)
+ }
+ mode, posn := args[0], args[1]
+
+ if mode == "help" {
+ printHelp()
+ os.Exit(2)
+ }
+
+ // Set up points-to analysis log file.
+ var ptalog io.Writer
+ if *ptalogFlag != "" {
+ if f, err := os.Create(*ptalogFlag); err != nil {
+ log.Fatalf("Failed to create PTA log file: %s", err)
+ } else {
+ buf := bufio.NewWriter(f)
+ ptalog = buf
+ defer func() {
+ if err := buf.Flush(); err != nil {
+ log.Printf("flush: %s", err)
+ }
+ if err := f.Close(); err != nil {
+ log.Printf("close: %s", err)
+ }
+ }()
+ }
+ }
+
+ // Profiling support.
+ if *cpuprofileFlag != "" {
+ f, err := os.Create(*cpuprofileFlag)
+ if err != nil {
+ log.Fatal(err)
+ }
+ pprof.StartCPUProfile(f)
+ defer pprof.StopCPUProfile()
+ }
+
+ ctxt := &build.Default
+
+ // If there were modified files,
+ // read them from the standard input and
+ // overlay them on the build context.
+ if *modifiedFlag {
+ modified, err := buildutil.ParseOverlayArchive(os.Stdin)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // All I/O done by guru needs to consult the modified map.
+ // The ReadFile done by referrers does,
+ // but the loader's cgo preprocessing currently does not.
+
+ if len(modified) > 0 {
+ ctxt = buildutil.OverlayContext(ctxt, modified)
+ }
+ }
+
+ var outputMu sync.Mutex
+ output := func(fset *token.FileSet, qr QueryResult) {
+ outputMu.Lock()
+ defer outputMu.Unlock()
+ if *jsonFlag {
+ // JSON output
+ fmt.Printf("%s\n", qr.JSON(fset))
+ } else {
+ // plain output
+ printf := func(pos interface{}, format string, args ...interface{}) {
+ fprintf(os.Stdout, fset, pos, format, args...)
+ }
+ qr.PrintPlain(printf)
+ }
+ }
+
+ // Avoid corner case of split("").
+ var scope []string
+ if *scopeFlag != "" {
+ scope = strings.Split(*scopeFlag, ",")
+ }
+
+ // Ask the guru.
+ query := Query{
+ Pos: posn,
+ Build: ctxt,
+ Scope: scope,
+ PTALog: ptalog,
+ Reflection: *reflectFlag,
+ Output: output,
+ }
+
+ if err := Run(mode, &query); err != nil {
+ log.Fatal(err)
+ }
+}