Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / godoc / analysis / analysis.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201105173854-bc9fc8d8c4bc/godoc/analysis/analysis.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201105173854-bc9fc8d8c4bc/godoc/analysis/analysis.go
new file mode 100644 (file)
index 0000000..b79286c
--- /dev/null
@@ -0,0 +1,613 @@
+// Copyright 2014 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.
+
+// Package analysis performs type and pointer analysis
+// and generates mark-up for the Go source view.
+//
+// The Run method populates a Result object by running type and
+// (optionally) pointer analysis.  The Result object is thread-safe
+// and at all times may be accessed by a serving thread, even as it is
+// progressively populated as analysis facts are derived.
+//
+// The Result is a mapping from each godoc file URL
+// (e.g. /src/fmt/print.go) to information about that file.  The
+// information is a list of HTML markup links and a JSON array of
+// structured data values.  Some of the links call client-side
+// JavaScript functions that index this array.
+//
+// The analysis computes mark-up for the following relations:
+//
+// IMPORTS: for each ast.ImportSpec, the package that it denotes.
+//
+// RESOLUTION: for each ast.Ident, its kind and type, and the location
+// of its definition.
+//
+// METHOD SETS, IMPLEMENTS: for each ast.Ident defining a named type,
+// its method-set, the set of interfaces it implements or is
+// implemented by, and its size/align values.
+//
+// CALLERS, CALLEES: for each function declaration ('func' token), its
+// callers, and for each call-site ('(' token), its callees.
+//
+// CALLGRAPH: the package docs include an interactive viewer for the
+// intra-package call graph of "fmt".
+//
+// CHANNEL PEERS: for each channel operation make/<-/close, the set of
+// other channel ops that alias the same channel(s).
+//
+// ERRORS: for each locus of a frontend (scanner/parser/type) error, the
+// location is highlighted in red and hover text provides the compiler
+// error message.
+//
+package analysis // import "golang.org/x/tools/godoc/analysis"
+
+import (
+       "fmt"
+       "go/build"
+       "go/scanner"
+       "go/token"
+       "go/types"
+       "html"
+       "io"
+       "log"
+       "os"
+       "path/filepath"
+       "sort"
+       "strings"
+       "sync"
+
+       "golang.org/x/tools/go/loader"
+       "golang.org/x/tools/go/pointer"
+       "golang.org/x/tools/go/ssa"
+       "golang.org/x/tools/go/ssa/ssautil"
+)
+
+// -- links ------------------------------------------------------------
+
+// A Link is an HTML decoration of the bytes [Start, End) of a file.
+// Write is called before/after those bytes to emit the mark-up.
+type Link interface {
+       Start() int
+       End() int
+       Write(w io.Writer, _ int, start bool) // the godoc.LinkWriter signature
+}
+
+// An <a> element.
+type aLink struct {
+       start, end int    // =godoc.Segment
+       title      string // hover text
+       onclick    string // JS code (NB: trusted)
+       href       string // URL     (NB: trusted)
+}
+
+func (a aLink) Start() int { return a.start }
+func (a aLink) End() int   { return a.end }
+func (a aLink) Write(w io.Writer, _ int, start bool) {
+       if start {
+               fmt.Fprintf(w, `<a title='%s'`, html.EscapeString(a.title))
+               if a.onclick != "" {
+                       fmt.Fprintf(w, ` onclick='%s'`, html.EscapeString(a.onclick))
+               }
+               if a.href != "" {
+                       // TODO(adonovan): I think that in principle, a.href must first be
+                       // url.QueryEscape'd, but if I do that, a leading slash becomes "%2F",
+                       // which causes the browser to treat the path as relative, not absolute.
+                       // WTF?
+                       fmt.Fprintf(w, ` href='%s'`, html.EscapeString(a.href))
+               }
+               fmt.Fprintf(w, ">")
+       } else {
+               fmt.Fprintf(w, "</a>")
+       }
+}
+
+// An <a class='error'> element.
+type errorLink struct {
+       start int
+       msg   string
+}
+
+func (e errorLink) Start() int { return e.start }
+func (e errorLink) End() int   { return e.start + 1 }
+
+func (e errorLink) Write(w io.Writer, _ int, start bool) {
+       // <span> causes havoc, not sure why, so use <a>.
+       if start {
+               fmt.Fprintf(w, `<a class='error' title='%s'>`, html.EscapeString(e.msg))
+       } else {
+               fmt.Fprintf(w, "</a>")
+       }
+}
+
+// -- fileInfo ---------------------------------------------------------
+
+// FileInfo holds analysis information for the source file view.
+// Clients must not mutate it.
+type FileInfo struct {
+       Data  []interface{} // JSON serializable values
+       Links []Link        // HTML link markup
+}
+
+// A fileInfo is the server's store of hyperlinks and JSON data for a
+// particular file.
+type fileInfo struct {
+       mu        sync.Mutex
+       data      []interface{} // JSON objects
+       links     []Link
+       sorted    bool
+       hasErrors bool // TODO(adonovan): surface this in the UI
+}
+
+// addLink adds a link to the Go source file fi.
+func (fi *fileInfo) addLink(link Link) {
+       fi.mu.Lock()
+       fi.links = append(fi.links, link)
+       fi.sorted = false
+       if _, ok := link.(errorLink); ok {
+               fi.hasErrors = true
+       }
+       fi.mu.Unlock()
+}
+
+// addData adds the structured value x to the JSON data for the Go
+// source file fi.  Its index is returned.
+func (fi *fileInfo) addData(x interface{}) int {
+       fi.mu.Lock()
+       index := len(fi.data)
+       fi.data = append(fi.data, x)
+       fi.mu.Unlock()
+       return index
+}
+
+// get returns the file info in external form.
+// Callers must not mutate its fields.
+func (fi *fileInfo) get() FileInfo {
+       var r FileInfo
+       // Copy slices, to avoid races.
+       fi.mu.Lock()
+       r.Data = append(r.Data, fi.data...)
+       if !fi.sorted {
+               sort.Sort(linksByStart(fi.links))
+               fi.sorted = true
+       }
+       r.Links = append(r.Links, fi.links...)
+       fi.mu.Unlock()
+       return r
+}
+
+// PackageInfo holds analysis information for the package view.
+// Clients must not mutate it.
+type PackageInfo struct {
+       CallGraph      []*PCGNodeJSON
+       CallGraphIndex map[string]int
+       Types          []*TypeInfoJSON
+}
+
+type pkgInfo struct {
+       mu             sync.Mutex
+       callGraph      []*PCGNodeJSON
+       callGraphIndex map[string]int  // keys are (*ssa.Function).RelString()
+       types          []*TypeInfoJSON // type info for exported types
+}
+
+func (pi *pkgInfo) setCallGraph(callGraph []*PCGNodeJSON, callGraphIndex map[string]int) {
+       pi.mu.Lock()
+       pi.callGraph = callGraph
+       pi.callGraphIndex = callGraphIndex
+       pi.mu.Unlock()
+}
+
+func (pi *pkgInfo) addType(t *TypeInfoJSON) {
+       pi.mu.Lock()
+       pi.types = append(pi.types, t)
+       pi.mu.Unlock()
+}
+
+// get returns the package info in external form.
+// Callers must not mutate its fields.
+func (pi *pkgInfo) get() PackageInfo {
+       var r PackageInfo
+       // Copy slices, to avoid races.
+       pi.mu.Lock()
+       r.CallGraph = append(r.CallGraph, pi.callGraph...)
+       r.CallGraphIndex = pi.callGraphIndex
+       r.Types = append(r.Types, pi.types...)
+       pi.mu.Unlock()
+       return r
+}
+
+// -- Result -----------------------------------------------------------
+
+// Result contains the results of analysis.
+// The result contains a mapping from filenames to a set of HTML links
+// and JavaScript data referenced by the links.
+type Result struct {
+       mu        sync.Mutex           // guards maps (but not their contents)
+       status    string               // global analysis status
+       fileInfos map[string]*fileInfo // keys are godoc file URLs
+       pkgInfos  map[string]*pkgInfo  // keys are import paths
+}
+
+// fileInfo returns the fileInfo for the specified godoc file URL,
+// constructing it as needed.  Thread-safe.
+func (res *Result) fileInfo(url string) *fileInfo {
+       res.mu.Lock()
+       fi, ok := res.fileInfos[url]
+       if !ok {
+               if res.fileInfos == nil {
+                       res.fileInfos = make(map[string]*fileInfo)
+               }
+               fi = new(fileInfo)
+               res.fileInfos[url] = fi
+       }
+       res.mu.Unlock()
+       return fi
+}
+
+// Status returns a human-readable description of the current analysis status.
+func (res *Result) Status() string {
+       res.mu.Lock()
+       defer res.mu.Unlock()
+       return res.status
+}
+
+func (res *Result) setStatusf(format string, args ...interface{}) {
+       res.mu.Lock()
+       res.status = fmt.Sprintf(format, args...)
+       log.Printf(format, args...)
+       res.mu.Unlock()
+}
+
+// FileInfo returns new slices containing opaque JSON values and the
+// HTML link markup for the specified godoc file URL.  Thread-safe.
+// Callers must not mutate the elements.
+// It returns "zero" if no data is available.
+//
+func (res *Result) FileInfo(url string) (fi FileInfo) {
+       return res.fileInfo(url).get()
+}
+
+// pkgInfo returns the pkgInfo for the specified import path,
+// constructing it as needed.  Thread-safe.
+func (res *Result) pkgInfo(importPath string) *pkgInfo {
+       res.mu.Lock()
+       pi, ok := res.pkgInfos[importPath]
+       if !ok {
+               if res.pkgInfos == nil {
+                       res.pkgInfos = make(map[string]*pkgInfo)
+               }
+               pi = new(pkgInfo)
+               res.pkgInfos[importPath] = pi
+       }
+       res.mu.Unlock()
+       return pi
+}
+
+// PackageInfo returns new slices of JSON values for the callgraph and
+// type info for the specified package.  Thread-safe.
+// Callers must not mutate its fields.
+// PackageInfo returns "zero" if no data is available.
+//
+func (res *Result) PackageInfo(importPath string) PackageInfo {
+       return res.pkgInfo(importPath).get()
+}
+
+// -- analysis ---------------------------------------------------------
+
+type analysis struct {
+       result    *Result
+       prog      *ssa.Program
+       ops       []chanOp       // all channel ops in program
+       allNamed  []*types.Named // all "defined" (formerly "named") types in the program
+       ptaConfig pointer.Config
+       path2url  map[string]string // maps openable path to godoc file URL (/src/fmt/print.go)
+       pcgs      map[*ssa.Package]*packageCallGraph
+}
+
+// fileAndOffset returns the file and offset for a given pos.
+func (a *analysis) fileAndOffset(pos token.Pos) (fi *fileInfo, offset int) {
+       return a.fileAndOffsetPosn(a.prog.Fset.Position(pos))
+}
+
+// fileAndOffsetPosn returns the file and offset for a given position.
+func (a *analysis) fileAndOffsetPosn(posn token.Position) (fi *fileInfo, offset int) {
+       url := a.path2url[posn.Filename]
+       return a.result.fileInfo(url), posn.Offset
+}
+
+// posURL returns the URL of the source extent [pos, pos+len).
+func (a *analysis) posURL(pos token.Pos, len int) string {
+       if pos == token.NoPos {
+               return ""
+       }
+       posn := a.prog.Fset.Position(pos)
+       url := a.path2url[posn.Filename]
+       return fmt.Sprintf("%s?s=%d:%d#L%d",
+               url, posn.Offset, posn.Offset+len, posn.Line)
+}
+
+// ----------------------------------------------------------------------
+
+// Run runs program analysis and computes the resulting markup,
+// populating *result in a thread-safe manner, first with type
+// information then later with pointer analysis information if
+// enabled by the pta flag.
+//
+func Run(pta bool, result *Result) {
+       conf := loader.Config{
+               AllowErrors: true,
+       }
+
+       // Silence the default error handler.
+       // Don't print all errors; we'll report just
+       // one per errant package later.
+       conf.TypeChecker.Error = func(e error) {}
+
+       var roots, args []string // roots[i] ends with os.PathSeparator
+
+       // Enumerate packages in $GOROOT.
+       root := filepath.Join(build.Default.GOROOT, "src") + string(os.PathSeparator)
+       roots = append(roots, root)
+       args = allPackages(root)
+       log.Printf("GOROOT=%s: %s\n", root, args)
+
+       // Enumerate packages in $GOPATH.
+       for i, dir := range filepath.SplitList(build.Default.GOPATH) {
+               root := filepath.Join(dir, "src") + string(os.PathSeparator)
+               roots = append(roots, root)
+               pkgs := allPackages(root)
+               log.Printf("GOPATH[%d]=%s: %s\n", i, root, pkgs)
+               args = append(args, pkgs...)
+       }
+
+       // Uncomment to make startup quicker during debugging.
+       //args = []string{"golang.org/x/tools/cmd/godoc"}
+       //args = []string{"fmt"}
+
+       if _, err := conf.FromArgs(args, true); err != nil {
+               // TODO(adonovan): degrade gracefully, not fail totally.
+               // (The crippling case is a parse error in an external test file.)
+               result.setStatusf("Analysis failed: %s.", err) // import error
+               return
+       }
+
+       result.setStatusf("Loading and type-checking packages...")
+       iprog, err := conf.Load()
+       if iprog != nil {
+               // Report only the first error of each package.
+               for _, info := range iprog.AllPackages {
+                       for _, err := range info.Errors {
+                               fmt.Fprintln(os.Stderr, err)
+                               break
+                       }
+               }
+               log.Printf("Loaded %d packages.", len(iprog.AllPackages))
+       }
+       if err != nil {
+               result.setStatusf("Loading failed: %s.\n", err)
+               return
+       }
+
+       // Create SSA-form program representation.
+       // Only the transitively error-free packages are used.
+       prog := ssautil.CreateProgram(iprog, ssa.GlobalDebug)
+
+       // Create a "testmain" package for each package with tests.
+       for _, pkg := range prog.AllPackages() {
+               if testmain := prog.CreateTestMainPackage(pkg); testmain != nil {
+                       log.Printf("Adding tests for %s", pkg.Pkg.Path())
+               }
+       }
+
+       // Build SSA code for bodies of all functions in the whole program.
+       result.setStatusf("Constructing SSA form...")
+       prog.Build()
+       log.Print("SSA construction complete")
+
+       a := analysis{
+               result: result,
+               prog:   prog,
+               pcgs:   make(map[*ssa.Package]*packageCallGraph),
+       }
+
+       // Build a mapping from openable filenames to godoc file URLs,
+       // i.e. "/src/" plus path relative to GOROOT/src or GOPATH[i]/src.
+       a.path2url = make(map[string]string)
+       for _, info := range iprog.AllPackages {
+       nextfile:
+               for _, f := range info.Files {
+                       if f.Pos() == 0 {
+                               continue // e.g. files generated by cgo
+                       }
+                       abs := iprog.Fset.File(f.Pos()).Name()
+                       // Find the root to which this file belongs.
+                       for _, root := range roots {
+                               rel := strings.TrimPrefix(abs, root)
+                               if len(rel) < len(abs) {
+                                       a.path2url[abs] = "/src/" + filepath.ToSlash(rel)
+                                       continue nextfile
+                               }
+                       }
+
+                       log.Printf("Can't locate file %s (package %q) beneath any root",
+                               abs, info.Pkg.Path())
+               }
+       }
+
+       // Add links for scanner, parser, type-checker errors.
+       // TODO(adonovan): fix: these links can overlap with
+       // identifier markup, causing the renderer to emit some
+       // characters twice.
+       errors := make(map[token.Position][]string)
+       for _, info := range iprog.AllPackages {
+               for _, err := range info.Errors {
+                       switch err := err.(type) {
+                       case types.Error:
+                               posn := a.prog.Fset.Position(err.Pos)
+                               errors[posn] = append(errors[posn], err.Msg)
+                       case scanner.ErrorList:
+                               for _, e := range err {
+                                       errors[e.Pos] = append(errors[e.Pos], e.Msg)
+                               }
+                       default:
+                               log.Printf("Package %q has error (%T) without position: %v\n",
+                                       info.Pkg.Path(), err, err)
+                       }
+               }
+       }
+       for posn, errs := range errors {
+               fi, offset := a.fileAndOffsetPosn(posn)
+               fi.addLink(errorLink{
+                       start: offset,
+                       msg:   strings.Join(errs, "\n"),
+               })
+       }
+
+       // ---------- type-based analyses ----------
+
+       // Compute the all-pairs IMPLEMENTS relation.
+       // Collect all named types, even local types
+       // (which can have methods via promotion)
+       // and the built-in "error".
+       errorType := types.Universe.Lookup("error").Type().(*types.Named)
+       a.allNamed = append(a.allNamed, errorType)
+       for _, info := range iprog.AllPackages {
+               for _, obj := range info.Defs {
+                       if obj, ok := obj.(*types.TypeName); ok {
+                               if named, ok := obj.Type().(*types.Named); ok {
+                                       a.allNamed = append(a.allNamed, named)
+                               }
+                       }
+               }
+       }
+       log.Print("Computing implements relation...")
+       facts := computeImplements(&a.prog.MethodSets, a.allNamed)
+
+       // Add the type-based analysis results.
+       log.Print("Extracting type info...")
+       for _, info := range iprog.AllPackages {
+               a.doTypeInfo(info, facts)
+       }
+
+       a.visitInstrs(pta)
+
+       result.setStatusf("Type analysis complete.")
+
+       if pta {
+               mainPkgs := ssautil.MainPackages(prog.AllPackages())
+               log.Print("Transitively error-free main packages: ", mainPkgs)
+               a.pointer(mainPkgs)
+       }
+}
+
+// visitInstrs visits all SSA instructions in the program.
+func (a *analysis) visitInstrs(pta bool) {
+       log.Print("Visit instructions...")
+       for fn := range ssautil.AllFunctions(a.prog) {
+               for _, b := range fn.Blocks {
+                       for _, instr := range b.Instrs {
+                               // CALLEES (static)
+                               // (Dynamic calls require pointer analysis.)
+                               //
+                               // We use the SSA representation to find the static callee,
+                               // since in many cases it does better than the
+                               // types.Info.{Refs,Selection} information.  For example:
+                               //
+                               //   defer func(){}()      // static call to anon function
+                               //   f := func(){}; f()    // static call to anon function
+                               //   f := fmt.Println; f() // static call to named function
+                               //
+                               // The downside is that we get no static callee information
+                               // for packages that (transitively) contain errors.
+                               if site, ok := instr.(ssa.CallInstruction); ok {
+                                       if callee := site.Common().StaticCallee(); callee != nil {
+                                               // TODO(adonovan): callgraph: elide wrappers.
+                                               // (Do static calls ever go to wrappers?)
+                                               if site.Common().Pos() != token.NoPos {
+                                                       a.addCallees(site, []*ssa.Function{callee})
+                                               }
+                                       }
+                               }
+
+                               if !pta {
+                                       continue
+                               }
+
+                               // CHANNEL PEERS
+                               // Collect send/receive/close instructions in the whole ssa.Program.
+                               for _, op := range chanOps(instr) {
+                                       a.ops = append(a.ops, op)
+                                       a.ptaConfig.AddQuery(op.ch) // add channel ssa.Value to PTA query
+                               }
+                       }
+               }
+       }
+       log.Print("Visit instructions complete")
+}
+
+// pointer runs the pointer analysis.
+func (a *analysis) pointer(mainPkgs []*ssa.Package) {
+       // Run the pointer analysis and build the complete callgraph.
+       a.ptaConfig.Mains = mainPkgs
+       a.ptaConfig.BuildCallGraph = true
+       a.ptaConfig.Reflection = false // (for now)
+
+       a.result.setStatusf("Pointer analysis running...")
+
+       ptares, err := pointer.Analyze(&a.ptaConfig)
+       if err != nil {
+               // If this happens, it indicates a bug.
+               a.result.setStatusf("Pointer analysis failed: %s.", err)
+               return
+       }
+       log.Print("Pointer analysis complete.")
+
+       // Add the results of pointer analysis.
+
+       a.result.setStatusf("Computing channel peers...")
+       a.doChannelPeers(ptares.Queries)
+       a.result.setStatusf("Computing dynamic call graph edges...")
+       a.doCallgraph(ptares.CallGraph)
+
+       a.result.setStatusf("Analysis complete.")
+}
+
+type linksByStart []Link
+
+func (a linksByStart) Less(i, j int) bool { return a[i].Start() < a[j].Start() }
+func (a linksByStart) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
+func (a linksByStart) Len() int           { return len(a) }
+
+// allPackages returns a new sorted slice of all packages beneath the
+// specified package root directory, e.g. $GOROOT/src or $GOPATH/src.
+// Derived from from go/ssa/stdlib_test.go
+// root must end with os.PathSeparator.
+//
+// TODO(adonovan): use buildutil.AllPackages when the tree thaws.
+func allPackages(root string) []string {
+       var pkgs []string
+       filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
+               if info == nil {
+                       return nil // non-existent root directory?
+               }
+               if !info.IsDir() {
+                       return nil // not a directory
+               }
+               // Prune the search if we encounter any of these names:
+               base := filepath.Base(path)
+               if base == "testdata" || strings.HasPrefix(base, ".") {
+                       return filepath.SkipDir
+               }
+               pkg := filepath.ToSlash(strings.TrimPrefix(path, root))
+               switch pkg {
+               case "builtin":
+                       return filepath.SkipDir
+               case "":
+                       return nil // ignore root of tree
+               }
+               pkgs = append(pkgs, pkg)
+               return nil
+       })
+       return pkgs
+}