Source file
src/go/doc/reader.go
Documentation: go/doc
1
2
3
4
5 package doc
6
7 import (
8 "fmt"
9 "go/ast"
10 "go/token"
11 "internal/lazyregexp"
12 "path"
13 "sort"
14 "strconv"
15 "strings"
16 "unicode"
17 "unicode/utf8"
18 )
19
20
21
22
23
24
25
26
27 type methodSet map[string]*Func
28
29
30
31 func recvString(recv ast.Expr) string {
32 switch t := recv.(type) {
33 case *ast.Ident:
34 return t.Name
35 case *ast.StarExpr:
36 return "*" + recvString(t.X)
37 case *ast.IndexExpr:
38
39 return fmt.Sprintf("%s[%s]", recvString(t.X), recvParam(t.Index))
40 case *ast.IndexListExpr:
41
42 if len(t.Indices) > 0 {
43 var b strings.Builder
44 b.WriteString(recvString(t.X))
45 b.WriteByte('[')
46 b.WriteString(recvParam(t.Indices[0]))
47 for _, e := range t.Indices[1:] {
48 b.WriteString(", ")
49 b.WriteString(recvParam(e))
50 }
51 b.WriteByte(']')
52 return b.String()
53 }
54 }
55 return "BADRECV"
56 }
57
58 func recvParam(p ast.Expr) string {
59 if id, ok := p.(*ast.Ident); ok {
60 return id.Name
61 }
62 return "BADPARAM"
63 }
64
65
66
67
68
69 func (mset methodSet) set(f *ast.FuncDecl, preserveAST bool) {
70 name := f.Name.Name
71 if g := mset[name]; g != nil && g.Doc != "" {
72
73
74
75
76
77 return
78 }
79
80 recv := ""
81 if f.Recv != nil {
82 var typ ast.Expr
83
84 if list := f.Recv.List; len(list) == 1 {
85 typ = list[0].Type
86 }
87 recv = recvString(typ)
88 }
89 mset[name] = &Func{
90 Doc: f.Doc.Text(),
91 Name: name,
92 Decl: f,
93 Recv: recv,
94 Orig: recv,
95 }
96 if !preserveAST {
97 f.Doc = nil
98 }
99 }
100
101
102
103
104 func (mset methodSet) add(m *Func) {
105 old := mset[m.Name]
106 if old == nil || m.Level < old.Level {
107 mset[m.Name] = m
108 return
109 }
110 if m.Level == old.Level {
111
112 mset[m.Name] = &Func{
113 Name: m.Name,
114 Level: m.Level,
115 }
116 }
117 }
118
119
120
121
122
123
124 func baseTypeName(x ast.Expr) (name string, imported bool) {
125 switch t := x.(type) {
126 case *ast.Ident:
127 return t.Name, false
128 case *ast.IndexExpr:
129 return baseTypeName(t.X)
130 case *ast.IndexListExpr:
131 return baseTypeName(t.X)
132 case *ast.SelectorExpr:
133 if _, ok := t.X.(*ast.Ident); ok {
134
135
136 return t.Sel.Name, true
137 }
138 case *ast.ParenExpr:
139 return baseTypeName(t.X)
140 case *ast.StarExpr:
141 return baseTypeName(t.X)
142 }
143 return "", false
144 }
145
146
147 type embeddedSet map[*namedType]bool
148
149
150
151
152 type namedType struct {
153 doc string
154 name string
155 decl *ast.GenDecl
156
157 isEmbedded bool
158 isStruct bool
159 embedded embeddedSet
160
161
162 values []*Value
163 funcs methodSet
164 methods methodSet
165 }
166
167
168
169
170
171
172
173
174
175
176 type reader struct {
177 mode Mode
178
179
180 doc string
181 filenames []string
182 notes map[string][]*Note
183
184
185 imports map[string]int
186 hasDotImp bool
187 importByName map[string]string
188
189
190 values []*Value
191 order int
192 types map[string]*namedType
193 funcs methodSet
194
195
196 shadowedPredecl map[string]bool
197 fixmap map[string][]*ast.InterfaceType
198 }
199
200 func (r *reader) isVisible(name string) bool {
201 return r.mode&AllDecls != 0 || token.IsExported(name)
202 }
203
204
205
206
207
208 func (r *reader) lookupType(name string) *namedType {
209 if name == "" || name == "_" {
210 return nil
211 }
212 if typ, found := r.types[name]; found {
213 return typ
214 }
215
216 typ := &namedType{
217 name: name,
218 embedded: make(embeddedSet),
219 funcs: make(methodSet),
220 methods: make(methodSet),
221 }
222 r.types[name] = typ
223 return typ
224 }
225
226
227
228
229
230 func (r *reader) recordAnonymousField(parent *namedType, fieldType ast.Expr) (fname string) {
231 fname, imp := baseTypeName(fieldType)
232 if parent == nil || imp {
233 return
234 }
235 if ftype := r.lookupType(fname); ftype != nil {
236 ftype.isEmbedded = true
237 _, ptr := fieldType.(*ast.StarExpr)
238 parent.embedded[ftype] = ptr
239 }
240 return
241 }
242
243 func (r *reader) readDoc(comment *ast.CommentGroup) {
244
245
246 text := comment.Text()
247 if r.doc == "" {
248 r.doc = text
249 return
250 }
251 r.doc += "\n" + text
252 }
253
254 func (r *reader) remember(predecl string, typ *ast.InterfaceType) {
255 if r.fixmap == nil {
256 r.fixmap = make(map[string][]*ast.InterfaceType)
257 }
258 r.fixmap[predecl] = append(r.fixmap[predecl], typ)
259 }
260
261 func specNames(specs []ast.Spec) []string {
262 names := make([]string, 0, len(specs))
263 for _, s := range specs {
264
265 for _, ident := range s.(*ast.ValueSpec).Names {
266 names = append(names, ident.Name)
267 }
268 }
269 return names
270 }
271
272
273 func (r *reader) readValue(decl *ast.GenDecl) {
274
275
276
277
278 domName := ""
279 domFreq := 0
280 prev := ""
281 n := 0
282 for _, spec := range decl.Specs {
283 s, ok := spec.(*ast.ValueSpec)
284 if !ok {
285 continue
286 }
287 name := ""
288 switch {
289 case s.Type != nil:
290
291 if n, imp := baseTypeName(s.Type); !imp {
292 name = n
293 }
294 case decl.Tok == token.CONST && len(s.Values) == 0:
295
296
297 name = prev
298 }
299 if name != "" {
300
301 if domName != "" && domName != name {
302
303
304 domName = ""
305 break
306 }
307 domName = name
308 domFreq++
309 }
310 prev = name
311 n++
312 }
313
314
315 if n == 0 {
316 return
317 }
318
319
320 values := &r.values
321 const threshold = 0.75
322 if domName != "" && r.isVisible(domName) && domFreq >= int(float64(len(decl.Specs))*threshold) {
323
324 if typ := r.lookupType(domName); typ != nil {
325 values = &typ.values
326 }
327 }
328
329 *values = append(*values, &Value{
330 Doc: decl.Doc.Text(),
331 Names: specNames(decl.Specs),
332 Decl: decl,
333 order: r.order,
334 })
335 if r.mode&PreserveAST == 0 {
336 decl.Doc = nil
337 }
338
339
340
341
342 r.order++
343 }
344
345
346 func fields(typ ast.Expr) (list []*ast.Field, isStruct bool) {
347 var fields *ast.FieldList
348 switch t := typ.(type) {
349 case *ast.StructType:
350 fields = t.Fields
351 isStruct = true
352 case *ast.InterfaceType:
353 fields = t.Methods
354 }
355 if fields != nil {
356 list = fields.List
357 }
358 return
359 }
360
361
362 func (r *reader) readType(decl *ast.GenDecl, spec *ast.TypeSpec) {
363 typ := r.lookupType(spec.Name.Name)
364 if typ == nil {
365 return
366 }
367
368
369
370 typ.decl = decl
371
372
373 doc := spec.Doc
374 if doc == nil {
375
376 doc = decl.Doc
377 }
378 if r.mode&PreserveAST == 0 {
379 spec.Doc = nil
380 decl.Doc = nil
381 }
382 typ.doc = doc.Text()
383
384
385
386
387 var list []*ast.Field
388 list, typ.isStruct = fields(spec.Type)
389 for _, field := range list {
390 if len(field.Names) == 0 {
391 r.recordAnonymousField(typ, field.Type)
392 }
393 }
394 }
395
396
397 func (r *reader) isPredeclared(n string) bool {
398 return predeclaredTypes[n] && r.types[n] == nil
399 }
400
401
402 func (r *reader) readFunc(fun *ast.FuncDecl) {
403
404 if r.mode&PreserveAST == 0 {
405 fun.Body = nil
406 }
407
408
409 if fun.Recv != nil {
410
411 if len(fun.Recv.List) == 0 {
412
413
414 return
415 }
416 recvTypeName, imp := baseTypeName(fun.Recv.List[0].Type)
417 if imp {
418
419
420 return
421 }
422 if typ := r.lookupType(recvTypeName); typ != nil {
423 typ.methods.set(fun, r.mode&PreserveAST != 0)
424 }
425
426
427
428
429
430 return
431 }
432
433
434
435 if fun.Type.Results.NumFields() >= 1 {
436 var typ *namedType
437 numResultTypes := 0
438 for _, res := range fun.Type.Results.List {
439 factoryType := res.Type
440 if t, ok := factoryType.(*ast.ArrayType); ok {
441
442
443 factoryType = t.Elt
444 }
445 if n, imp := baseTypeName(factoryType); !imp && r.isVisible(n) && !r.isPredeclared(n) {
446 if lookupTypeParam(n, fun.Type.TypeParams) != nil {
447
448
449 continue
450 }
451 if t := r.lookupType(n); t != nil {
452 typ = t
453 numResultTypes++
454 if numResultTypes > 1 {
455 break
456 }
457 }
458 }
459 }
460
461
462 if numResultTypes == 1 {
463 typ.funcs.set(fun, r.mode&PreserveAST != 0)
464 return
465 }
466 }
467
468
469 r.funcs.set(fun, r.mode&PreserveAST != 0)
470 }
471
472
473
474 func lookupTypeParam(name string, tparams *ast.FieldList) *ast.Ident {
475 if tparams == nil {
476 return nil
477 }
478 for _, field := range tparams.List {
479 for _, id := range field.Names {
480 if id.Name == name {
481 return id
482 }
483 }
484 }
485 return nil
486 }
487
488 var (
489 noteMarker = `([A-Z][A-Z]+)\(([^)]+)\):?`
490 noteMarkerRx = lazyregexp.New(`^[ \t]*` + noteMarker)
491 noteCommentRx = lazyregexp.New(`^/[/*][ \t]*` + noteMarker)
492 )
493
494
495
496 func clean(s string) string {
497 var b []byte
498 p := byte(' ')
499 for i := 0; i < len(s); i++ {
500 q := s[i]
501 if q == '\r' || q == '\t' {
502 q = ' '
503 }
504 if q != ' ' || p != ' ' {
505 b = append(b, q)
506 p = q
507 }
508 }
509
510 if n := len(b); n > 0 && p == ' ' {
511 b = b[0 : n-1]
512 }
513 return string(b)
514 }
515
516
517 func (r *reader) readNote(list []*ast.Comment) {
518 text := (&ast.CommentGroup{List: list}).Text()
519 if m := noteMarkerRx.FindStringSubmatchIndex(text); m != nil {
520
521
522
523
524 body := clean(text[m[1]:])
525 if body != "" {
526 marker := text[m[2]:m[3]]
527 r.notes[marker] = append(r.notes[marker], &Note{
528 Pos: list[0].Pos(),
529 End: list[len(list)-1].End(),
530 UID: text[m[4]:m[5]],
531 Body: body,
532 })
533 }
534 }
535 }
536
537
538
539
540
541
542 func (r *reader) readNotes(comments []*ast.CommentGroup) {
543 for _, group := range comments {
544 i := -1
545 list := group.List
546 for j, c := range list {
547 if noteCommentRx.MatchString(c.Text) {
548 if i >= 0 {
549 r.readNote(list[i:j])
550 }
551 i = j
552 }
553 }
554 if i >= 0 {
555 r.readNote(list[i:])
556 }
557 }
558 }
559
560
561 func (r *reader) readFile(src *ast.File) {
562
563 if src.Doc != nil {
564 r.readDoc(src.Doc)
565 if r.mode&PreserveAST == 0 {
566 src.Doc = nil
567 }
568 }
569
570
571 for _, decl := range src.Decls {
572 switch d := decl.(type) {
573 case *ast.GenDecl:
574 switch d.Tok {
575 case token.IMPORT:
576
577 for _, spec := range d.Specs {
578 if s, ok := spec.(*ast.ImportSpec); ok {
579 if import_, err := strconv.Unquote(s.Path.Value); err == nil {
580 r.imports[import_] = 1
581 var name string
582 if s.Name != nil {
583 name = s.Name.Name
584 if name == "." {
585 r.hasDotImp = true
586 }
587 }
588 if name != "." {
589 if name == "" {
590 name = assumedPackageName(import_)
591 }
592 old, ok := r.importByName[name]
593 if !ok {
594 r.importByName[name] = import_
595 } else if old != import_ && old != "" {
596 r.importByName[name] = ""
597 }
598 }
599 }
600 }
601 }
602 case token.CONST, token.VAR:
603
604 r.readValue(d)
605 case token.TYPE:
606
607 if len(d.Specs) == 1 && !d.Lparen.IsValid() {
608
609
610
611
612
613 if s, ok := d.Specs[0].(*ast.TypeSpec); ok {
614 r.readType(d, s)
615 }
616 break
617 }
618 for _, spec := range d.Specs {
619 if s, ok := spec.(*ast.TypeSpec); ok {
620
621
622
623
624 fake := &ast.GenDecl{
625 Doc: d.Doc,
626
627
628
629
630
631 TokPos: s.Pos(),
632 Tok: token.TYPE,
633 Specs: []ast.Spec{s},
634 }
635 r.readType(fake, s)
636 }
637 }
638 }
639 }
640 }
641
642
643 r.readNotes(src.Comments)
644 if r.mode&PreserveAST == 0 {
645 src.Comments = nil
646 }
647 }
648
649 func (r *reader) readPackage(pkg *ast.Package, mode Mode) {
650
651 r.filenames = make([]string, len(pkg.Files))
652 r.imports = make(map[string]int)
653 r.mode = mode
654 r.types = make(map[string]*namedType)
655 r.funcs = make(methodSet)
656 r.notes = make(map[string][]*Note)
657 r.importByName = make(map[string]string)
658
659
660
661 i := 0
662 for filename := range pkg.Files {
663 r.filenames[i] = filename
664 i++
665 }
666 sort.Strings(r.filenames)
667
668
669 for _, filename := range r.filenames {
670 f := pkg.Files[filename]
671 if mode&AllDecls == 0 {
672 r.fileExports(f)
673 }
674 r.readFile(f)
675 }
676
677 for name, path := range r.importByName {
678 if path == "" {
679 delete(r.importByName, name)
680 }
681 }
682
683
684 for _, f := range pkg.Files {
685 for _, decl := range f.Decls {
686 if d, ok := decl.(*ast.FuncDecl); ok {
687 r.readFunc(d)
688 }
689 }
690 }
691 }
692
693
694
695
696 func customizeRecv(f *Func, recvTypeName string, embeddedIsPtr bool, level int) *Func {
697 if f == nil || f.Decl == nil || f.Decl.Recv == nil || len(f.Decl.Recv.List) != 1 {
698 return f
699 }
700
701
702 newField := *f.Decl.Recv.List[0]
703 origPos := newField.Type.Pos()
704 _, origRecvIsPtr := newField.Type.(*ast.StarExpr)
705 newIdent := &ast.Ident{NamePos: origPos, Name: recvTypeName}
706 var typ ast.Expr = newIdent
707 if !embeddedIsPtr && origRecvIsPtr {
708 newIdent.NamePos++
709 typ = &ast.StarExpr{Star: origPos, X: newIdent}
710 }
711 newField.Type = typ
712
713
714 newFieldList := *f.Decl.Recv
715 newFieldList.List = []*ast.Field{&newField}
716
717
718 newFuncDecl := *f.Decl
719 newFuncDecl.Recv = &newFieldList
720
721
722 newF := *f
723 newF.Decl = &newFuncDecl
724 newF.Recv = recvString(typ)
725
726 newF.Level = level
727
728 return &newF
729 }
730
731
732 func (r *reader) collectEmbeddedMethods(mset methodSet, typ *namedType, recvTypeName string, embeddedIsPtr bool, level int, visited embeddedSet) {
733 visited[typ] = true
734 for embedded, isPtr := range typ.embedded {
735
736
737
738
739
740 thisEmbeddedIsPtr := embeddedIsPtr || isPtr
741 for _, m := range embedded.methods {
742
743 if m.Level == 0 {
744 mset.add(customizeRecv(m, recvTypeName, thisEmbeddedIsPtr, level))
745 }
746 }
747 if !visited[embedded] {
748 r.collectEmbeddedMethods(mset, embedded, recvTypeName, thisEmbeddedIsPtr, level+1, visited)
749 }
750 }
751 delete(visited, typ)
752 }
753
754
755 func (r *reader) computeMethodSets() {
756 for _, t := range r.types {
757
758 if t.isStruct {
759
760 r.collectEmbeddedMethods(t.methods, t, t.name, false, 1, make(embeddedSet))
761 } else {
762
763
764 }
765 }
766
767
768
769 for predecl := range r.shadowedPredecl {
770 for _, ityp := range r.fixmap[predecl] {
771 removeAnonymousField(predecl, ityp)
772 }
773 }
774 }
775
776
777
778
779
780 func (r *reader) cleanupTypes() {
781 for _, t := range r.types {
782 visible := r.isVisible(t.name)
783 predeclared := predeclaredTypes[t.name]
784
785 if t.decl == nil && (predeclared || visible && (t.isEmbedded || r.hasDotImp)) {
786
787
788
789
790
791
792 r.values = append(r.values, t.values...)
793
794 for name, f := range t.funcs {
795
796
797 r.funcs[name] = f
798 }
799
800 if !predeclared {
801 for name, m := range t.methods {
802
803 if _, found := r.funcs[name]; !found {
804 r.funcs[name] = m
805 }
806 }
807 }
808 }
809
810 if t.decl == nil || !visible {
811 delete(r.types, t.name)
812 }
813 }
814 }
815
816
817
818
819 type data struct {
820 n int
821 swap func(i, j int)
822 less func(i, j int) bool
823 }
824
825 func (d *data) Len() int { return d.n }
826 func (d *data) Swap(i, j int) { d.swap(i, j) }
827 func (d *data) Less(i, j int) bool { return d.less(i, j) }
828
829
830 func sortBy(less func(i, j int) bool, swap func(i, j int), n int) {
831 sort.Sort(&data{n, swap, less})
832 }
833
834 func sortedKeys(m map[string]int) []string {
835 list := make([]string, len(m))
836 i := 0
837 for key := range m {
838 list[i] = key
839 i++
840 }
841 sort.Strings(list)
842 return list
843 }
844
845
846 func sortingName(d *ast.GenDecl) string {
847 if len(d.Specs) == 1 {
848 if s, ok := d.Specs[0].(*ast.ValueSpec); ok {
849 return s.Names[0].Name
850 }
851 }
852 return ""
853 }
854
855 func sortedValues(m []*Value, tok token.Token) []*Value {
856 list := make([]*Value, len(m))
857 i := 0
858 for _, val := range m {
859 if val.Decl.Tok == tok {
860 list[i] = val
861 i++
862 }
863 }
864 list = list[0:i]
865
866 sortBy(
867 func(i, j int) bool {
868 if ni, nj := sortingName(list[i].Decl), sortingName(list[j].Decl); ni != nj {
869 return ni < nj
870 }
871 return list[i].order < list[j].order
872 },
873 func(i, j int) { list[i], list[j] = list[j], list[i] },
874 len(list),
875 )
876
877 return list
878 }
879
880 func sortedTypes(m map[string]*namedType, allMethods bool) []*Type {
881 list := make([]*Type, len(m))
882 i := 0
883 for _, t := range m {
884 list[i] = &Type{
885 Doc: t.doc,
886 Name: t.name,
887 Decl: t.decl,
888 Consts: sortedValues(t.values, token.CONST),
889 Vars: sortedValues(t.values, token.VAR),
890 Funcs: sortedFuncs(t.funcs, true),
891 Methods: sortedFuncs(t.methods, allMethods),
892 }
893 i++
894 }
895
896 sortBy(
897 func(i, j int) bool { return list[i].Name < list[j].Name },
898 func(i, j int) { list[i], list[j] = list[j], list[i] },
899 len(list),
900 )
901
902 return list
903 }
904
905 func removeStar(s string) string {
906 if len(s) > 0 && s[0] == '*' {
907 return s[1:]
908 }
909 return s
910 }
911
912 func sortedFuncs(m methodSet, allMethods bool) []*Func {
913 list := make([]*Func, len(m))
914 i := 0
915 for _, m := range m {
916
917 switch {
918 case m.Decl == nil:
919
920 case allMethods, m.Level == 0, !token.IsExported(removeStar(m.Orig)):
921
922
923 list[i] = m
924 i++
925 }
926 }
927 list = list[0:i]
928 sortBy(
929 func(i, j int) bool { return list[i].Name < list[j].Name },
930 func(i, j int) { list[i], list[j] = list[j], list[i] },
931 len(list),
932 )
933 return list
934 }
935
936
937
938 func noteBodies(notes []*Note) []string {
939 var list []string
940 for _, n := range notes {
941 list = append(list, n.Body)
942 }
943 return list
944 }
945
946
947
948
949
950 func IsPredeclared(s string) bool {
951 return predeclaredTypes[s] || predeclaredFuncs[s] || predeclaredConstants[s]
952 }
953
954 var predeclaredTypes = map[string]bool{
955 "any": true,
956 "bool": true,
957 "byte": true,
958 "comparable": true,
959 "complex64": true,
960 "complex128": true,
961 "error": true,
962 "float32": true,
963 "float64": true,
964 "int": true,
965 "int8": true,
966 "int16": true,
967 "int32": true,
968 "int64": true,
969 "rune": true,
970 "string": true,
971 "uint": true,
972 "uint8": true,
973 "uint16": true,
974 "uint32": true,
975 "uint64": true,
976 "uintptr": true,
977 }
978
979 var predeclaredFuncs = map[string]bool{
980 "append": true,
981 "cap": true,
982 "close": true,
983 "complex": true,
984 "copy": true,
985 "delete": true,
986 "imag": true,
987 "len": true,
988 "make": true,
989 "new": true,
990 "panic": true,
991 "print": true,
992 "println": true,
993 "real": true,
994 "recover": true,
995 }
996
997 var predeclaredConstants = map[string]bool{
998 "false": true,
999 "iota": true,
1000 "nil": true,
1001 "true": true,
1002 }
1003
1004
1005
1006
1007 func assumedPackageName(importPath string) string {
1008 notIdentifier := func(ch rune) bool {
1009 return !('a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' ||
1010 '0' <= ch && ch <= '9' ||
1011 ch == '_' ||
1012 ch >= utf8.RuneSelf && (unicode.IsLetter(ch) || unicode.IsDigit(ch)))
1013 }
1014
1015 base := path.Base(importPath)
1016 if strings.HasPrefix(base, "v") {
1017 if _, err := strconv.Atoi(base[1:]); err == nil {
1018 dir := path.Dir(importPath)
1019 if dir != "." {
1020 base = path.Base(dir)
1021 }
1022 }
1023 }
1024 base = strings.TrimPrefix(base, "go-")
1025 if i := strings.IndexFunc(base, notIdentifier); i >= 0 {
1026 base = base[:i]
1027 }
1028 return base
1029 }
1030
View as plain text