Add generated file

This PR adds generated files under pkg/client and vendor folder.
This commit is contained in:
xing-yang
2018-07-12 10:55:15 -07:00
parent 36b1de0341
commit e213d1890d
17729 changed files with 5090889 additions and 0 deletions

111
vendor/golang.org/x/tools/godoc/analysis/README generated vendored Normal file
View File

@@ -0,0 +1,111 @@
Type and Pointer Analysis to-do list
====================================
Alan Donovan <adonovan@google.com>
Overall design
--------------
We should re-run the type and pointer analyses periodically,
as we do with the indexer.
Version skew: how to mitigate the bad effects of stale URLs in old pages?
We could record the file's length/CRC32/mtime in the go/loader, and
refuse to decorate it with links unless they match at serving time.
Use the VFS mechanism when (a) enumerating packages and (b) loading
them. (Requires planned changes to go/loader.)
Future work: shard this using map/reduce for larger corpora.
Testing: how does one test that a web page "looks right"?
Bugs
----
(*ssa.Program).Create requires transitively error-free packages. We
can make this more robust by making the requirement transitively free
of "hard" errors; soft errors are fine.
Markup of compiler errors is slightly buggy because they overlap with
other selections (e.g. Idents). Fix.
User Interface
--------------
CALLGRAPH:
- Add a search box: given a search node, expand path from each entry
point to it.
- Cause hovering over a given node to highlight that node, and all
nodes that are logically identical to it.
- Initially expand the callgraph trees (but not their toggle divs).
CALLEES:
- The '(' links are not very discoverable. Highlight them?
Type info:
- In the source viewer's lower pane, use a toggle div around the
IMPLEMENTS and METHODSETS lists, like we do in the pacakge view.
Only expand them initially if short.
- Include IMPLEMENTS and METHOD SETS information in search index.
- URLs in IMPLEMENTS/METHOD SETS always link to source, even from the
package docs view. This makes sense for links to non-exported
types, but links to exported types and funcs should probably go to
other package docs.
- Suppress toggle divs for empty method sets.
Misc:
- The [X] button in the lower pane is subject to scrolling.
- Should the lower pane be floating? An iframe?
When we change document.location by clicking on a link, it will go away.
How do we prevent that (a la Gmail's chat windows)?
- Progress/status: for each file, display its analysis status, one of:
- not in analysis scope
- type analysis running...
- type analysis complete
(+ optionally: there were type errors in this file)
And if PTA requested:
- type analysis complete; PTA not attempted due to type errors
- PTA running...
- PTA complete
- Scroll the selection into view, e.g. the vertical center, or better
still, under the pointer (assuming we have a mouse).
More features
-------------
Display the REFERRERS relation? (Useful but potentially large.)
Display the INSTANTIATIONS relation? i.e. given a type T, show the set of
syntactic constructs that can instantiate it:
var x T
x := T{...}
x = new(T)
x = make([]T, n)
etc
+ all INSTANTIATIONS of all S defined as struct{t T} or [n]T
(Potentially a lot of information.)
(Add this to guru too.)
Optimisations
-------------
Each call to addLink takes a (per-file) lock. The locking is
fine-grained so server latency isn't terrible, but overall it makes
the link computation quite slow. Batch update might be better.
Memory usage is now about 1.5GB for GOROOT + go.tools. It used to be 700MB.
Optimize for time and space. The main slowdown is the network I/O
time caused by an increase in page size of about 3x: about 2x from
HTML, and 0.7--2.1x from JSON (unindented vs indented). The JSON
contains a lot of filenames (e.g. 820 copies of 16 distinct
filenames). 20% of the HTML is L%d spans (now disabled). The HTML
also contains lots of tooltips for long struct/interface types.
De-dup or just abbreviate? The actual formatting is very fast.

613
vendor/golang.org/x/tools/godoc/analysis/analysis.go generated vendored Normal file
View File

@@ -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
}

351
vendor/golang.org/x/tools/godoc/analysis/callgraph.go generated vendored Normal file
View File

