1 |
747 |
jeremybenn |
// Copyright 2009 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 doc
|
6 |
|
|
|
7 |
|
|
import (
|
8 |
|
|
"go/ast"
|
9 |
|
|
"go/token"
|
10 |
|
|
"regexp"
|
11 |
|
|
"sort"
|
12 |
|
|
"strconv"
|
13 |
|
|
)
|
14 |
|
|
|
15 |
|
|
// ----------------------------------------------------------------------------
|
16 |
|
|
// function/method sets
|
17 |
|
|
//
|
18 |
|
|
// Internally, we treat functions like methods and collect them in method sets.
|
19 |
|
|
|
20 |
|
|
// methodSet describes a set of methods. Entries where Decl == nil are conflict
|
21 |
|
|
// entries (more then one method with the same name at the same embedding level).
|
22 |
|
|
//
|
23 |
|
|
type methodSet map[string]*Func
|
24 |
|
|
|
25 |
|
|
// recvString returns a string representation of recv of the
|
26 |
|
|
// form "T", "*T", or "BADRECV" (if not a proper receiver type).
|
27 |
|
|
//
|
28 |
|
|
func recvString(recv ast.Expr) string {
|
29 |
|
|
switch t := recv.(type) {
|
30 |
|
|
case *ast.Ident:
|
31 |
|
|
return t.Name
|
32 |
|
|
case *ast.StarExpr:
|
33 |
|
|
return "*" + recvString(t.X)
|
34 |
|
|
}
|
35 |
|
|
return "BADRECV"
|
36 |
|
|
}
|
37 |
|
|
|
38 |
|
|
// set creates the corresponding Func for f and adds it to mset.
|
39 |
|
|
// If there are multiple f's with the same name, set keeps the first
|
40 |
|
|
// one with documentation; conflicts are ignored.
|
41 |
|
|
//
|
42 |
|
|
func (mset methodSet) set(f *ast.FuncDecl) {
|
43 |
|
|
name := f.Name.Name
|
44 |
|
|
if g := mset[name]; g != nil && g.Doc != "" {
|
45 |
|
|
// A function with the same name has already been registered;
|
46 |
|
|
// since it has documentation, assume f is simply another
|
47 |
|
|
// implementation and ignore it. This does not happen if the
|
48 |
|
|
// caller is using go/build.ScanDir to determine the list of
|
49 |
|
|
// files implementing a package.
|
50 |
|
|
return
|
51 |
|
|
}
|
52 |
|
|
// function doesn't exist or has no documentation; use f
|
53 |
|
|
recv := ""
|
54 |
|
|
if f.Recv != nil {
|
55 |
|
|
var typ ast.Expr
|
56 |
|
|
// be careful in case of incorrect ASTs
|
57 |
|
|
if list := f.Recv.List; len(list) == 1 {
|
58 |
|
|
typ = list[0].Type
|
59 |
|
|
}
|
60 |
|
|
recv = recvString(typ)
|
61 |
|
|
}
|
62 |
|
|
mset[name] = &Func{
|
63 |
|
|
Doc: f.Doc.Text(),
|
64 |
|
|
Name: name,
|
65 |
|
|
Decl: f,
|
66 |
|
|
Recv: recv,
|
67 |
|
|
Orig: recv,
|
68 |
|
|
}
|
69 |
|
|
f.Doc = nil // doc consumed - remove from AST
|
70 |
|
|
}
|
71 |
|
|
|
72 |
|
|
// add adds method m to the method set; m is ignored if the method set
|
73 |
|
|
// already contains a method with the same name at the same or a higher
|
74 |
|
|
// level then m.
|
75 |
|
|
//
|
76 |
|
|
func (mset methodSet) add(m *Func) {
|
77 |
|
|
old := mset[m.Name]
|
78 |
|
|
if old == nil || m.Level < old.Level {
|
79 |
|
|
mset[m.Name] = m
|
80 |
|
|
return
|
81 |
|
|
}
|
82 |
|
|
if old != nil && m.Level == old.Level {
|
83 |
|
|
// conflict - mark it using a method with nil Decl
|
84 |
|
|
mset[m.Name] = &Func{
|
85 |
|
|
Name: m.Name,
|
86 |
|
|
Level: m.Level,
|
87 |
|
|
}
|
88 |
|
|
}
|
89 |
|
|
}
|
90 |
|
|
|
91 |
|
|
// ----------------------------------------------------------------------------
|
92 |
|
|
// Named types
|
93 |
|
|
|
94 |
|
|
// baseTypeName returns the name of the base type of x (or "")
|
95 |
|
|
// and whether the type is imported or not.
|
96 |
|
|
//
|
97 |
|
|
func baseTypeName(x ast.Expr) (name string, imported bool) {
|
98 |
|
|
switch t := x.(type) {
|
99 |
|
|
case *ast.Ident:
|
100 |
|
|
return t.Name, false
|
101 |
|
|
case *ast.SelectorExpr:
|
102 |
|
|
if _, ok := t.X.(*ast.Ident); ok {
|
103 |
|
|
// only possible for qualified type names;
|
104 |
|
|
// assume type is imported
|
105 |
|
|
return t.Sel.Name, true
|
106 |
|
|
}
|
107 |
|
|
case *ast.StarExpr:
|
108 |
|
|
return baseTypeName(t.X)
|
109 |
|
|
}
|
110 |
|
|
return
|
111 |
|
|
}
|
112 |
|
|
|
113 |
|
|
// A namedType represents a named unqualified (package local, or possibly
|
114 |
|
|
// predeclared) type. The namedType for a type name is always found via
|
115 |
|
|
// reader.lookupType.
|
116 |
|
|
//
|
117 |
|
|
type namedType struct {
|
118 |
|
|
doc string // doc comment for type
|
119 |
|
|
name string // type name
|
120 |
|
|
decl *ast.GenDecl // nil if declaration hasn't been seen yet
|
121 |
|
|
|
122 |
|
|
isEmbedded bool // true if this type is embedded
|
123 |
|
|
isStruct bool // true if this type is a struct
|
124 |
|
|
embedded map[*namedType]bool // true if the embedded type is a pointer
|
125 |
|
|
|
126 |
|
|
// associated declarations
|
127 |
|
|
values []*Value // consts and vars
|
128 |
|
|
funcs methodSet
|
129 |
|
|
methods methodSet
|
130 |
|
|
}
|
131 |
|
|
|
132 |
|
|
// ----------------------------------------------------------------------------
|
133 |
|
|
// AST reader
|
134 |
|
|
|
135 |
|
|
// reader accumulates documentation for a single package.
|
136 |
|
|
// It modifies the AST: Comments (declaration documentation)
|
137 |
|
|
// that have been collected by the reader are set to nil
|
138 |
|
|
// in the respective AST nodes so that they are not printed
|
139 |
|
|
// twice (once when printing the documentation and once when
|
140 |
|
|
// printing the corresponding AST node).
|
141 |
|
|
//
|
142 |
|
|
type reader struct {
|
143 |
|
|
mode Mode
|
144 |
|
|
|
145 |
|
|
// package properties
|
146 |
|
|
doc string // package documentation, if any
|
147 |
|
|
filenames []string
|
148 |
|
|
bugs []string
|
149 |
|
|
|
150 |
|
|
// declarations
|
151 |
|
|
imports map[string]int
|
152 |
|
|
values []*Value // consts and vars
|
153 |
|
|
types map[string]*namedType
|
154 |
|
|
funcs methodSet
|
155 |
|
|
}
|
156 |
|
|
|
157 |
|
|
func (r *reader) isVisible(name string) bool {
|
158 |
|
|
return r.mode&AllDecls != 0 || ast.IsExported(name)
|
159 |
|
|
}
|
160 |
|
|
|
161 |
|
|
// lookupType returns the base type with the given name.
|
162 |
|
|
// If the base type has not been encountered yet, a new
|
163 |
|
|
// type with the given name but no associated declaration
|
164 |
|
|
// is added to the type map.
|
165 |
|
|
//
|
166 |
|
|
func (r *reader) lookupType(name string) *namedType {
|
167 |
|
|
if name == "" || name == "_" {
|
168 |
|
|
return nil // no type docs for anonymous types
|
169 |
|
|
}
|
170 |
|
|
if typ, found := r.types[name]; found {
|
171 |
|
|
return typ
|
172 |
|
|
}
|
173 |
|
|
// type not found - add one without declaration
|
174 |
|
|
typ := &namedType{
|
175 |
|
|
name: name,
|
176 |
|
|
embedded: make(map[*namedType]bool),
|
177 |
|
|
funcs: make(methodSet),
|
178 |
|
|
methods: make(methodSet),
|
179 |
|
|
}
|
180 |
|
|
r.types[name] = typ
|
181 |
|
|
return typ
|
182 |
|
|
}
|
183 |
|
|
|
184 |
|
|
// recordAnonymousField registers fieldType as the type of an
|
185 |
|
|
// anonymous field in the parent type. If the field is imported
|
186 |
|
|
// (qualified name) or the parent is nil, the field is ignored.
|
187 |
|
|
// The function returns the field name.
|
188 |
|
|
//
|
189 |
|
|
func (r *reader) recordAnonymousField(parent *namedType, fieldType ast.Expr) (fname string) {
|
190 |
|
|
fname, imp := baseTypeName(fieldType)
|
191 |
|
|
if parent == nil || imp {
|
192 |
|
|
return
|
193 |
|
|
}
|
194 |
|
|
if ftype := r.lookupType(fname); ftype != nil {
|
195 |
|
|
ftype.isEmbedded = true
|
196 |
|
|
_, ptr := fieldType.(*ast.StarExpr)
|
197 |
|
|
parent.embedded[ftype] = ptr
|
198 |
|
|
}
|
199 |
|
|
return
|
200 |
|
|
}
|
201 |
|
|
|
202 |
|
|
func (r *reader) readDoc(comment *ast.CommentGroup) {
|
203 |
|
|
// By convention there should be only one package comment
|
204 |
|
|
// but collect all of them if there are more then one.
|
205 |
|
|
text := comment.Text()
|
206 |
|
|
if r.doc == "" {
|
207 |
|
|
r.doc = text
|
208 |
|
|
return
|
209 |
|
|
}
|
210 |
|
|
r.doc += "\n" + text
|
211 |
|
|
}
|
212 |
|
|
|
213 |
|
|
func specNames(specs []ast.Spec) []string {
|
214 |
|
|
names := make([]string, 0, len(specs)) // reasonable estimate
|
215 |
|
|
for _, s := range specs {
|
216 |
|
|
// s guaranteed to be an *ast.ValueSpec by readValue
|
217 |
|
|
for _, ident := range s.(*ast.ValueSpec).Names {
|
218 |
|
|
names = append(names, ident.Name)
|
219 |
|
|
}
|
220 |
|
|
}
|
221 |
|
|
return names
|
222 |
|
|
}
|
223 |
|
|
|
224 |
|
|
// readValue processes a const or var declaration.
|
225 |
|
|
//
|
226 |
|
|
func (r *reader) readValue(decl *ast.GenDecl) {
|
227 |
|
|
// determine if decl should be associated with a type
|
228 |
|
|
// Heuristic: For each typed entry, determine the type name, if any.
|
229 |
|
|
// If there is exactly one type name that is sufficiently
|
230 |
|
|
// frequent, associate the decl with the respective type.
|
231 |
|
|
domName := ""
|
232 |
|
|
domFreq := 0
|
233 |
|
|
prev := ""
|
234 |
|
|
n := 0
|
235 |
|
|
for _, spec := range decl.Specs {
|
236 |
|
|
s, ok := spec.(*ast.ValueSpec)
|
237 |
|
|
if !ok {
|
238 |
|
|
continue // should not happen, but be conservative
|
239 |
|
|
}
|
240 |
|
|
name := ""
|
241 |
|
|
switch {
|
242 |
|
|
case s.Type != nil:
|
243 |
|
|
// a type is present; determine its name
|
244 |
|
|
if n, imp := baseTypeName(s.Type); !imp {
|
245 |
|
|
name = n
|
246 |
|
|
}
|
247 |
|
|
case decl.Tok == token.CONST:
|
248 |
|
|
// no type is present but we have a constant declaration;
|
249 |
|
|
// use the previous type name (w/o more type information
|
250 |
|
|
// we cannot handle the case of unnamed variables with
|
251 |
|
|
// initializer expressions except for some trivial cases)
|
252 |
|
|
name = prev
|
253 |
|
|
}
|
254 |
|
|
if name != "" {
|
255 |
|
|
// entry has a named type
|
256 |
|
|
if domName != "" && domName != name {
|
257 |
|
|
// more than one type name - do not associate
|
258 |
|
|
// with any type
|
259 |
|
|
domName = ""
|
260 |
|
|
break
|
261 |
|
|
}
|
262 |
|
|
domName = name
|
263 |
|
|
domFreq++
|
264 |
|
|
}
|
265 |
|
|
prev = name
|
266 |
|
|
n++
|
267 |
|
|
}
|
268 |
|
|
|
269 |
|
|
// nothing to do w/o a legal declaration
|
270 |
|
|
if n == 0 {
|
271 |
|
|
return
|
272 |
|
|
}
|
273 |
|
|
|
274 |
|
|
// determine values list with which to associate the Value for this decl
|
275 |
|
|
values := &r.values
|
276 |
|
|
const threshold = 0.75
|
277 |
|
|
if domName != "" && domFreq >= int(float64(len(decl.Specs))*threshold) {
|
278 |
|
|
// typed entries are sufficiently frequent
|
279 |
|
|
if typ := r.lookupType(domName); typ != nil {
|
280 |
|
|
values = &typ.values // associate with that type
|
281 |
|
|
}
|
282 |
|
|
}
|
283 |
|
|
|
284 |
|
|
*values = append(*values, &Value{
|
285 |
|
|
Doc: decl.Doc.Text(),
|
286 |
|
|
Names: specNames(decl.Specs),
|
287 |
|
|
Decl: decl,
|
288 |
|
|
order: len(*values),
|
289 |
|
|
})
|
290 |
|
|
decl.Doc = nil // doc consumed - remove from AST
|
291 |
|
|
}
|
292 |
|
|
|
293 |
|
|
// fields returns a struct's fields or an interface's methods.
|
294 |
|
|
//
|
295 |
|
|
func fields(typ ast.Expr) (list []*ast.Field, isStruct bool) {
|
296 |
|
|
var fields *ast.FieldList
|
297 |
|
|
switch t := typ.(type) {
|
298 |
|
|
case *ast.StructType:
|
299 |
|
|
fields = t.Fields
|
300 |
|
|
isStruct = true
|
301 |
|
|
case *ast.InterfaceType:
|
302 |
|
|
fields = t.Methods
|
303 |
|
|
}
|
304 |
|
|
if fields != nil {
|
305 |
|
|
list = fields.List
|
306 |
|
|
}
|
307 |
|
|
return
|
308 |
|
|
}
|
309 |
|
|
|
310 |
|
|
// readType processes a type declaration.
|
311 |
|
|
//
|
312 |
|
|
func (r *reader) readType(decl *ast.GenDecl, spec *ast.TypeSpec) {
|
313 |
|
|
typ := r.lookupType(spec.Name.Name)
|
314 |
|
|
if typ == nil {
|
315 |
|
|
return // no name or blank name - ignore the type
|
316 |
|
|
}
|
317 |
|
|
|
318 |
|
|
// A type should be added at most once, so info.decl
|
319 |
|
|
// should be nil - if it is not, simply overwrite it.
|
320 |
|
|
typ.decl = decl
|
321 |
|
|
|
322 |
|
|
// compute documentation
|
323 |
|
|
doc := spec.Doc
|
324 |
|
|
spec.Doc = nil // doc consumed - remove from AST
|
325 |
|
|
if doc == nil {
|
326 |
|
|
// no doc associated with the spec, use the declaration doc, if any
|
327 |
|
|
doc = decl.Doc
|
328 |
|
|
}
|
329 |
|
|
decl.Doc = nil // doc consumed - remove from AST
|
330 |
|
|
typ.doc = doc.Text()
|
331 |
|
|
|
332 |
|
|
// record anonymous fields (they may contribute methods)
|
333 |
|
|
// (some fields may have been recorded already when filtering
|
334 |
|
|
// exports, but that's ok)
|
335 |
|
|
var list []*ast.Field
|
336 |
|
|
list, typ.isStruct = fields(spec.Type)
|
337 |
|
|
for _, field := range list {
|
338 |
|
|
if len(field.Names) == 0 {
|
339 |
|
|
r.recordAnonymousField(typ, field.Type)
|
340 |
|
|
}
|
341 |
|
|
}
|
342 |
|
|
}
|
343 |
|
|
|
344 |
|
|
// readFunc processes a func or method declaration.
|
345 |
|
|
//
|
346 |
|
|
func (r *reader) readFunc(fun *ast.FuncDecl) {
|
347 |
|
|
// strip function body
|
348 |
|
|
fun.Body = nil
|
349 |
|
|
|
350 |
|
|
// associate methods with the receiver type, if any
|
351 |
|
|
if fun.Recv != nil {
|
352 |
|
|
// method
|
353 |
|
|
recvTypeName, imp := baseTypeName(fun.Recv.List[0].Type)
|
354 |
|
|
if imp {
|
355 |
|
|
// should not happen (incorrect AST);
|
356 |
|
|
// don't show this method
|
357 |
|
|
return
|
358 |
|
|
}
|
359 |
|
|
if typ := r.lookupType(recvTypeName); typ != nil {
|
360 |
|
|
typ.methods.set(fun)
|
361 |
|
|
}
|
362 |
|
|
// otherwise ignore the method
|
363 |
|
|
// TODO(gri): There may be exported methods of non-exported types
|
364 |
|
|
// that can be called because of exported values (consts, vars, or
|
365 |
|
|
// function results) of that type. Could determine if that is the
|
366 |
|
|
// case and then show those methods in an appropriate section.
|
367 |
|
|
return
|
368 |
|
|
}
|
369 |
|
|
|
370 |
|
|
// associate factory functions with the first visible result type, if any
|
371 |
|
|
if fun.Type.Results.NumFields() >= 1 {
|
372 |
|
|
res := fun.Type.Results.List[0]
|
373 |
|
|
if len(res.Names) <= 1 {
|
374 |
|
|
// exactly one (named or anonymous) result associated
|
375 |
|
|
// with the first type in result signature (there may
|
376 |
|
|
// be more than one result)
|
377 |
|
|
if n, imp := baseTypeName(res.Type); !imp && r.isVisible(n) {
|
378 |
|
|
if typ := r.lookupType(n); typ != nil {
|
379 |
|
|
// associate function with typ
|
380 |
|
|
typ.funcs.set(fun)
|
381 |
|
|
return
|
382 |
|
|
}
|
383 |
|
|
}
|
384 |
|
|
}
|
385 |
|
|
}
|
386 |
|
|
|
387 |
|
|
// just an ordinary function
|
388 |
|
|
r.funcs.set(fun)
|
389 |
|
|
}
|
390 |
|
|
|
391 |
|
|
var (
|
392 |
|
|
bug_markers = regexp.MustCompile("^/[/*][ \t]*BUG\\(.*\\):[ \t]*") // BUG(uid):
|
393 |
|
|
bug_content = regexp.MustCompile("[^ \n\r\t]+") // at least one non-whitespace char
|
394 |
|
|
)
|
395 |
|
|
|
396 |
|
|
// readFile adds the AST for a source file to the reader.
|
397 |
|
|
//
|
398 |
|
|
func (r *reader) readFile(src *ast.File) {
|
399 |
|
|
// add package documentation
|
400 |
|
|
if src.Doc != nil {
|
401 |
|
|
r.readDoc(src.Doc)
|
402 |
|
|
src.Doc = nil // doc consumed - remove from AST
|
403 |
|
|
}
|
404 |
|
|
|
405 |
|
|
// add all declarations
|
406 |
|
|
for _, decl := range src.Decls {
|
407 |
|
|
switch d := decl.(type) {
|
408 |
|
|
case *ast.GenDecl:
|
409 |
|
|
switch d.Tok {
|
410 |
|
|
case token.IMPORT:
|
411 |
|
|
// imports are handled individually
|
412 |
|
|
for _, spec := range d.Specs {
|
413 |
|
|
if s, ok := spec.(*ast.ImportSpec); ok {
|
414 |
|
|
if import_, err := strconv.Unquote(s.Path.Value); err == nil {
|
415 |
|
|
r.imports[import_] = 1
|
416 |
|
|
}
|
417 |
|
|
}
|
418 |
|
|
}
|
419 |
|
|
case token.CONST, token.VAR:
|
420 |
|
|
// constants and variables are always handled as a group
|
421 |
|
|
r.readValue(d)
|
422 |
|
|
case token.TYPE:
|
423 |
|
|
// types are handled individually
|
424 |
|
|
for _, spec := range d.Specs {
|
425 |
|
|
if s, ok := spec.(*ast.TypeSpec); ok {
|
426 |
|
|
// use an individual (possibly fake) declaration
|
427 |
|
|
// for each type; this also ensures that each type
|
428 |
|
|
// gets to (re-)use the declaration documentation
|
429 |
|
|
// if there's none associated with the spec itself
|
430 |
|
|
fake := &ast.GenDecl{
|
431 |
|
|
d.Doc, d.Pos(), token.TYPE, token.NoPos,
|
432 |
|
|
[]ast.Spec{s}, token.NoPos,
|
433 |
|
|
}
|
434 |
|
|
r.readType(fake, s)
|
435 |
|
|
}
|
436 |
|
|
}
|
437 |
|
|
}
|
438 |
|
|
case *ast.FuncDecl:
|
439 |
|
|
r.readFunc(d)
|
440 |
|
|
}
|
441 |
|
|
}
|
442 |
|
|
|
443 |
|
|
// collect BUG(...) comments
|
444 |
|
|
for _, c := range src.Comments {
|
445 |
|
|
text := c.List[0].Text
|
446 |
|
|
if m := bug_markers.FindStringIndex(text); m != nil {
|
447 |
|
|
// found a BUG comment; maybe empty
|
448 |
|
|
if btxt := text[m[1]:]; bug_content.MatchString(btxt) {
|
449 |
|
|
// non-empty BUG comment; collect comment without BUG prefix
|
450 |
|
|
list := append([]*ast.Comment(nil), c.List...) // make a copy
|
451 |
|
|
list[0].Text = text[m[1]:]
|
452 |
|
|
r.bugs = append(r.bugs, (&ast.CommentGroup{list}).Text())
|
453 |
|
|
}
|
454 |
|
|
}
|
455 |
|
|
}
|
456 |
|
|
src.Comments = nil // consumed unassociated comments - remove from AST
|
457 |
|
|
}
|
458 |
|
|
|
459 |
|
|
func (r *reader) readPackage(pkg *ast.Package, mode Mode) {
|
460 |
|
|
// initialize reader
|
461 |
|
|
r.filenames = make([]string, len(pkg.Files))
|
462 |
|
|
r.imports = make(map[string]int)
|
463 |
|
|
r.mode = mode
|
464 |
|
|
r.types = make(map[string]*namedType)
|
465 |
|
|
r.funcs = make(methodSet)
|
466 |
|
|
|
467 |
|
|
// sort package files before reading them so that the
|
468 |
|
|
// result result does not depend on map iteration order
|
469 |
|
|
i := 0
|
470 |
|
|
for filename := range pkg.Files {
|
471 |
|
|
r.filenames[i] = filename
|
472 |
|
|
i++
|
473 |
|
|
}
|
474 |
|
|
sort.Strings(r.filenames)
|
475 |
|
|
|
476 |
|
|
// process files in sorted order
|
477 |
|
|
for _, filename := range r.filenames {
|
478 |
|
|
f := pkg.Files[filename]
|
479 |
|
|
if mode&AllDecls == 0 {
|
480 |
|
|
r.fileExports(f)
|
481 |
|
|
}
|
482 |
|
|
r.readFile(f)
|
483 |
|
|
}
|
484 |
|
|
}
|
485 |
|
|
|
486 |
|
|
// ----------------------------------------------------------------------------
|
487 |
|
|
// Types
|
488 |
|
|
|
489 |
|
|
var predeclaredTypes = map[string]bool{
|
490 |
|
|
"bool": true,
|
491 |
|
|
"byte": true,
|
492 |
|
|
"complex64": true,
|
493 |
|
|
"complex128": true,
|
494 |
|
|
"error": true,
|
495 |
|
|
"float32": true,
|
496 |
|
|
"float64": true,
|
497 |
|
|
"int": true,
|
498 |
|
|
"int8": true,
|
499 |
|
|
"int16": true,
|
500 |
|
|
"int32": true,
|
501 |
|
|
"int64": true,
|
502 |
|
|
"rune": true,
|
503 |
|
|
"string": true,
|
504 |
|
|
"uint": true,
|
505 |
|
|
"uint8": true,
|
506 |
|
|
"uint16": true,
|
507 |
|
|
"uint32": true,
|
508 |
|
|
"uint64": true,
|
509 |
|
|
"uintptr": true,
|
510 |
|
|
}
|
511 |
|
|
|
512 |
|
|
func customizeRecv(f *Func, recvTypeName string, embeddedIsPtr bool, level int) *Func {
|
513 |
|
|
if f == nil || f.Decl == nil || f.Decl.Recv == nil || len(f.Decl.Recv.List) != 1 {
|
514 |
|
|
return f // shouldn't happen, but be safe
|
515 |
|
|
}
|
516 |
|
|
|
517 |
|
|
// copy existing receiver field and set new type
|
518 |
|
|
newField := *f.Decl.Recv.List[0]
|
519 |
|
|
_, origRecvIsPtr := newField.Type.(*ast.StarExpr)
|
520 |
|
|
var typ ast.Expr = ast.NewIdent(recvTypeName)
|
521 |
|
|
if !embeddedIsPtr && origRecvIsPtr {
|
522 |
|
|
typ = &ast.StarExpr{token.NoPos, typ}
|
523 |
|
|
}
|
524 |
|
|
newField.Type = typ
|
525 |
|
|
|
526 |
|
|
// copy existing receiver field list and set new receiver field
|
527 |
|
|
newFieldList := *f.Decl.Recv
|
528 |
|
|
newFieldList.List = []*ast.Field{&newField}
|
529 |
|
|
|
530 |
|
|
// copy existing function declaration and set new receiver field list
|
531 |
|
|
newFuncDecl := *f.Decl
|
532 |
|
|
newFuncDecl.Recv = &newFieldList
|
533 |
|
|
|
534 |
|
|
// copy existing function documentation and set new declaration
|
535 |
|
|
newF := *f
|
536 |
|
|
newF.Decl = &newFuncDecl
|
537 |
|
|
newF.Recv = recvString(typ)
|
538 |
|
|
// the Orig field never changes
|
539 |
|
|
newF.Level = level
|
540 |
|
|
|
541 |
|
|
return &newF
|
542 |
|
|
}
|
543 |
|
|
|
544 |
|
|
// collectEmbeddedMethods collects the embedded methods of typ in mset.
|
545 |
|
|
//
|
546 |
|
|
func (r *reader) collectEmbeddedMethods(mset methodSet, typ *namedType, recvTypeName string, embeddedIsPtr bool, level int) {
|
547 |
|
|
for embedded, isPtr := range typ.embedded {
|
548 |
|
|
// Once an embedded type is embedded as a pointer type
|
549 |
|
|
// all embedded types in those types are treated like
|
550 |
|
|
// pointer types for the purpose of the receiver type
|
551 |
|
|
// computation; i.e., embeddedIsPtr is sticky for this
|
552 |
|
|
// embedding hierarchy.
|
553 |
|
|
thisEmbeddedIsPtr := embeddedIsPtr || isPtr
|
554 |
|
|
for _, m := range embedded.methods {
|
555 |
|
|
// only top-level methods are embedded
|
556 |
|
|
if m.Level == 0 {
|
557 |
|
|
mset.add(customizeRecv(m, recvTypeName, thisEmbeddedIsPtr, level))
|
558 |
|
|
}
|
559 |
|
|
}
|
560 |
|
|
r.collectEmbeddedMethods(mset, embedded, recvTypeName, thisEmbeddedIsPtr, level+1)
|
561 |
|
|
}
|
562 |
|
|
}
|
563 |
|
|
|
564 |
|
|
// computeMethodSets determines the actual method sets for each type encountered.
|
565 |
|
|
//
|
566 |
|
|
func (r *reader) computeMethodSets() {
|
567 |
|
|
for _, t := range r.types {
|
568 |
|
|
// collect embedded methods for t
|
569 |
|
|
if t.isStruct {
|
570 |
|
|
// struct
|
571 |
|
|
r.collectEmbeddedMethods(t.methods, t, t.name, false, 1)
|
572 |
|
|
} else {
|
573 |
|
|
// interface
|
574 |
|
|
// TODO(gri) fix this
|
575 |
|
|
}
|
576 |
|
|
}
|
577 |
|
|
}
|
578 |
|
|
|
579 |
|
|
// cleanupTypes removes the association of functions and methods with
|
580 |
|
|
// types that have no declaration. Instead, these functions and methods
|
581 |
|
|
// are shown at the package level. It also removes types with missing
|
582 |
|
|
// declarations or which are not visible.
|
583 |
|
|
//
|
584 |
|
|
func (r *reader) cleanupTypes() {
|
585 |
|
|
for _, t := range r.types {
|
586 |
|
|
visible := r.isVisible(t.name)
|
587 |
|
|
if t.decl == nil && (predeclaredTypes[t.name] || t.isEmbedded && visible) {
|
588 |
|
|
// t.name is a predeclared type (and was not redeclared in this package),
|
589 |
|
|
// or it was embedded somewhere but its declaration is missing (because
|
590 |
|
|
// the AST is incomplete): move any associated values, funcs, and methods
|
591 |
|
|
// back to the top-level so that they are not lost.
|
592 |
|
|
// 1) move values
|
593 |
|
|
r.values = append(r.values, t.values...)
|
594 |
|
|
// 2) move factory functions
|
595 |
|
|
for name, f := range t.funcs {
|
596 |
|
|
// in a correct AST, package-level function names
|
597 |
|
|
// are all different - no need to check for conflicts
|
598 |
|
|
r.funcs[name] = f
|
599 |
|
|
}
|
600 |
|
|
// 3) move methods
|
601 |
|
|
for name, m := range t.methods {
|
602 |
|
|
// don't overwrite functions with the same name - drop them
|
603 |
|
|
if _, found := r.funcs[name]; !found {
|
604 |
|
|
r.funcs[name] = m
|
605 |
|
|
}
|
606 |
|
|
}
|
607 |
|
|
}
|
608 |
|
|
// remove types w/o declaration or which are not visible
|
609 |
|
|
if t.decl == nil || !visible {
|
610 |
|
|
delete(r.types, t.name)
|
611 |
|
|
}
|
612 |
|
|
}
|
613 |
|
|
}
|
614 |
|
|
|
615 |
|
|
// ----------------------------------------------------------------------------
|
616 |
|
|
// Sorting
|
617 |
|
|
|
618 |
|
|
type data struct {
|
619 |
|
|
n int
|
620 |
|
|
swap func(i, j int)
|
621 |
|
|
less func(i, j int) bool
|
622 |
|
|
}
|
623 |
|
|
|
624 |
|
|
func (d *data) Len() int { return d.n }
|
625 |
|
|
func (d *data) Swap(i, j int) { d.swap(i, j) }
|
626 |
|
|
func (d *data) Less(i, j int) bool { return d.less(i, j) }
|
627 |
|
|
|
628 |
|
|
// sortBy is a helper function for sorting
|
629 |
|
|
func sortBy(less func(i, j int) bool, swap func(i, j int), n int) {
|
630 |
|
|
sort.Sort(&data{n, swap, less})
|
631 |
|
|
}
|
632 |
|
|
|
633 |
|
|
func sortedKeys(m map[string]int) []string {
|
634 |
|
|
list := make([]string, len(m))
|
635 |
|
|
i := 0
|
636 |
|
|
for key := range m {
|
637 |
|
|
list[i] = key
|
638 |
|
|
i++
|
639 |
|
|
}
|
640 |
|
|
sort.Strings(list)
|
641 |
|
|
return list
|
642 |
|
|
}
|
643 |
|
|
|
644 |
|
|
// sortingName returns the name to use when sorting d into place.
|
645 |
|
|
//
|
646 |
|
|
func sortingName(d *ast.GenDecl) string {
|
647 |
|
|
if len(d.Specs) == 1 {
|
648 |
|
|
if s, ok := d.Specs[0].(*ast.ValueSpec); ok {
|
649 |
|
|
return s.Names[0].Name
|
650 |
|
|
}
|
651 |
|
|
}
|
652 |
|
|
return ""
|
653 |
|
|
}
|
654 |
|
|
|
655 |
|
|
func sortedValues(m []*Value, tok token.Token) []*Value {
|
656 |
|
|
list := make([]*Value, len(m)) // big enough in any case
|
657 |
|
|
i := 0
|
658 |
|
|
for _, val := range m {
|
659 |
|
|
if val.Decl.Tok == tok {
|
660 |
|
|
list[i] = val
|
661 |
|
|
i++
|
662 |
|
|
}
|
663 |
|
|
}
|
664 |
|
|
list = list[0:i]
|
665 |
|
|
|
666 |
|
|
sortBy(
|
667 |
|
|
func(i, j int) bool {
|
668 |
|
|
if ni, nj := sortingName(list[i].Decl), sortingName(list[j].Decl); ni != nj {
|
669 |
|
|
return ni < nj
|
670 |
|
|
}
|
671 |
|
|
return list[i].order < list[j].order
|
672 |
|
|
},
|
673 |
|
|
func(i, j int) { list[i], list[j] = list[j], list[i] },
|
674 |
|
|
len(list),
|
675 |
|
|
)
|
676 |
|
|
|
677 |
|
|
return list
|
678 |
|
|
}
|
679 |
|
|
|
680 |
|
|
func sortedTypes(m map[string]*namedType, allMethods bool) []*Type {
|
681 |
|
|
list := make([]*Type, len(m))
|
682 |
|
|
i := 0
|
683 |
|
|
for _, t := range m {
|
684 |
|
|
list[i] = &Type{
|
685 |
|
|
Doc: t.doc,
|
686 |
|
|
Name: t.name,
|
687 |
|
|
Decl: t.decl,
|
688 |
|
|
Consts: sortedValues(t.values, token.CONST),
|
689 |
|
|
Vars: sortedValues(t.values, token.VAR),
|
690 |
|
|
Funcs: sortedFuncs(t.funcs, true),
|
691 |
|
|
Methods: sortedFuncs(t.methods, allMethods),
|
692 |
|
|
}
|
693 |
|
|
i++
|
694 |
|
|
}
|
695 |
|
|
|
696 |
|
|
sortBy(
|
697 |
|
|
func(i, j int) bool { return list[i].Name < list[j].Name },
|
698 |
|
|
func(i, j int) { list[i], list[j] = list[j], list[i] },
|
699 |
|
|
len(list),
|
700 |
|
|
)
|
701 |
|
|
|
702 |
|
|
return list
|
703 |
|
|
}
|
704 |
|
|
|
705 |
|
|
func removeStar(s string) string {
|
706 |
|
|
if len(s) > 0 && s[0] == '*' {
|
707 |
|
|
return s[1:]
|
708 |
|
|
}
|
709 |
|
|
return s
|
710 |
|
|
}
|
711 |
|
|
|
712 |
|
|
func sortedFuncs(m methodSet, allMethods bool) []*Func {
|
713 |
|
|
list := make([]*Func, len(m))
|
714 |
|
|
i := 0
|
715 |
|
|
for _, m := range m {
|
716 |
|
|
// determine which methods to include
|
717 |
|
|
switch {
|
718 |
|
|
case m.Decl == nil:
|
719 |
|
|
// exclude conflict entry
|
720 |
|
|
case allMethods, m.Level == 0, !ast.IsExported(removeStar(m.Orig)):
|
721 |
|
|
// forced inclusion, method not embedded, or method
|
722 |
|
|
// embedded but original receiver type not exported
|
723 |
|
|
list[i] = m
|
724 |
|
|
i++
|
725 |
|
|
}
|
726 |
|
|
}
|
727 |
|
|
list = list[0:i]
|
728 |
|
|
sortBy(
|
729 |
|
|
func(i, j int) bool { return list[i].Name < list[j].Name },
|
730 |
|
|
func(i, j int) { list[i], list[j] = list[j], list[i] },
|
731 |
|
|
len(list),
|
732 |
|
|
)
|
733 |
|
|
return list
|
734 |
|
|
}
|