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 | package analysis // import "golang.org/x/tools/godoc/analysis" |
43 | |
44 | import ( |
45 | "io" |
46 | "sort" |
47 | "sync" |
48 | ) |
49 | |
50 | // -- links ------------------------------------------------------------ |
51 | |
52 | // A Link is an HTML decoration of the bytes [Start, End) of a file. |
53 | // Write is called before/after those bytes to emit the mark-up. |
54 | type Link interface { |
55 | Start() int |
56 | End() int |
57 | Write(w io.Writer, _ int, start bool) // the godoc.LinkWriter signature |
58 | } |
59 | |
60 | // -- fileInfo --------------------------------------------------------- |
61 | |
62 | // FileInfo holds analysis information for the source file view. |
63 | // Clients must not mutate it. |
64 | type FileInfo struct { |
65 | Data []interface{} // JSON serializable values |
66 | Links []Link // HTML link markup |
67 | } |
68 | |
69 | // A fileInfo is the server's store of hyperlinks and JSON data for a |
70 | // particular file. |
71 | type fileInfo struct { |
72 | mu sync.Mutex |
73 | data []interface{} // JSON objects |
74 | links []Link |
75 | sorted bool |
76 | hasErrors bool // TODO(adonovan): surface this in the UI |
77 | } |
78 | |
79 | // get returns the file info in external form. |
80 | // Callers must not mutate its fields. |
81 | func (fi *fileInfo) get() FileInfo { |
82 | var r FileInfo |
83 | // Copy slices, to avoid races. |
84 | fi.mu.Lock() |
85 | r.Data = append(r.Data, fi.data...) |
86 | if !fi.sorted { |
87 | sort.Sort(linksByStart(fi.links)) |
88 | fi.sorted = true |
89 | } |
90 | r.Links = append(r.Links, fi.links...) |
91 | fi.mu.Unlock() |
92 | return r |
93 | } |
94 | |
95 | // PackageInfo holds analysis information for the package view. |
96 | // Clients must not mutate it. |
97 | type PackageInfo struct { |
98 | CallGraph []*PCGNodeJSON |
99 | CallGraphIndex map[string]int |
100 | Types []*TypeInfoJSON |
101 | } |
102 | |
103 | type pkgInfo struct { |
104 | mu sync.Mutex |
105 | callGraph []*PCGNodeJSON |
106 | callGraphIndex map[string]int // keys are (*ssa.Function).RelString() |
107 | types []*TypeInfoJSON // type info for exported types |
108 | } |
109 | |
110 | // get returns the package info in external form. |
111 | // Callers must not mutate its fields. |
112 | func (pi *pkgInfo) get() PackageInfo { |
113 | var r PackageInfo |
114 | // Copy slices, to avoid races. |
115 | pi.mu.Lock() |
116 | r.CallGraph = append(r.CallGraph, pi.callGraph...) |
117 | r.CallGraphIndex = pi.callGraphIndex |
118 | r.Types = append(r.Types, pi.types...) |
119 | pi.mu.Unlock() |
120 | return r |
121 | } |
122 | |
123 | // -- Result ----------------------------------------------------------- |
124 | |
125 | // Result contains the results of analysis. |
126 | // The result contains a mapping from filenames to a set of HTML links |
127 | // and JavaScript data referenced by the links. |
128 | type Result struct { |
129 | mu sync.Mutex // guards maps (but not their contents) |
130 | status string // global analysis status |
131 | fileInfos map[string]*fileInfo // keys are godoc file URLs |
132 | pkgInfos map[string]*pkgInfo // keys are import paths |
133 | } |
134 | |
135 | // fileInfo returns the fileInfo for the specified godoc file URL, |
136 | // constructing it as needed. Thread-safe. |
137 | func (res *Result) fileInfo(url string) *fileInfo { |
138 | res.mu.Lock() |
139 | fi, ok := res.fileInfos[url] |
140 | if !ok { |
141 | if res.fileInfos == nil { |
142 | res.fileInfos = make(map[string]*fileInfo) |
143 | } |
144 | fi = new(fileInfo) |
145 | res.fileInfos[url] = fi |
146 | } |
147 | res.mu.Unlock() |
148 | return fi |
149 | } |
150 | |
151 | // Status returns a human-readable description of the current analysis status. |
152 | func (res *Result) Status() string { |
153 | res.mu.Lock() |
154 | defer res.mu.Unlock() |
155 | return res.status |
156 | } |
157 | |
158 | // FileInfo returns new slices containing opaque JSON values and the |
159 | // HTML link markup for the specified godoc file URL. Thread-safe. |
160 | // Callers must not mutate the elements. |
161 | // It returns "zero" if no data is available. |
162 | func (res *Result) FileInfo(url string) (fi FileInfo) { |
163 | return res.fileInfo(url).get() |
164 | } |
165 | |
166 | // pkgInfo returns the pkgInfo for the specified import path, |
167 | // constructing it as needed. Thread-safe. |
168 | func (res *Result) pkgInfo(importPath string) *pkgInfo { |
169 | res.mu.Lock() |
170 | pi, ok := res.pkgInfos[importPath] |
171 | if !ok { |
172 | if res.pkgInfos == nil { |
173 | res.pkgInfos = make(map[string]*pkgInfo) |
174 | } |
175 | pi = new(pkgInfo) |
176 | res.pkgInfos[importPath] = pi |
177 | } |
178 | res.mu.Unlock() |
179 | return pi |
180 | } |
181 | |
182 | // PackageInfo returns new slices of JSON values for the callgraph and |
183 | // type info for the specified package. Thread-safe. |
184 | // Callers must not mutate its fields. |
185 | // PackageInfo returns "zero" if no data is available. |
186 | func (res *Result) PackageInfo(importPath string) PackageInfo { |
187 | return res.pkgInfo(importPath).get() |
188 | } |
189 | |
190 | type linksByStart []Link |
191 | |
192 | func (a linksByStart) Less(i, j int) bool { return a[i].Start() < a[j].Start() } |
193 | func (a linksByStart) Swap(i, j int) { a[i], a[j] = a[j], a[i] } |
194 | func (a linksByStart) Len() int { return len(a) } |
195 |
Members