@@ -0,0 +1,351 @@
// 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
// This file computes the CALLERS and CALLEES relations from the call
// graph. CALLERS/CALLEES information is displayed in the lower pane
// when a "func" token or ast.CallExpr.Lparen is clicked, respectively.
import (
"fmt"
"go/ast"
"go/token"
"go/types"
"log"
"math/big"
"sort"
"golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/ssa"
)
// doCallgraph computes the CALLEES and CALLERS relations.
func (a *analysis) doCallgraph(cg *callgraph.Graph) {
log.Print("Deleting synthetic nodes...")
// TODO(adonovan): opt: DeleteSyntheticNodes is asymptotically
// inefficient and can be (unpredictably) slow.
cg.DeleteSyntheticNodes()
log.Print("Synthetic nodes deleted")
// Populate nodes of package call graphs (PCGs).
for _, n := range cg.Nodes {
a.pcgAddNode(n.Func)
}
// Within each PCG, sort funcs by name.
for _, pcg := range a.pcgs {
pcg.sortNodes()
}
calledFuncs := make(map[ssa.CallInstruction]map[*ssa.Function]bool)
callingSites := make(map[*ssa.Function]map[ssa.CallInstruction]bool)
for _, n := range cg.Nodes {
for _, e := range n.Out {
if e.Site == nil {
continue // a call from a synthetic node such as <root>
}
// Add (site pos, callee) to calledFuncs.
// (Dynamic calls only.)
callee := e.Callee.Func
a.pcgAddEdge(n.Func, callee)
if callee.Synthetic != "" {
continue // call of a package initializer
}
if e.Site.Common().StaticCallee() == nil {
// dynamic call
// (CALLEES information for static calls
// is computed using SSA information.)
lparen := e.Site.Common().Pos()
if lparen != token.NoPos {
fns := calledFuncs[e.Site]
if fns == nil {
fns = make(map[*ssa.Function]bool)
calledFuncs[e.Site] = fns
}
fns[callee] = true
}
}
// Add (callee, site) to callingSites.
fns := callingSites[callee]
if fns == nil {
fns = make(map[ssa.CallInstruction]bool)
callingSites[callee] = fns
}
fns[e.Site] = true
}
}
// CALLEES.
log.Print("Callees...")
for site, fns := range calledFuncs {
var funcs funcsByPos
for fn := range fns {
funcs = append(funcs, fn)
}
sort.Sort(funcs)
a.addCallees(site, funcs)
}
// CALLERS
log.Print("Callers...")
for callee, sites := range callingSites {
pos := funcToken(callee)
if pos == token.NoPos {
log.Printf("CALLERS: skipping %s: no pos", callee)
continue
}
var this *types.Package // for relativizing names
if callee.Pkg != nil {
this = callee.Pkg.Pkg
}
// Compute sites grouped by parent, with text and URLs.
sitesByParent := make(map[*ssa.Function]sitesByPos)
for site := range sites {
fn := site.Parent()
sitesByParent[fn] = append(sitesByParent[fn], site)
}
var funcs funcsByPos
for fn := range sitesByParent {
funcs = append(funcs, fn)
}
sort.Sort(funcs)
v := callersJSON{
Callee: callee.String(),
Callers: []callerJSON{}, // (JS wants non-nil)
}
for _, fn := range funcs {
caller := callerJSON{
Func: prettyFunc(this, fn),
Sites: []anchorJSON{}, // (JS wants non-nil)
}
sites := sitesByParent[fn]
sort.Sort(sites)
for _, site := range sites {
pos := site.Common().Pos()
if pos != token.NoPos {
caller.Sites = append(caller.Sites, anchorJSON{
Text: fmt.Sprintf("%d", a.prog.Fset.Position(pos).Line),
Href: a.posURL(pos, len("(")),
})
}
}
v.Callers = append(v.Callers, caller)
}
fi, offset := a.fileAndOffset(pos)
fi.addLink(aLink{
start: offset,
end: offset + len("func"),
title: fmt.Sprintf("%d callers", len(sites)),
onclick: fmt.Sprintf("onClickCallers(%d)", fi.addData(v)),
})
}
// PACKAGE CALLGRAPH
log.Print("Package call graph...")
for pkg, pcg := range a.pcgs {
// Maps (*ssa.Function).RelString() to index in JSON CALLGRAPH array.
index := make(map[string]int)
// Treat exported functions (and exported methods of
// exported named types) as roots even if they aren't
// actually called from outside the package.
for i, n := range pcg.nodes {
if i == 0 || n.fn.Object() == nil || !n.fn.Object().Exported() {
continue
}
recv := n.fn.Signature.Recv()
if recv == nil || deref(recv.Type()).(*types.Named).Obj().Exported() {
roots := &pcg.nodes[0].edges
roots.SetBit(roots, i, 1)
}
index[n.fn.RelString(pkg.Pkg)] = i
}
json := a.pcgJSON(pcg)
// TODO(adonovan): pkg.Path() is not unique!
// It is possible to declare a non-test package called x_test.
a.result.pkgInfo(pkg.Pkg.Path()).setCallGraph(json, index)
}
}
// addCallees adds client data and links for the facts that site calls fns.
func (a *analysis) addCallees(site ssa.CallInstruction, fns []*ssa.Function) {
v := calleesJSON{
Descr: site.Common().Description(),
Callees: []anchorJSON{}, // (JS wants non-nil)
}
var this *types.Package // for relativizing names
if p := site.Parent().Package(); p != nil {
this = p.Pkg
}
for _, fn := range fns {
v.Callees = append(v.Callees, anchorJSON{
Text: prettyFunc(this, fn),
Href: a.posURL(funcToken(fn), len("func")),
})
}
fi, offset := a.fileAndOffset(site.Common().Pos())
fi.addLink(aLink{
start: offset,
end: offset + len("("),
title: fmt.Sprintf("%d callees", len(v.Callees)),
onclick: fmt.Sprintf("onClickCallees(%d)", fi.addData(v)),
})
}
// -- utilities --------------------------------------------------------
// stable order within packages but undefined across packages.
type funcsByPos []*ssa.Function
func (a funcsByPos) Less(i, j int) bool { return a[i].Pos() < a[j].Pos() }
func (a funcsByPos) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a funcsByPos) Len() int { return len(a) }
type sitesByPos []ssa.CallInstruction
func (a sitesByPos) Less(i, j int) bool { return a[i].Common().Pos() < a[j].Common().Pos() }
func (a sitesByPos) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a sitesByPos) Len() int { return len(a) }
func funcToken(fn *ssa.Function) token.Pos {
switch syntax := fn.Syntax().(type) {
case *ast.FuncLit:
return syntax.Type.Func
case *ast.FuncDecl:
return syntax.Type.Func
}
return token.NoPos
}
// prettyFunc pretty-prints fn for the user interface.
// TODO(adonovan): return HTML so we have more markup freedom.
func prettyFunc(this *types.Package, fn *ssa.Function) string {
if fn.Parent() != nil {
return fmt.Sprintf("%s in %s",
types.TypeString(fn.Signature, types.RelativeTo(this)),
prettyFunc(this, fn.Parent()))
}
if fn.Synthetic != "" && fn.Name() == "init" {
// (This is the actual initializer, not a declared 'func init').
if fn.Pkg.Pkg == this {
return "package initializer"
}
return fmt.Sprintf("%q package initializer", fn.Pkg.Pkg.Path())
}
return fn.RelString(this)
}
// -- intra-package callgraph ------------------------------------------
// pcgNode represents a node in the package call graph (PCG).
type pcgNode struct {
fn *ssa.Function
pretty string // cache of prettyFunc(fn)
edges big.Int // set of callee func indices
}
// A packageCallGraph represents the intra-package edges of the global call graph.
// The zeroth node indicates "all external functions".
type packageCallGraph struct {
nodeIndex map[*ssa.Function]int // maps func to node index (a small int)
nodes []*pcgNode // maps node index to node
}
// sortNodes populates pcg.nodes in name order and updates the nodeIndex.
func (pcg *packageCallGraph) sortNodes() {
nodes := make([]*pcgNode, 0, len(pcg.nodeIndex))
nodes = append(nodes, &pcgNode{fn: nil, pretty: "<external>"})
for fn := range pcg.nodeIndex {
nodes = append(nodes, &pcgNode{
fn: fn,
pretty: prettyFunc(fn.Pkg.Pkg, fn),
})
}
sort.Sort(pcgNodesByPretty(nodes[1:]))
for i, n := range nodes {
pcg.nodeIndex[n.fn] = i
}
pcg.nodes = nodes
}
func (pcg *packageCallGraph) addEdge(caller, callee *ssa.Function) {
var callerIndex int
if caller.Pkg == callee.Pkg {
// intra-package edge
callerIndex = pcg.nodeIndex[caller]
if callerIndex < 1 {
panic(caller)
}
}
edges := &pcg.nodes[callerIndex].edges
edges.SetBit(edges, pcg.nodeIndex[callee], 1)
}
func (a *analysis) pcgAddNode(fn *ssa.Function) {
if fn.Pkg == nil {
return
}
pcg, ok := a.pcgs[fn.Pkg]
if !ok {
pcg = &packageCallGraph{nodeIndex: make(map[*ssa.Function]int)}
a.pcgs[fn.Pkg] = pcg
}
pcg.nodeIndex[fn] = -1
}
func (a *analysis) pcgAddEdge(caller, callee *ssa.Function) {
if callee.Pkg != nil {
a.pcgs[callee.Pkg].addEdge(caller, callee)
}
}
// pcgJSON returns a new slice of callgraph JSON values.
func (a *analysis) pcgJSON(pcg *packageCallGraph) []*PCGNodeJSON {
var nodes []*PCGNodeJSON
for _, n := range pcg.nodes {
// TODO(adonovan): why is there no good way to iterate
// over the set bits of a big.Int?
var callees []int
nbits := n.edges.BitLen()
for j := 0; j < nbits; j++ {
if n.edges.Bit(j) == 1 {
callees = append(callees, j)
}
}
var pos token.Pos
if n.fn != nil {
pos = funcToken(n.fn)
}
nodes = append(nodes, &PCGNodeJSON{
Func: anchorJSON{
Text: n.pretty,
Href: a.posURL(pos, len("func")),
},
Callees: callees,
})
}
return nodes
}
type pcgNodesByPretty []*pcgNode
func (a pcgNodesByPretty) Less(i, j int) bool { return a[i].pretty < a[j].pretty }
func (a pcgNodesByPretty) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a pcgNodesByPretty) Len() int { return len(a) }

