1 // 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.
15 // ----------------------------------------------------------------------------
16 // function/method sets
18 // Internally, we treat functions like methods and collect them in method sets.
20 // A 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).
23 type methodSet map[string]*Func
25 // recvString returns a string representation of recv of the
26 // form "T", "*T", or "BADRECV" (if not a proper receiver type).
28 func recvString(recv ast.Expr) string {
29 switch t := recv.(type) {
33 return "*" + recvString(t.X)
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.
42 func (mset methodSet) set(f *ast.FuncDecl) {
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.
52 // function doesn't exist or has no documentation; use f
56 // be careful in case of incorrect ASTs
57 if list := f.Recv.List; len(list) == 1 {
60 recv = recvString(typ)
69 f.Doc = nil // doc consumed - remove from AST
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
76 func (mset methodSet) add(m *Func) {
78 if old == nil || m.Level < old.Level {
82 if old != nil && m.Level == old.Level {
83 // conflict - mark it using a method with nil Decl
91 // ----------------------------------------------------------------------------
94 // baseTypeName returns the name of the base type of x (or "")
95 // and whether the type is imported or not.
97 func baseTypeName(x ast.Expr) (name string, imported bool) {
98 switch t := x.(type) {
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
108 return baseTypeName(t.X)
113 // An embeddedSet describes a set of embedded types.
114 type embeddedSet map[*namedType]bool
116 // A namedType represents a named unqualified (package local, or possibly
117 // predeclared) type. The namedType for a type name is always found via
118 // reader.lookupType.
120 type namedType struct {
121 doc string // doc comment for type
122 name string // type name
123 decl *ast.GenDecl // nil if declaration hasn't been seen yet
125 isEmbedded bool // true if this type is embedded
126 isStruct bool // true if this type is a struct
127 embedded embeddedSet // true if the embedded type is a pointer
129 // associated declarations
130 values []*Value // consts and vars
135 // ----------------------------------------------------------------------------
138 // reader accumulates documentation for a single package.
139 // It modifies the AST: Comments (declaration documentation)
140 // that have been collected by the reader are set to nil
141 // in the respective AST nodes so that they are not printed
142 // twice (once when printing the documentation and once when
143 // printing the corresponding AST node).
148 // package properties
149 doc string // package documentation, if any
154 imports map[string]int
155 values []*Value // consts and vars
156 types map[string]*namedType
159 // support for package-local error type declarations
160 errorDecl bool // if set, type "error" was declared locally
161 fixlist []*ast.InterfaceType // list of interfaces containing anonymous field "error"
164 func (r *reader) isVisible(name string) bool {
165 return r.mode&AllDecls != 0 || ast.IsExported(name)
168 // lookupType returns the base type with the given name.
169 // If the base type has not been encountered yet, a new
170 // type with the given name but no associated declaration
171 // is added to the type map.
173 func (r *reader) lookupType(name string) *namedType {
174 if name == "" || name == "_" {
175 return nil // no type docs for anonymous types
177 if typ, found := r.types[name]; found {
180 // type not found - add one without declaration
183 embedded: make(embeddedSet),
184 funcs: make(methodSet),
185 methods: make(methodSet),
191 // recordAnonymousField registers fieldType as the type of an
192 // anonymous field in the parent type. If the field is imported
193 // (qualified name) or the parent is nil, the field is ignored.
194 // The function returns the field name.
196 func (r *reader) recordAnonymousField(parent *namedType, fieldType ast.Expr) (fname string) {
197 fname, imp := baseTypeName(fieldType)
198 if parent == nil || imp {
201 if ftype := r.lookupType(fname); ftype != nil {
202 ftype.isEmbedded = true
203 _, ptr := fieldType.(*ast.StarExpr)
204 parent.embedded[ftype] = ptr
209 func (r *reader) readDoc(comment *ast.CommentGroup) {
210 // By convention there should be only one package comment
211 // but collect all of them if there are more then one.
212 text := comment.Text()
220 func (r *reader) remember(typ *ast.InterfaceType) {
221 r.fixlist = append(r.fixlist, typ)
224 func specNames(specs []ast.Spec) []string {
225 names := make([]string, 0, len(specs)) // reasonable estimate
226 for _, s := range specs {
227 // s guaranteed to be an *ast.ValueSpec by readValue
228 for _, ident := range s.(*ast.ValueSpec).Names {
229 names = append(names, ident.Name)
235 // readValue processes a const or var declaration.
237 func (r *reader) readValue(decl *ast.GenDecl) {
238 // determine if decl should be associated with a type
239 // Heuristic: For each typed entry, determine the type name, if any.
240 // If there is exactly one type name that is sufficiently
241 // frequent, associate the decl with the respective type.
246 for _, spec := range decl.Specs {
247 s, ok := spec.(*ast.ValueSpec)
249 continue // should not happen, but be conservative
254 // a type is present; determine its name
255 if n, imp := baseTypeName(s.Type); !imp {
258 case decl.Tok == token.CONST:
259 // no type is present but we have a constant declaration;
260 // use the previous type name (w/o more type information
261 // we cannot handle the case of unnamed variables with
262 // initializer expressions except for some trivial cases)
266 // entry has a named type
267 if domName != "" && domName != name {
268 // more than one type name - do not associate
280 // nothing to do w/o a legal declaration
285 // determine values list with which to associate the Value for this decl
287 const threshold = 0.75
288 if domName != "" && r.isVisible(domName) && domFreq >= int(float64(len(decl.Specs))*threshold) {
289 // typed entries are sufficiently frequent
290 if typ := r.lookupType(domName); typ != nil {
291 values = &typ.values // associate with that type
295 *values = append(*values, &Value{
296 Doc: decl.Doc.Text(),
297 Names: specNames(decl.Specs),
301 decl.Doc = nil // doc consumed - remove from AST
304 // fields returns a struct's fields or an interface's methods.
306 func fields(typ ast.Expr) (list []*ast.Field, isStruct bool) {
307 var fields *ast.FieldList
308 switch t := typ.(type) {
309 case *ast.StructType:
312 case *ast.InterfaceType:
321 // readType processes a type declaration.
323 func (r *reader) readType(decl *ast.GenDecl, spec *ast.TypeSpec) {
324 typ := r.lookupType(spec.Name.Name)
326 return // no name or blank name - ignore the type
329 // A type should be added at most once, so typ.decl
330 // should be nil - if it is not, simply overwrite it.
333 // compute documentation
335 spec.Doc = nil // doc consumed - remove from AST
337 // no doc associated with the spec, use the declaration doc, if any
340 decl.Doc = nil // doc consumed - remove from AST
343 // record anonymous fields (they may contribute methods)
344 // (some fields may have been recorded already when filtering
345 // exports, but that's ok)
346 var list []*ast.Field
347 list, typ.isStruct = fields(spec.Type)
348 for _, field := range list {
349 if len(field.Names) == 0 {
350 r.recordAnonymousField(typ, field.Type)
355 // readFunc processes a func or method declaration.
357 func (r *reader) readFunc(fun *ast.FuncDecl) {
358 // strip function body
361 // associate methods with the receiver type, if any
364 recvTypeName, imp := baseTypeName(fun.Recv.List[0].Type)
366 // should not happen (incorrect AST);
367 // don't show this method
370 if typ := r.lookupType(recvTypeName); typ != nil {
373 // otherwise ignore the method
374 // TODO(gri): There may be exported methods of non-exported types
375 // that can be called because of exported values (consts, vars, or
376 // function results) of that type. Could determine if that is the
377 // case and then show those methods in an appropriate section.
381 // associate factory functions with the first visible result type, if any
382 if fun.Type.Results.NumFields() >= 1 {
383 res := fun.Type.Results.List[0]
384 if len(res.Names) <= 1 {
385 // exactly one (named or anonymous) result associated
386 // with the first type in result signature (there may
387 // be more than one result)
388 if n, imp := baseTypeName(res.Type); !imp && r.isVisible(n) {
389 if typ := r.lookupType(n); typ != nil {
390 // associate function with typ
398 // just an ordinary function
403 bug_markers = regexp.MustCompile("^/[/*][ \t]*BUG\\(.*\\):[ \t]*") // BUG(uid):
404 bug_content = regexp.MustCompile("[^ \n\r\t]+") // at least one non-whitespace char
407 // readFile adds the AST for a source file to the reader.
409 func (r *reader) readFile(src *ast.File) {
410 // add package documentation
413 src.Doc = nil // doc consumed - remove from AST
416 // add all declarations
417 for _, decl := range src.Decls {
418 switch d := decl.(type) {
422 // imports are handled individually
423 for _, spec := range d.Specs {
424 if s, ok := spec.(*ast.ImportSpec); ok {
425 if import_, err := strconv.Unquote(s.Path.Value); err == nil {
426 r.imports[import_] = 1
430 case token.CONST, token.VAR:
431 // constants and variables are always handled as a group
434 // types are handled individually
435 if len(d.Specs) == 1 && !d.Lparen.IsValid() {
436 // common case: single declaration w/o parentheses
437 // (if a single declaration is parenthesized,
438 // create a new fake declaration below, so that
439 // go/doc type declarations always appear w/o
441 if s, ok := d.Specs[0].(*ast.TypeSpec); ok {
446 for _, spec := range d.Specs {
447 if s, ok := spec.(*ast.TypeSpec); ok {
448 // use an individual (possibly fake) declaration
449 // for each type; this also ensures that each type
450 // gets to (re-)use the declaration documentation
451 // if there's none associated with the spec itself
452 fake := &ast.GenDecl{
454 // don't use the existing TokPos because it
455 // will lead to the wrong selection range for
456 // the fake declaration if there are more
457 // than one type in the group (this affects
458 // src/cmd/godoc/godoc.go's posLink_urlFunc)
461 Specs: []ast.Spec{s},
472 // collect BUG(...) comments
473 for _, c := range src.Comments {
474 text := c.List[0].Text
475 if m := bug_markers.FindStringIndex(text); m != nil {
476 // found a BUG comment; maybe empty
477 if btxt := text[m[1]:]; bug_content.MatchString(btxt) {
478 // non-empty BUG comment; collect comment without BUG prefix
479 list := append([]*ast.Comment(nil), c.List...) // make a copy
480 list[0].Text = text[m[1]:]
481 r.bugs = append(r.bugs, (&ast.CommentGroup{List: list}).Text())
485 src.Comments = nil // consumed unassociated comments - remove from AST
488 func (r *reader) readPackage(pkg *ast.Package, mode Mode) {
490 r.filenames = make([]string, len(pkg.Files))
491 r.imports = make(map[string]int)
493 r.types = make(map[string]*namedType)
494 r.funcs = make(methodSet)
496 // sort package files before reading them so that the
497 // result result does not depend on map iteration order
499 for filename := range pkg.Files {
500 r.filenames[i] = filename
503 sort.Strings(r.filenames)
505 // process files in sorted order
506 for _, filename := range r.filenames {
507 f := pkg.Files[filename]
508 if mode&AllDecls == 0 {
515 // ----------------------------------------------------------------------------
518 var predeclaredTypes = map[string]bool{
541 func customizeRecv(f *Func, recvTypeName string, embeddedIsPtr bool, level int) *Func {
542 if f == nil || f.Decl == nil || f.Decl.Recv == nil || len(f.Decl.Recv.List) != 1 {
543 return f // shouldn't happen, but be safe
546 // copy existing receiver field and set new type
547 newField := *f.Decl.Recv.List[0]
548 _, origRecvIsPtr := newField.Type.(*ast.StarExpr)
549 var typ ast.Expr = ast.NewIdent(recvTypeName)
550 if !embeddedIsPtr && origRecvIsPtr {
551 typ = &ast.StarExpr{X: typ}
555 // copy existing receiver field list and set new receiver field
556 newFieldList := *f.Decl.Recv
557 newFieldList.List = []*ast.Field{&newField}
559 // copy existing function declaration and set new receiver field list
560 newFuncDecl := *f.Decl
561 newFuncDecl.Recv = &newFieldList
563 // copy existing function documentation and set new declaration
565 newF.Decl = &newFuncDecl
566 newF.Recv = recvString(typ)
567 // the Orig field never changes
573 // collectEmbeddedMethods collects the embedded methods of typ in mset.
575 func (r *reader) collectEmbeddedMethods(mset methodSet, typ *namedType, recvTypeName string, embeddedIsPtr bool, level int, visited embeddedSet) {
577 for embedded, isPtr := range typ.embedded {
578 // Once an embedded type is embedded as a pointer type
579 // all embedded types in those types are treated like
580 // pointer types for the purpose of the receiver type
581 // computation; i.e., embeddedIsPtr is sticky for this
582 // embedding hierarchy.
583 thisEmbeddedIsPtr := embeddedIsPtr || isPtr
584 for _, m := range embedded.methods {
585 // only top-level methods are embedded
587 mset.add(customizeRecv(m, recvTypeName, thisEmbeddedIsPtr, level))
590 if !visited[embedded] {
591 r.collectEmbeddedMethods(mset, embedded, recvTypeName, thisEmbeddedIsPtr, level+1, visited)
597 // computeMethodSets determines the actual method sets for each type encountered.
599 func (r *reader) computeMethodSets() {
600 for _, t := range r.types {
601 // collect embedded methods for t
604 r.collectEmbeddedMethods(t.methods, t, t.name, false, 1, make(embeddedSet))
607 // TODO(gri) fix this
611 // if error was declared locally, don't treat it as exported field anymore
613 for _, ityp := range r.fixlist {
614 removeErrorField(ityp)
619 // cleanupTypes removes the association of functions and methods with
620 // types that have no declaration. Instead, these functions and methods
621 // are shown at the package level. It also removes types with missing
622 // declarations or which are not visible.
624 func (r *reader) cleanupTypes() {
625 for _, t := range r.types {
626 visible := r.isVisible(t.name)
627 if t.decl == nil && (predeclaredTypes[t.name] || t.isEmbedded && visible) {
628 // t.name is a predeclared type (and was not redeclared in this package),
629 // or it was embedded somewhere but its declaration is missing (because
630 // the AST is incomplete): move any associated values, funcs, and methods
631 // back to the top-level so that they are not lost.
633 r.values = append(r.values, t.values...)
634 // 2) move factory functions
635 for name, f := range t.funcs {
636 // in a correct AST, package-level function names
637 // are all different - no need to check for conflicts
641 for name, m := range t.methods {
642 // don't overwrite functions with the same name - drop them
643 if _, found := r.funcs[name]; !found {
648 // remove types w/o declaration or which are not visible
649 if t.decl == nil || !visible {
650 delete(r.types, t.name)
655 // ----------------------------------------------------------------------------
661 less func(i, j int) bool
664 func (d *data) Len() int { return d.n }
665 func (d *data) Swap(i, j int) { d.swap(i, j) }
666 func (d *data) Less(i, j int) bool { return d.less(i, j) }
668 // sortBy is a helper function for sorting
669 func sortBy(less func(i, j int) bool, swap func(i, j int), n int) {
670 sort.Sort(&data{n, swap, less})
673 func sortedKeys(m map[string]int) []string {
674 list := make([]string, len(m))
684 // sortingName returns the name to use when sorting d into place.
686 func sortingName(d *ast.GenDecl) string {
687 if len(d.Specs) == 1 {
688 if s, ok := d.Specs[0].(*ast.ValueSpec); ok {
689 return s.Names[0].Name
695 func sortedValues(m []*Value, tok token.Token) []*Value {
696 list := make([]*Value, len(m)) // big enough in any case
698 for _, val := range m {
699 if val.Decl.Tok == tok {
707 func(i, j int) bool {
708 if ni, nj := sortingName(list[i].Decl), sortingName(list[j].Decl); ni != nj {
711 return list[i].order < list[j].order
713 func(i, j int) { list[i], list[j] = list[j], list[i] },
720 func sortedTypes(m map[string]*namedType, allMethods bool) []*Type {
721 list := make([]*Type, len(m))
723 for _, t := range m {
728 Consts: sortedValues(t.values, token.CONST),
729 Vars: sortedValues(t.values, token.VAR),
730 Funcs: sortedFuncs(t.funcs, true),
731 Methods: sortedFuncs(t.methods, allMethods),
737 func(i, j int) bool { return list[i].Name < list[j].Name },
738 func(i, j int) { list[i], list[j] = list[j], list[i] },
745 func removeStar(s string) string {
746 if len(s) > 0 && s[0] == '*' {
752 func sortedFuncs(m methodSet, allMethods bool) []*Func {
753 list := make([]*Func, len(m))
755 for _, m := range m {
756 // determine which methods to include
759 // exclude conflict entry
760 case allMethods, m.Level == 0, !ast.IsExported(removeStar(m.Orig)):
761 // forced inclusion, method not embedded, or method
762 // embedded but original receiver type not exported
769 func(i, j int) bool { return list[i].Name < list[j].Name },
770 func(i, j int) { list[i], list[j] = list[j], list[i] },