forked from golang/tools
-
Notifications
You must be signed in to change notification settings - Fork 0
/
analysis.go
194 lines (174 loc) · 6.09 KB
/
analysis.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
// 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 "github.com/goki/go-tools/godoc/analysis"
import (
"io"
"sort"
"sync"
)
// -- 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
}
// -- 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
}
// 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
}
// 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
}
// 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()
}
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) }