195
vendor/golang.org/x/tools/godoc/analysis/implements.go generated vendored Normal file
View File

@@ -0,0 +1,195 @@
// 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
// This file computes the "implements" relation over all pairs of
// named types in the program. (The mark-up is done by typeinfo.go.)
// TODO(adonovan): do we want to report implements(C, I) where C and I
// belong to different packages and at least one is not exported?
import (
"go/types"
"sort"
"golang.org/x/tools/go/types/typeutil"
)
// computeImplements computes the "implements" relation over all pairs
// of named types in allNamed.
func computeImplements(cache *typeutil.MethodSetCache, allNamed []*types.Named) map[*types.Named]implementsFacts {
// Information about a single type's method set.
type msetInfo struct {
typ types.Type
mset *types.MethodSet
mask1, mask2 uint64
}
initMsetInfo := func(info *msetInfo, typ types.Type) {
info.typ = typ
info.mset = cache.MethodSet(typ)
for i := 0; i < info.mset.Len(); i++ {
name := info.mset.At(i).Obj().Name()
info.mask1 |= 1 << methodBit(name[0])
info.mask2 |= 1 << methodBit(name[len(name)-1])
}
}
// satisfies(T, U) reports whether type T satisfies type U.
// U must be an interface.
//
// Since there are thousands of types (and thus millions of
// pairs of types) and types.Assignable(T, U) is relatively
// expensive, we compute assignability directly from the
// method sets. (At least one of T and U must be an
// interface.)
//
// We use a trick (thanks gri!) related to a Bloom filter to
// quickly reject most tests, which are false. For each
// method set, we precompute a mask, a set of bits, one per
// distinct initial byte of each method name. Thus the mask
// for io.ReadWriter would be {'R','W'}. AssignableTo(T, U)
// cannot be true unless mask(T)&mask(U)==mask(U).
//
// As with a Bloom filter, we can improve precision by testing
// additional hashes, e.g. using the last letter of each
// method name, so long as the subset mask property holds.
//
// When analyzing the standard library, there are about 1e6
// calls to satisfies(), of which 0.6% return true. With a
// 1-hash filter, 95% of calls avoid the expensive check; with
// a 2-hash filter, this grows to 98.2%.
satisfies := func(T, U *msetInfo) bool {
return T.mask1&U.mask1 == U.mask1 &&
T.mask2&U.mask2 == U.mask2 &&
containsAllIdsOf(T.mset, U.mset)
}
// Information about a named type N, and perhaps also *N.
type namedInfo struct {
isInterface bool
base msetInfo // N
ptr msetInfo // *N, iff N !isInterface
}
var infos []namedInfo
// Precompute the method sets and their masks.
for _, N := range allNamed {
var info namedInfo
initMsetInfo(&info.base, N)
_, info.isInterface = N.Underlying().(*types.Interface)
if !info.isInterface {
initMsetInfo(&info.ptr, types.NewPointer(N))
}
if info.base.mask1|info.ptr.mask1 == 0 {
continue // neither N nor *N has methods
}
infos = append(infos, info)
}
facts := make(map[*types.Named]implementsFacts)
// Test all pairs of distinct named types (T, U).
// TODO(adonovan): opt: compute (U, T) at the same time.
for t := range infos {
T := &infos[t]
var to, from, fromPtr []types.Type
for u := range infos {
if t == u {
continue
}
U := &infos[u]
switch {
case T.isInterface && U.isInterface:
if satisfies(&U.base, &T.base) {
to = append(to, U.base.typ)
}
if satisfies(&T.base, &U.base) {
from = append(from, U.base.typ)
}
case T.isInterface: // U concrete
if satisfies(&U.base, &T.base) {
to = append(to, U.base.typ)
} else if satisfies(&U.ptr, &T.base) {
to = append(to, U.ptr.typ)
}
case U.isInterface: // T concrete
if satisfies(&T.base, &U.base) {
from = append(from, U.base.typ)
} else if satisfies(&T.ptr, &U.base) {
fromPtr = append(fromPtr, U.base.typ)
}
}
}
// Sort types (arbitrarily) to avoid nondeterminism.
sort.Sort(typesByString(to))
sort.Sort(typesByString(from))
sort.Sort(typesByString(fromPtr))
facts[T.base.typ.(*types.Named)] = implementsFacts{to, from, fromPtr}
}
return facts
}
type implementsFacts struct {
to []types.Type // named or ptr-to-named types assignable to interface T
from []types.Type // named interfaces assignable from T
fromPtr []types.Type // named interfaces assignable only from *T
}
type typesByString []types.Type
func (p typesByString) Len() int { return len(p) }
func (p typesByString) Less(i, j int) bool { return p[i].String() < p[j].String() }
func (p typesByString) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
// methodBit returns the index of x in [a-zA-Z], or 52 if not found.
func methodBit(x byte) uint64 {
switch {
case 'a' <= x && x <= 'z':
return uint64(x - 'a')
case 'A' <= x && x <= 'Z':
return uint64(26 + x - 'A')
}
return 52 // all other bytes
}
// containsAllIdsOf reports whether the method identifiers of T are a
// superset of those in U. If U belongs to an interface type, the
// result is equal to types.Assignable(T, U), but is cheaper to compute.
//
// TODO(gri): make this a method of *types.MethodSet.
//
func containsAllIdsOf(T, U *types.MethodSet) bool {
t, tlen := 0, T.Len()
u, ulen := 0, U.Len()
for t < tlen && u < ulen {
tMeth := T.At(t).Obj()
uMeth := U.At(u).Obj()
tId := tMeth.Id()
uId := uMeth.Id()
if tId > uId {
// U has a method T lacks: fail.
return false
}
if tId < uId {
// T has a method U lacks: ignore it.
t++
continue
}
// U and T both have a method of this Id. Check types.
if !types.Identical(tMeth.Type(), uMeth.Type()) {
return false // type mismatch
}
u++
t++
}
return u == ulen
}

