github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/godoc/analysis/analysis.go (about)

     1  // Copyright 2014 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  // Package analysis performs type and pointer analysis
     6  // and generates mark-up for the Go source view.
     7  //
     8  // The Run method populates a Result object by running type and
     9  // (optionally) pointer analysis.  The Result object is thread-safe
    10  // and at all times may be accessed by a serving thread, even as it is
    11  // progressively populated as analysis facts are derived.
    12  //
    13  // The Result is a mapping from each godoc file URL
    14  // (e.g. /src/fmt/print.go) to information about that file.  The
    15  // information is a list of HTML markup links and a JSON array of
    16  // structured data values.  Some of the links call client-side
    17  // JavaScript functions that index this array.
    18  //
    19  // The analysis computes mark-up for the following relations:
    20  //
    21  // IMPORTS: for each ast.ImportSpec, the package that it denotes.
    22  //
    23  // RESOLUTION: for each ast.Ident, its kind and type, and the location
    24  // of its definition.
    25  //
    26  // METHOD SETS, IMPLEMENTS: for each ast.Ident defining a named type,
    27  // its method-set, the set of interfaces it implements or is
    28  // implemented by, and its size/align values.
    29  //
    30  // CALLERS, CALLEES: for each function declaration ('func' token), its
    31  // callers, and for each call-site ('(' token), its callees.
    32  //
    33  // CALLGRAPH: the package docs include an interactive viewer for the
    34  // intra-package call graph of "fmt".
    35  //
    36  // CHANNEL PEERS: for each channel operation make/<-/close, the set of
    37  // other channel ops that alias the same channel(s).
    38  //
    39  // ERRORS: for each locus of a frontend (scanner/parser/type) error, the
    40  // location is highlighted in red and hover text provides the compiler
    41  // error message.
    42  //
    43  package analysis // import "github.com/powerman/golang-tools/godoc/analysis"
    44  
    45  import (
    46  	"io"
    47  	"sort"
    48  	"sync"
    49  )
    50  
    51  // -- links ------------------------------------------------------------
    52  
    53  // A Link is an HTML decoration of the bytes [Start, End) of a file.
    54  // Write is called before/after those bytes to emit the mark-up.
    55  type Link interface {
    56  	Start() int
    57  	End() int
    58  	Write(w io.Writer, _ int, start bool) // the godoc.LinkWriter signature
    59  }
    60  
    61  // -- fileInfo ---------------------------------------------------------
    62  
    63  // FileInfo holds analysis information for the source file view.
    64  // Clients must not mutate it.
    65  type FileInfo struct {
    66  	Data  []interface{} // JSON serializable values
    67  	Links []Link        // HTML link markup
    68  }
    69  
    70  // A fileInfo is the server's store of hyperlinks and JSON data for a
    71  // particular file.
    72  type fileInfo struct {
    73  	mu        sync.Mutex
    74  	data      []interface{} // JSON objects
    75  	links     []Link
    76  	sorted    bool
    77  	hasErrors bool // TODO(adonovan): surface this in the UI
    78  }
    79  
    80  // get returns the file info in external form.
    81  // Callers must not mutate its fields.
    82  func (fi *fileInfo) get() FileInfo {
    83  	var r FileInfo
    84  	// Copy slices, to avoid races.
    85  	fi.mu.Lock()
    86  	r.Data = append(r.Data, fi.data...)
    87  	if !fi.sorted {
    88  		sort.Sort(linksByStart(fi.links))
    89  		fi.sorted = true
    90  	}
    91  	r.Links = append(r.Links, fi.links...)
    92  	fi.mu.Unlock()
    93  	return r
    94  }
    95  
    96  // PackageInfo holds analysis information for the package view.
    97  // Clients must not mutate it.
    98  type PackageInfo struct {
    99  	CallGraph      []*PCGNodeJSON
   100  	CallGraphIndex map[string]int
   101  	Types          []*TypeInfoJSON
   102  }
   103  
   104  type pkgInfo struct {
   105  	mu             sync.Mutex
   106  	callGraph      []*PCGNodeJSON
   107  	callGraphIndex map[string]int  // keys are (*ssa.Function).RelString()
   108  	types          []*TypeInfoJSON // type info for exported types
   109  }
   110  
   111  // get returns the package info in external form.
   112  // Callers must not mutate its fields.
   113  func (pi *pkgInfo) get() PackageInfo {
   114  	var r PackageInfo
   115  	// Copy slices, to avoid races.
   116  	pi.mu.Lock()
   117  	r.CallGraph = append(r.CallGraph, pi.callGraph...)
   118  	r.CallGraphIndex = pi.callGraphIndex
   119  	r.Types = append(r.Types, pi.types...)
   120  	pi.mu.Unlock()
   121  	return r
   122  }
   123  
   124  // -- Result -----------------------------------------------------------
   125  
   126  // Result contains the results of analysis.
   127  // The result contains a mapping from filenames to a set of HTML links
   128  // and JavaScript data referenced by the links.
   129  type Result struct {
   130  	mu        sync.Mutex           // guards maps (but not their contents)
   131  	status    string               // global analysis status
   132  	fileInfos map[string]*fileInfo // keys are godoc file URLs
   133  	pkgInfos  map[string]*pkgInfo  // keys are import paths
   134  }
   135  
   136  // fileInfo returns the fileInfo for the specified godoc file URL,
   137  // constructing it as needed.  Thread-safe.
   138  func (res *Result) fileInfo(url string) *fileInfo {
   139  	res.mu.Lock()
   140  	fi, ok := res.fileInfos[url]
   141  	if !ok {
   142  		if res.fileInfos == nil {
   143  			res.fileInfos = make(map[string]*fileInfo)
   144  		}
   145  		fi = new(fileInfo)
   146  		res.fileInfos[url] = fi
   147  	}
   148  	res.mu.Unlock()
   149  	return fi
   150  }
   151  
   152  // Status returns a human-readable description of the current analysis status.
   153  func (res *Result) Status() string {
   154  	res.mu.Lock()
   155  	defer res.mu.Unlock()
   156  	return res.status
   157  }
   158  
   159  // FileInfo returns new slices containing opaque JSON values and the
   160  // HTML link markup for the specified godoc file URL.  Thread-safe.
   161  // Callers must not mutate the elements.
   162  // It returns "zero" if no data is available.
   163  //
   164  func (res *Result) FileInfo(url string) (fi FileInfo) {
   165  	return res.fileInfo(url).get()
   166  }
   167  
   168  // pkgInfo returns the pkgInfo for the specified import path,
   169  // constructing it as needed.  Thread-safe.
   170  func (res *Result) pkgInfo(importPath string) *pkgInfo {
   171  	res.mu.Lock()
   172  	pi, ok := res.pkgInfos[importPath]
   173  	if !ok {
   174  		if res.pkgInfos == nil {
   175  			res.pkgInfos = make(map[string]*pkgInfo)
   176  		}
   177  		pi = new(pkgInfo)
   178  		res.pkgInfos[importPath] = pi
   179  	}
   180  	res.mu.Unlock()
   181  	return pi
   182  }
   183  
   184  // PackageInfo returns new slices of JSON values for the callgraph and
   185  // type info for the specified package.  Thread-safe.
   186  // Callers must not mutate its fields.
   187  // PackageInfo returns "zero" if no data is available.
   188  //
   189  func (res *Result) PackageInfo(importPath string) PackageInfo {
   190  	return res.pkgInfo(importPath).get()
   191  }
   192  
   193  type linksByStart []Link
   194  
   195  func (a linksByStart) Less(i, j int) bool { return a[i].Start() < a[j].Start() }
   196  func (a linksByStart) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
   197  func (a linksByStart) Len() int           { return len(a) }