69
vendor/golang.org/x/tools/godoc/analysis/json.go generated vendored Normal file
View File

@@ -0,0 +1,69 @@
// 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
// This file defines types used by client-side JavaScript.
type anchorJSON struct {
Text string // HTML
Href string // URL
}
type commOpJSON struct {
Op anchorJSON
Fn string
}
// JavaScript's onClickComm() expects a commJSON.
type commJSON struct {
Ops []commOpJSON
}
// Indicates one of these forms of fact about a type T:
// T "is implemented by <ByKind> type <Other>" (ByKind != "", e.g. "array")
// T "implements <Other>" (ByKind == "")
type implFactJSON struct {
ByKind string `json:",omitempty"`
Other anchorJSON
}
// Implements facts are grouped by form, for ease of reading.
type implGroupJSON struct {
Descr string
Facts []implFactJSON
}
// JavaScript's onClickIdent() expects a TypeInfoJSON.
type TypeInfoJSON struct {
Name string // type name
Size, Align int64
Methods []anchorJSON
ImplGroups []implGroupJSON
}
// JavaScript's onClickCallees() expects a calleesJSON.
type calleesJSON struct {
Descr string
Callees []anchorJSON // markup for called function
}
type callerJSON struct {
Func string
Sites []anchorJSON
}
// JavaScript's onClickCallers() expects a callersJSON.
type callersJSON struct {
Callee string
Callers []callerJSON
}
// JavaScript's cgAddChild requires a global array of PCGNodeJSON
// called CALLGRAPH, representing the intra-package call graph.
// The first element is special and represents "all external callers".
type PCGNodeJSON struct {
Func anchorJSON
Callees []int // indices within CALLGRAPH of nodes called by this one
}

154
vendor/golang.org/x/tools/godoc/analysis/peers.go generated vendored Normal file
View File

@@ -0,0 +1,154 @@
// 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
// This file computes the channel "peers" relation over all pairs of
// channel operations in the program. The peers are displayed in the
// lower pane when a channel operation (make, <-, close) is clicked.
// TODO(adonovan): handle calls to reflect.{Select,Recv,Send,Close} too,
// then enable reflection in PTA.
import (
"fmt"
"go/token"
"go/types"
"golang.org/x/tools/go/pointer"
"golang.org/x/tools/go/ssa"
)
func (a *analysis) doChannelPeers(ptsets map[ssa.Value]pointer.Pointer) {
addSendRecv := func(j *commJSON, op chanOp) {
j.Ops = append(j.Ops, commOpJSON{
Op: anchorJSON{
Text: op.mode,
Href: a.posURL(op.pos, op.len),
},
Fn: prettyFunc(nil, op.fn),
})
}
// Build an undirected bipartite multigraph (binary relation)
// of MakeChan ops and send/recv/close ops.
//
// TODO(adonovan): opt: use channel element types to partition
// the O(n^2) problem into subproblems.
aliasedOps := make(map[*ssa.MakeChan][]chanOp)
opToMakes := make(map[chanOp][]*ssa.MakeChan)
for _, op := range a.ops {
// Combine the PT sets from all contexts.
var makes []*ssa.MakeChan // aliased ops
ptr, ok := ptsets[op.ch]
if !ok {
continue // e.g. channel op in dead code
}
for _, label := range ptr.PointsTo().Labels() {
makechan, ok := label.Value().(*ssa.MakeChan)
if !ok {
continue // skip intrinsically-created channels for now
}
if makechan.Pos() == token.NoPos {
continue // not possible?
}
makes = append(makes, makechan)
aliasedOps[makechan] = append(aliasedOps[makechan], op)
}
opToMakes[op] = makes
}
// Now that complete relation is built, build links for ops.
for _, op := range a.ops {
v := commJSON{
Ops: []commOpJSON{}, // (JS wants non-nil)
}
ops := make(map[chanOp]bool)
for _, makechan := range opToMakes[op] {
v.Ops = append(v.Ops, commOpJSON{
Op: anchorJSON{
Text: "made",
Href: a.posURL(makechan.Pos()-token.Pos(len("make")),
len("make")),
},
Fn: makechan.Parent().RelString(op.fn.Package().Pkg),
})
for _, op := range aliasedOps[makechan] {
ops[op] = true
}
}
for op := range ops {
addSendRecv(&v, op)
}
// Add links for each aliased op.
fi, offset := a.fileAndOffset(op.pos)
fi.addLink(aLink{
start: offset,
end: offset + op.len,
title: "show channel ops",
onclick: fmt.Sprintf("onClickComm(%d)", fi.addData(v)),
})
}
// Add links for makechan ops themselves.
for makechan, ops := range aliasedOps {
v := commJSON{
Ops: []commOpJSON{}, // (JS wants non-nil)
}
for _, op := range ops {
addSendRecv(&v, op)
}
fi, offset := a.fileAndOffset(makechan.Pos())
fi.addLink(aLink{
start: offset - len("make"),
end: offset,
title: "show channel ops",
onclick: fmt.Sprintf("onClickComm(%d)", fi.addData(v)),
})
}
}
// -- utilities --------------------------------------------------------
// chanOp abstracts an ssa.Send, ssa.Unop(ARROW), close(), or a SelectState.
// Derived from cmd/guru/peers.go.
type chanOp struct {
ch ssa.Value
mode string // sent|received|closed
pos token.Pos
len int
fn *ssa.Function
}
// chanOps returns a slice of all the channel operations in the instruction.
// Derived from cmd/guru/peers.go.
func chanOps(instr ssa.Instruction) []chanOp {
fn := instr.Parent()
var ops []chanOp
switch instr := instr.(type) {
case *ssa.UnOp:
if instr.Op == token.ARROW {
// TODO(adonovan): don't assume <-ch; could be 'range ch'.
ops = append(ops, chanOp{instr.X, "received", instr.Pos(), len("<-"), fn})
}
case *ssa.Send:
ops = append(ops, chanOp{instr.Chan, "sent", instr.Pos(), len("<-"), fn})
case *ssa.Select:
for _, st := range instr.States {
mode := "received"
if st.Dir == types.SendOnly {
mode = "sent"
}
ops = append(ops, chanOp{st.Chan, mode, st.Pos, len("<-"), fn})
}
case ssa.CallInstruction:
call := instr.Common()
if blt, ok := call.Value.(*ssa.Builtin); ok && blt.Name() == "close" {
pos := instr.Common().Pos()
ops = append(ops, chanOp{call.Args[0], "closed", pos - token.Pos(len("close")), len("close("), fn})
}
}
return ops
}

234
vendor/golang.org/x/tools/godoc/analysis/typeinfo.go generated vendored Normal file
View File

@@ -0,0 +1,234 @@
// 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
// This file computes the markup for information from go/types:
// IMPORTS, identifier RESOLUTION, METHOD SETS, size/alignment, and
// the IMPLEMENTS relation.
//
// IMPORTS links connect import specs to the documentation for the
// imported package.
//
// RESOLUTION links referring identifiers to their defining
// identifier, and adds tooltips for kind and type.
//
// METHOD SETS, size/alignment, and the IMPLEMENTS relation are
// displayed in the lower pane when a type's defining identifier is
// clicked.
import (
"fmt"
"go/types"
"reflect"
"strconv"
"strings"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/types/typeutil"
)
// TODO(adonovan): audit to make sure it's safe on ill-typed packages.
// TODO(adonovan): use same Sizes as loader.Config.
var sizes = types.StdSizes{WordSize: 8, MaxAlign: 8}
func (a *analysis) doTypeInfo(info *loader.PackageInfo, implements map[*types.Named]implementsFacts) {
// We must not assume the corresponding SSA packages were
// created (i.e. were transitively error-free).
// IMPORTS
for _, f := range info.Files {
// Package decl.
fi, offset := a.fileAndOffset(f.Name.Pos())
fi.addLink(aLink{
start: offset,
end: offset + len(f.Name.Name),
title: "Package docs for " + info.Pkg.Path(),
// TODO(adonovan): fix: we're putting the untrusted Path()
// into a trusted field. What's the appropriate sanitizer?
href: "/pkg/" + info.Pkg.Path(),
})
// Import specs.
for _, imp := range f.Imports {
// Remove quotes.
L := int(imp.End()-imp.Path.Pos()) - len(`""`)
path, _ := strconv.Unquote(imp.Path.Value)
fi, offset := a.fileAndOffset(imp.Path.Pos())
fi.addLink(aLink{
start: offset + 1,
end: offset + 1 + L,
title: "Package docs for " + path,
// TODO(adonovan): fix: we're putting the untrusted path
// into a trusted field. What's the appropriate sanitizer?
href: "/pkg/" + path,
})
}
}
// RESOLUTION
qualifier := types.RelativeTo(info.Pkg)
for id, obj := range info.Uses {
// Position of the object definition.
pos := obj.Pos()
Len := len(obj.Name())
// Correct the position for non-renaming import specs.
// import "sync/atomic"
// ^^^^^^^^^^^
if obj, ok := obj.(*types.PkgName); ok && id.Name == obj.Imported().Name() {
// Assume this is a non-renaming import.
// NB: not true for degenerate renamings: `import foo "foo"`.
pos++
Len = len(obj.Imported().Path())
}
if obj.Pkg() == nil {
continue // don't mark up built-ins.
}
fi, offset := a.fileAndOffset(id.NamePos)
fi.addLink(aLink{
start: offset,
end: offset + len(id.Name),
title: types.ObjectString(obj, qualifier),
href: a.posURL(pos, Len),
})
}
// IMPLEMENTS & METHOD SETS
for _, obj := range info.Defs {
if obj, ok := obj.(*types.TypeName); ok {
if named, ok := obj.Type().(*types.Named); ok {
a.namedType(named, implements)
}
}
}
}
func (a *analysis) namedType(T *types.Named, implements map[*types.Named]implementsFacts) {
obj := T.Obj()
qualifier := types.RelativeTo(obj.Pkg())
v := &TypeInfoJSON{
Name: obj.Name(),
Size: sizes.Sizeof(T),
Align: sizes.Alignof(T),
Methods: []anchorJSON{}, // (JS wants non-nil)
}
// addFact adds the fact "is implemented by T" (by) or
// "implements T" (!by) to group.
addFact := func(group *implGroupJSON, T types.Type, by bool) {
Tobj := deref(T).(*types.Named).Obj()
var byKind string
if by {
// Show underlying kind of implementing type,
// e.g. "slice", "array", "struct".
s := reflect.TypeOf(T.Underlying()).String()
byKind = strings.ToLower(strings.TrimPrefix(s, "*types."))
}
group.Facts = append(group.Facts, implFactJSON{
ByKind: byKind,
Other: anchorJSON{
Href: a.posURL(Tobj.Pos(), len(Tobj.Name())),
Text: types.TypeString(T, qualifier),
},
})
}
// IMPLEMENTS
if r, ok := implements[T]; ok {
if isInterface(T) {
// "T is implemented by <conc>" ...
// "T is implemented by <iface>"...
// "T implements <iface>"...
group := implGroupJSON{
Descr: types.TypeString(T, qualifier),
}
// Show concrete types first; use two passes.
for _, sub := range r.to {
if !isInterface(sub) {
addFact(&group, sub, true)
}
}
for _, sub := range r.to {
if isInterface(sub) {
addFact(&group, sub, true)
}
}
for _, super := range r.from {
addFact(&group, super, false)
}
v.ImplGroups = append(v.ImplGroups, group)
} else {
// T is concrete.
if r.from != nil {
// "T implements <iface>"...
group := implGroupJSON{
Descr: types.TypeString(T, qualifier),
}
for _, super := range r.from {
addFact(&group, super, false)
}
v.ImplGroups = append(v.ImplGroups, group)
}
if r.fromPtr != nil {
// "*C implements <iface>"...
group := implGroupJSON{
Descr: "*" + types.TypeString(T, qualifier),
}
for _, psuper := range r.fromPtr {
addFact(&group, psuper, false)
}
v.ImplGroups = append(v.ImplGroups, group)
}
}
}
// METHOD SETS
for _, sel := range typeutil.IntuitiveMethodSet(T, &a.prog.MethodSets) {
meth := sel.Obj().(*types.Func)
pos := meth.Pos() // may be 0 for error.Error
v.Methods = append(v.Methods, anchorJSON{
Href: a.posURL(pos, len(meth.Name())),
Text: types.SelectionString(sel, qualifier),
})
}
// Since there can be many specs per decl, we
// can't attach the link to the keyword 'type'
// (as we do with 'func'); we use the Ident.
fi, offset := a.fileAndOffset(obj.Pos())
fi.addLink(aLink{
start: offset,
end: offset + len(obj.Name()),
title: fmt.Sprintf("type info for %s", obj.Name()),
onclick: fmt.Sprintf("onClickTypeInfo(%d)", fi.addData(v)),
})
// Add info for exported package-level types to the package info.
if obj.Exported() && isPackageLevel(obj) {
// TODO(adonovan): Path is not unique!
// It is possible to declare a non-test package called x_test.
a.result.pkgInfo(obj.Pkg().Path()).addType(v)
}
}
// -- utilities --------------------------------------------------------
func isInterface(T types.Type) bool { return types.IsInterface(T) }
// deref returns a pointer's element type; otherwise it returns typ.
func deref(typ types.Type) types.Type {
if p, ok := typ.Underlying().(*types.Pointer); ok {
return p.Elem()
}
return typ
}
// isPackageLevel reports whether obj is a package-level object.
func isPackageLevel(obj types.Object) bool {
return obj.Pkg().Scope().Lookup(obj.Name()) == obj
}