1
2
3
4
5 package doc
6
7 import (
8 "bufio"
9 "bytes"
10 "fmt"
11 "go/ast"
12 "go/doc"
13 "go/format"
14 "go/parser"
15 "go/printer"
16 "go/token"
17 "io"
18 "io/fs"
19 "log"
20 "path/filepath"
21 "strings"
22 "unicode"
23 "unicode/utf8"
24
25 "cmd/go/internal/cfg"
26 "cmd/go/internal/load"
27 )
28
29 const (
30 punchedCardWidth = 80
31 indent = " "
32 )
33
34 type Package struct {
35 writer io.Writer
36 name string
37 userPath string
38 pkg *ast.Package
39 file *ast.File
40 doc *doc.Package
41 build *load.Package
42 typedValue map[*doc.Value]bool
43 constructor map[*doc.Func]bool
44 fs *token.FileSet
45 buf pkgBuffer
46 }
47
48 func (pkg *Package) ToText(w io.Writer, text, prefix, codePrefix string) {
49 d := pkg.doc.Parser().Parse(text)
50 pr := pkg.doc.Printer()
51 pr.TextPrefix = prefix
52 pr.TextCodePrefix = codePrefix
53 w.Write(pr.Text(d))
54 }
55
56
57
58 type pkgBuffer struct {
59 pkg *Package
60 printed bool
61 bytes.Buffer
62 }
63
64 func (pb *pkgBuffer) Write(p []byte) (int, error) {
65 pb.packageClause()
66 return pb.Buffer.Write(p)
67 }
68
69 func (pb *pkgBuffer) packageClause() {
70 if !pb.printed {
71 pb.printed = true
72
73 if pb.pkg.pkg.Name != "main" || showCmd {
74 pb.pkg.packageClause()
75 }
76 }
77 }
78
79 type PackageError string
80
81 func (p PackageError) Error() string {
82 return string(p)
83 }
84
85
86
87
88
89 func (pkg *Package) prettyPath() string {
90 path := pkg.build.ImportComment
91 if path == "" {
92 path = pkg.build.ImportPath
93 }
94 if path != "." && path != "" {
95 return path
96 }
97
98
99 path = filepath.Clean(filepath.ToSlash(pkg.build.Dir))
100
101 if cfg.GOROOT != "" {
102 goroot := filepath.Join(cfg.GOROOT, "src")
103 if p, ok := trim(path, filepath.ToSlash(goroot)); ok {
104 return p
105 }
106 }
107 for _, gopath := range splitGopath() {
108 if p, ok := trim(path, filepath.ToSlash(gopath)); ok {
109 return p
110 }
111 }
112 return path
113 }
114
115
116
117
118
119 func trim(path, prefix string) (string, bool) {
120 if !strings.HasPrefix(path, prefix) {
121 return path, false
122 }
123 if path == prefix {
124 return path, true
125 }
126 if path[len(prefix)] == '/' {
127 return path[len(prefix)+1:], true
128 }
129 return path, false
130 }
131
132
133
134
135
136 func (pkg *Package) Fatalf(format string, args ...any) {
137 panic(PackageError(fmt.Sprintf(format, args...)))
138 }
139
140
141
142 func parsePackage(writer io.Writer, pkg *load.Package, userPath string) *Package {
143
144
145
146
147 include := func(info fs.FileInfo) bool {
148 files := [][]string{pkg.GoFiles, pkg.CgoFiles, pkg.TestGoFiles, pkg.XTestGoFiles}
149 for _, f := range files {
150 for _, name := range f {
151 if name == info.Name() {
152 return true
153 }
154 }
155 }
156 return false
157 }
158 fset := token.NewFileSet()
159 pkgs, err := parser.ParseDir(fset, pkg.Dir, include, parser.ParseComments)
160 if err != nil {
161 log.Fatal(err)
162 }
163 if len(pkgs) == 0 {
164 log.Fatalf("no source-code package in directory %s", pkg.Dir)
165 }
166 astPkg := pkgs[pkg.Name]
167
168
169
170
171
172
173
174
175
176 mode := doc.AllDecls
177 if showSrc {
178 mode |= doc.PreserveAST
179 }
180 var allGoFiles []*ast.File
181 for _, p := range pkgs {
182 for _, f := range p.Files {
183 allGoFiles = append(allGoFiles, f)
184 }
185 }
186 docPkg, err := doc.NewFromFiles(fset, allGoFiles, pkg.ImportPath, mode)
187 if err != nil {
188 log.Fatal(err)
189 }
190 typedValue := make(map[*doc.Value]bool)
191 constructor := make(map[*doc.Func]bool)
192 for _, typ := range docPkg.Types {
193 docPkg.Consts = append(docPkg.Consts, typ.Consts...)
194 docPkg.Vars = append(docPkg.Vars, typ.Vars...)
195 docPkg.Funcs = append(docPkg.Funcs, typ.Funcs...)
196 if isExported(typ.Name) {
197 for _, value := range typ.Consts {
198 typedValue[value] = true
199 }
200 for _, value := range typ.Vars {
201 typedValue[value] = true
202 }
203 for _, fun := range typ.Funcs {
204
205
206 constructor[fun] = true
207 }
208 }
209 }
210
211 p := &Package{
212 writer: writer,
213 name: pkg.Name,
214 userPath: userPath,
215 pkg: astPkg,
216 file: ast.MergePackageFiles(astPkg, 0),
217 doc: docPkg,
218 typedValue: typedValue,
219 constructor: constructor,
220 build: pkg,
221 fs: fset,
222 }
223 p.buf.pkg = p
224 return p
225 }
226
227 func (pkg *Package) Printf(format string, args ...any) {
228 fmt.Fprintf(&pkg.buf, format, args...)
229 }
230
231 func (pkg *Package) flush() {
232 _, err := pkg.writer.Write(pkg.buf.Bytes())
233 if err != nil {
234 log.Fatal(err)
235 }
236 pkg.buf.Reset()
237 }
238
239 var newlineBytes = []byte("\n\n")
240
241
242 func (pkg *Package) newlines(n int) {
243 for !bytes.HasSuffix(pkg.buf.Bytes(), newlineBytes[:n]) {
244 pkg.buf.WriteRune('\n')
245 }
246 }
247
248
249
250
251 func (pkg *Package) emit(comment string, node ast.Node) {
252 if node != nil {
253 var arg any = node
254 if showSrc {
255
256 arg = &printer.CommentedNode{
257 Node: node,
258 Comments: pkg.file.Comments,
259 }
260 }
261 err := format.Node(&pkg.buf, pkg.fs, arg)
262 if err != nil {
263 log.Fatal(err)
264 }
265 if comment != "" && !showSrc {
266 pkg.newlines(1)
267 pkg.ToText(&pkg.buf, comment, indent, indent+indent)
268 pkg.newlines(2)
269 } else {
270 pkg.newlines(1)
271 }
272 }
273 }
274
275
276 func (pkg *Package) oneLineNode(node ast.Node) string {
277 const maxDepth = 10
278 return pkg.oneLineNodeDepth(node, maxDepth)
279 }
280
281
282
283 func (pkg *Package) oneLineNodeDepth(node ast.Node, depth int) string {
284 const dotDotDot = "..."
285 if depth == 0 {
286 return dotDotDot
287 }
288 depth--
289
290 switch n := node.(type) {
291 case nil:
292 return ""
293
294 case *ast.GenDecl:
295
296 trailer := ""
297 if len(n.Specs) > 1 {
298 trailer = " " + dotDotDot
299 }
300
301
302 typ := ""
303 for i, spec := range n.Specs {
304 valueSpec := spec.(*ast.ValueSpec)
305
306
307
308 if valueSpec.Type != nil {
309 typ = fmt.Sprintf(" %s", pkg.oneLineNodeDepth(valueSpec.Type, depth))
310 } else if len(valueSpec.Values) > 0 {
311 typ = ""
312 }
313
314 if !isExported(valueSpec.Names[0].Name) {
315 continue
316 }
317 val := ""
318 if i < len(valueSpec.Values) && valueSpec.Values[i] != nil {
319 val = fmt.Sprintf(" = %s", pkg.oneLineNodeDepth(valueSpec.Values[i], depth))
320 }
321 return fmt.Sprintf("%s %s%s%s%s", n.Tok, valueSpec.Names[0], typ, val, trailer)
322 }
323 return ""
324
325 case *ast.FuncDecl:
326
327 name := n.Name.Name
328 recv := pkg.oneLineNodeDepth(n.Recv, depth)
329 if len(recv) > 0 {
330 recv = "(" + recv + ") "
331 }
332 fnc := pkg.oneLineNodeDepth(n.Type, depth)
333 fnc = strings.TrimPrefix(fnc, "func")
334 return fmt.Sprintf("func %s%s%s", recv, name, fnc)
335
336 case *ast.TypeSpec:
337 sep := " "
338 if n.Assign.IsValid() {
339 sep = " = "
340 }
341 tparams := pkg.formatTypeParams(n.TypeParams, depth)
342 return fmt.Sprintf("type %s%s%s%s", n.Name.Name, tparams, sep, pkg.oneLineNodeDepth(n.Type, depth))
343
344 case *ast.FuncType:
345 var params []string
346 if n.Params != nil {
347 for _, field := range n.Params.List {
348 params = append(params, pkg.oneLineField(field, depth))
349 }
350 }
351 needParens := false
352 var results []string
353 if n.Results != nil {
354 needParens = needParens || len(n.Results.List) > 1
355 for _, field := range n.Results.List {
356 needParens = needParens || len(field.Names) > 0
357 results = append(results, pkg.oneLineField(field, depth))
358 }
359 }
360
361 tparam := pkg.formatTypeParams(n.TypeParams, depth)
362 param := joinStrings(params)
363 if len(results) == 0 {
364 return fmt.Sprintf("func%s(%s)", tparam, param)
365 }
366 result := joinStrings(results)
367 if !needParens {
368 return fmt.Sprintf("func%s(%s) %s", tparam, param, result)
369 }
370 return fmt.Sprintf("func%s(%s) (%s)", tparam, param, result)
371
372 case *ast.StructType:
373 if n.Fields == nil || len(n.Fields.List) == 0 {
374 return "struct{}"
375 }
376 return "struct{ ... }"
377
378 case *ast.InterfaceType:
379 if n.Methods == nil || len(n.Methods.List) == 0 {
380 return "interface{}"
381 }
382 return "interface{ ... }"
383
384 case *ast.FieldList:
385 if n == nil || len(n.List) == 0 {
386 return ""
387 }
388 if len(n.List) == 1 {
389 return pkg.oneLineField(n.List[0], depth)
390 }
391 return dotDotDot
392
393 case *ast.FuncLit:
394 return pkg.oneLineNodeDepth(n.Type, depth) + " { ... }"
395
396 case *ast.CompositeLit:
397 typ := pkg.oneLineNodeDepth(n.Type, depth)
398 if len(n.Elts) == 0 {
399 return fmt.Sprintf("%s{}", typ)
400 }
401 return fmt.Sprintf("%s{ %s }", typ, dotDotDot)
402
403 case *ast.ArrayType:
404 length := pkg.oneLineNodeDepth(n.Len, depth)
405 element := pkg.oneLineNodeDepth(n.Elt, depth)
406 return fmt.Sprintf("[%s]%s", length, element)
407
408 case *ast.MapType:
409 key := pkg.oneLineNodeDepth(n.Key, depth)
410 value := pkg.oneLineNodeDepth(n.Value, depth)
411 return fmt.Sprintf("map[%s]%s", key, value)
412
413 case *ast.CallExpr:
414 fnc := pkg.oneLineNodeDepth(n.Fun, depth)
415 var args []string
416 for _, arg := range n.Args {
417 args = append(args, pkg.oneLineNodeDepth(arg, depth))
418 }
419 return fmt.Sprintf("%s(%s)", fnc, joinStrings(args))
420
421 case *ast.UnaryExpr:
422 return fmt.Sprintf("%s%s", n.Op, pkg.oneLineNodeDepth(n.X, depth))
423
424 case *ast.Ident:
425 return n.Name
426
427 default:
428
429 buf := new(strings.Builder)
430 format.Node(buf, pkg.fs, node)
431 s := buf.String()
432 if strings.Contains(s, "\n") {
433 return dotDotDot
434 }
435 return s
436 }
437 }
438
439 func (pkg *Package) formatTypeParams(list *ast.FieldList, depth int) string {
440 if list.NumFields() == 0 {
441 return ""
442 }
443 var tparams []string
444 for _, field := range list.List {
445 tparams = append(tparams, pkg.oneLineField(field, depth))
446 }
447 return "[" + joinStrings(tparams) + "]"
448 }
449
450
451 func (pkg *Package) oneLineField(field *ast.Field, depth int) string {
452 var names []string
453 for _, name := range field.Names {
454 names = append(names, name.Name)
455 }
456 if len(names) == 0 {
457 return pkg.oneLineNodeDepth(field.Type, depth)
458 }
459 return joinStrings(names) + " " + pkg.oneLineNodeDepth(field.Type, depth)
460 }
461
462
463
464 func joinStrings(ss []string) string {
465 var n int
466 for i, s := range ss {
467 n += len(s) + len(", ")
468 if n > punchedCardWidth {
469 ss = append(ss[:i:i], "...")
470 break
471 }
472 }
473 return strings.Join(ss, ", ")
474 }
475
476
477 func (pkg *Package) printHeader(s string) {
478 pkg.Printf("\n%s\n\n", s)
479 }
480
481
482
483 func (pkg *Package) constsDoc(printed map[*ast.GenDecl]bool) {
484 var header bool
485 for _, value := range pkg.doc.Consts {
486
487
488 for _, name := range value.Names {
489 if isExported(name) && !pkg.typedValue[value] {
490 if !header {
491 pkg.printHeader("CONSTANTS")
492 header = true
493 }
494 pkg.valueDoc(value, printed)
495 break
496 }
497 }
498 }
499 }
500
501
502
503 func (pkg *Package) varsDoc(printed map[*ast.GenDecl]bool) {
504 var header bool
505 for _, value := range pkg.doc.Vars {
506
507
508 for _, name := range value.Names {
509 if isExported(name) && !pkg.typedValue[value] {
510 if !header {
511 pkg.printHeader("VARIABLES")
512 header = true
513 }
514 pkg.valueDoc(value, printed)
515 break
516 }
517 }
518 }
519 }
520
521
522 func (pkg *Package) funcsDoc() {
523 var header bool
524 for _, fun := range pkg.doc.Funcs {
525 if isExported(fun.Name) && !pkg.constructor[fun] {
526 if !header {
527 pkg.printHeader("FUNCTIONS")
528 header = true
529 }
530 pkg.emit(fun.Doc, fun.Decl)
531 }
532 }
533 }
534
535
536 func (pkg *Package) typesDoc() {
537 var header bool
538 for _, typ := range pkg.doc.Types {
539 if isExported(typ.Name) {
540 if !header {
541 pkg.printHeader("TYPES")
542 header = true
543 }
544 pkg.typeDoc(typ)
545 }
546 }
547 }
548
549
550 func (pkg *Package) packageDoc() {
551 pkg.Printf("")
552 if showAll || !short {
553 pkg.ToText(&pkg.buf, pkg.doc.Doc, "", indent)
554 pkg.newlines(1)
555 }
556
557 switch {
558 case showAll:
559 printed := make(map[*ast.GenDecl]bool)
560 pkg.constsDoc(printed)
561 pkg.varsDoc(printed)
562 pkg.funcsDoc()
563 pkg.typesDoc()
564
565 case pkg.pkg.Name == "main" && !showCmd:
566
567 return
568
569 default:
570 if !short {
571 pkg.newlines(2)
572 }
573 pkg.valueSummary(pkg.doc.Consts, false)
574 pkg.valueSummary(pkg.doc.Vars, false)
575 pkg.funcSummary(pkg.doc.Funcs, false)
576 pkg.typeSummary()
577 pkg.exampleSummary(pkg.doc.Examples, false)
578 }
579
580 if !short {
581 pkg.bugs()
582 }
583 }
584
585
586 func (pkg *Package) packageClause() {
587 if short {
588 return
589 }
590 importPath := pkg.build.ImportComment
591 if importPath == "" {
592 importPath = pkg.build.ImportPath
593 }
594
595
596
597
598
599 if usingModules {
600 for _, root := range codeRoots() {
601 if pkg.build.Dir == root.dir {
602 importPath = root.importPath
603 break
604 }
605 if strings.HasPrefix(pkg.build.Dir, root.dir+string(filepath.Separator)) {
606 suffix := filepath.ToSlash(pkg.build.Dir[len(root.dir)+1:])
607 if root.importPath == "" {
608 importPath = suffix
609 } else {
610 importPath = root.importPath + "/" + suffix
611 }
612 break
613 }
614 }
615 }
616
617 pkg.Printf("package %s // import %q\n\n", pkg.name, importPath)
618 if !usingModules && importPath != pkg.build.ImportPath {
619 pkg.Printf("WARNING: package source is installed in %q\n", pkg.build.ImportPath)
620 }
621 }
622
623
624
625
626 func (pkg *Package) valueSummary(values []*doc.Value, showGrouped bool) {
627 var isGrouped map[*doc.Value]bool
628 if !showGrouped {
629 isGrouped = make(map[*doc.Value]bool)
630 for _, typ := range pkg.doc.Types {
631 if !isExported(typ.Name) {
632 continue
633 }
634 for _, c := range typ.Consts {
635 isGrouped[c] = true
636 }
637 for _, v := range typ.Vars {
638 isGrouped[v] = true
639 }
640 }
641 }
642
643 for _, value := range values {
644 if !isGrouped[value] {
645 if decl := pkg.oneLineNode(value.Decl); decl != "" {
646 pkg.Printf("%s\n", decl)
647 }
648 }
649 }
650 }
651
652
653
654 func (pkg *Package) funcSummary(funcs []*doc.Func, showConstructors bool) {
655 for _, fun := range funcs {
656
657 if isExported(fun.Name) {
658 if showConstructors || !pkg.constructor[fun] {
659 pkg.Printf("%s\n", pkg.oneLineNode(fun.Decl))
660 if showEx {
661 pkg.exampleSummary(fun.Examples, false)
662 }
663 }
664 }
665 }
666 }
667
668
669 func (pkg *Package) exampleSummary(exs []*doc.Example, showDoc bool) {
670 if !showEx {
671 return
672 }
673 for _, ex := range exs {
674 pkg.Printf(indent+"func Example%s()\n", ex.Name)
675 if showDoc && ex.Doc != "" {
676 pkg.ToText(&pkg.buf, ex.Doc, indent+indent, indent+indent)
677 }
678 }
679 }
680
681
682 func (pkg *Package) typeSummary() {
683 for _, typ := range pkg.doc.Types {
684 for _, spec := range typ.Decl.Specs {
685 typeSpec := spec.(*ast.TypeSpec)
686 if isExported(typeSpec.Name.Name) {
687 pkg.Printf("%s\n", pkg.oneLineNode(typeSpec))
688
689 for _, c := range typ.Consts {
690 if decl := pkg.oneLineNode(c.Decl); decl != "" {
691 pkg.Printf(indent+"%s\n", decl)
692 }
693 }
694 for _, v := range typ.Vars {
695 if decl := pkg.oneLineNode(v.Decl); decl != "" {
696 pkg.Printf(indent+"%s\n", decl)
697 }
698 }
699 for _, constructor := range typ.Funcs {
700 if isExported(constructor.Name) {
701 pkg.Printf(indent+"%s\n", pkg.oneLineNode(constructor.Decl))
702 pkg.exampleSummary(constructor.Examples, false)
703 }
704 }
705 pkg.exampleSummary(typ.Examples, false)
706 }
707 }
708 }
709 }
710
711
712
713 func (pkg *Package) bugs() {
714 if pkg.doc.Notes["BUG"] == nil {
715 return
716 }
717 pkg.Printf("\n")
718 for _, note := range pkg.doc.Notes["BUG"] {
719 pkg.Printf("%s: %v\n", "BUG", note.Body)
720 }
721 }
722
723
724 func (pkg *Package) findValues(symbol string, docValues []*doc.Value) (values []*doc.Value) {
725 for _, value := range docValues {
726 for _, name := range value.Names {
727 if match(symbol, name) {
728 values = append(values, value)
729 }
730 }
731 }
732 return
733 }
734
735
736 func (pkg *Package) findFuncs(symbol string) (funcs []*doc.Func) {
737 for _, fun := range pkg.doc.Funcs {
738 if match(symbol, fun.Name) {
739 funcs = append(funcs, fun)
740 }
741 }
742 return
743 }
744
745
746
747 func (pkg *Package) findTypes(symbol string) (types []*doc.Type) {
748 for _, typ := range pkg.doc.Types {
749 if symbol == "" && isExported(typ.Name) || match(symbol, typ.Name) {
750 types = append(types, typ)
751 }
752 }
753 return
754 }
755
756
757 func (pkg *Package) findExamples(symbol string) (examples []*doc.Example) {
758 if !strings.HasPrefix(symbol, "Example") {
759 return
760 }
761 symbol = strings.TrimPrefix(symbol, "Example")
762 var all []*doc.Example
763 all = append(all, pkg.doc.Examples...)
764 for _, typ := range pkg.doc.Types {
765 all = append(all, typ.Examples...)
766 for _, fun := range typ.Funcs {
767 all = append(all, fun.Examples...)
768 }
769 for _, fun := range typ.Methods {
770 all = append(all, fun.Examples...)
771 }
772 }
773 for _, fun := range pkg.doc.Funcs {
774 all = append(all, fun.Examples...)
775 }
776
777
778
779 u := unexported
780 unexported = true
781 for _, ex := range all {
782 if match(symbol, ex.Name) {
783 examples = append(examples, ex)
784 }
785 }
786 unexported = u
787 return
788 }
789
790
791
792 func (pkg *Package) findTypeSpec(decl *ast.GenDecl, symbol string) *ast.TypeSpec {
793 for _, spec := range decl.Specs {
794 typeSpec := spec.(*ast.TypeSpec)
795 if symbol == typeSpec.Name.Name {
796 return typeSpec
797 }
798 }
799 return nil
800 }
801
802
803
804
805 func (pkg *Package) symbolDoc(symbol string) bool {
806 found := false
807
808 for _, fun := range pkg.findFuncs(symbol) {
809
810 decl := fun.Decl
811 pkg.emit(fun.Doc, decl)
812 pkg.exampleSummary(fun.Examples, true)
813 found = true
814 }
815 for _, ex := range pkg.findExamples(symbol) {
816 pkg.emitExample(ex)
817 found = true
818 }
819
820 values := pkg.findValues(symbol, pkg.doc.Consts)
821 values = append(values, pkg.findValues(symbol, pkg.doc.Vars)...)
822 printed := make(map[*ast.GenDecl]bool)
823 for _, value := range values {
824 pkg.valueDoc(value, printed)
825 found = true
826 }
827
828 for _, typ := range pkg.findTypes(symbol) {
829 pkg.typeDoc(typ)
830 found = true
831 }
832 if !found {
833
834 if !pkg.printMethodDoc("", symbol) {
835 return false
836 }
837 }
838 return true
839 }
840
841
842
843
844
845
846
847
848 func (pkg *Package) valueDoc(value *doc.Value, printed map[*ast.GenDecl]bool) {
849 if printed[value.Decl] {
850 return
851 }
852
853
854
855
856
857 specs := make([]ast.Spec, 0, len(value.Decl.Specs))
858 var typ ast.Expr
859 for _, spec := range value.Decl.Specs {
860 vspec := spec.(*ast.ValueSpec)
861
862
863
864 if vspec.Type != nil {
865 typ = vspec.Type
866 }
867
868 for _, ident := range vspec.Names {
869 if showSrc || isExported(ident.Name) {
870 if vspec.Type == nil && vspec.Values == nil && typ != nil {
871
872
873 vspec.Type = &ast.Ident{
874 Name: pkg.oneLineNode(typ),
875 NamePos: vspec.End() - 1,
876 }
877 }
878
879 specs = append(specs, vspec)
880 typ = nil
881 break
882 }
883 }
884 }
885 if len(specs) == 0 {
886 return
887 }
888 value.Decl.Specs = specs
889 pkg.emit(value.Doc, value.Decl)
890 printed[value.Decl] = true
891 }
892
893
894
895 func (pkg *Package) typeDoc(typ *doc.Type) {
896 decl := typ.Decl
897 spec := pkg.findTypeSpec(decl, typ.Name)
898 trimUnexportedElems(spec)
899
900 if len(decl.Specs) > 1 {
901 decl.Specs = []ast.Spec{spec}
902 }
903 pkg.emit(typ.Doc, decl)
904 pkg.newlines(2)
905
906 if showAll {
907 printed := make(map[*ast.GenDecl]bool)
908
909 values := typ.Consts
910 values = append(values, typ.Vars...)
911 for _, value := range values {
912 for _, name := range value.Names {
913 if isExported(name) {
914 pkg.valueDoc(value, printed)
915 break
916 }
917 }
918 }
919 funcs := typ.Funcs
920 funcs = append(funcs, typ.Methods...)
921 for _, fun := range funcs {
922 if isExported(fun.Name) {
923 pkg.emit(fun.Doc, fun.Decl)
924 if fun.Doc == "" {
925 pkg.newlines(2)
926 }
927 }
928 }
929 } else {
930 pkg.valueSummary(typ.Consts, true)
931 pkg.valueSummary(typ.Vars, true)
932 pkg.funcSummary(typ.Funcs, true)
933 pkg.exampleSummary(typ.Examples, false)
934 pkg.funcSummary(typ.Methods, true)
935 }
936 }
937
938
939
940
941 func trimUnexportedElems(spec *ast.TypeSpec) {
942 if showSrc {
943 return
944 }
945 switch typ := spec.Type.(type) {
946 case *ast.StructType:
947 typ.Fields = trimUnexportedFields(typ.Fields, false)
948 case *ast.InterfaceType:
949 typ.Methods = trimUnexportedFields(typ.Methods, true)
950 }
951 }
952
953
954 func trimUnexportedFields(fields *ast.FieldList, isInterface bool) *ast.FieldList {
955 what := "methods"
956 if !isInterface {
957 what = "fields"
958 }
959
960 trimmed := false
961 list := make([]*ast.Field, 0, len(fields.List))
962 for _, field := range fields.List {
963
964
965
966
967
968
969
970
971
972
973 if field.Doc != nil {
974 doc := field.Doc
975 text := doc.Text()
976
977 trailingBlankLine := len(doc.List[len(doc.List)-1].Text) == 2
978 if !trailingBlankLine {
979
980 lt := len(text)
981 if lt > 0 && text[lt-1] == '\n' {
982 text = text[:lt-1]
983 }
984 }
985
986 start := doc.List[0].Slash
987 doc.List = doc.List[:0]
988 for line := range strings.SplitSeq(text, "\n") {
989 prefix := "// "
990 if len(line) > 0 && line[0] == '\t' {
991 prefix = "//"
992 }
993 doc.List = append(doc.List, &ast.Comment{
994 Text: prefix + line,
995 })
996 }
997 doc.List[0].Slash = start
998 }
999
1000 names := field.Names
1001 if len(names) == 0 {
1002
1003
1004
1005
1006 ty := field.Type
1007 if se, ok := field.Type.(*ast.StarExpr); !isInterface && ok {
1008
1009
1010 ty = se.X
1011 }
1012 constraint := false
1013 switch ident := ty.(type) {
1014 case *ast.Ident:
1015 if isInterface && ident.Obj == nil &&
1016 (ident.Name == "error" || ident.Name == "comparable") {
1017
1018
1019
1020 list = append(list, field)
1021 continue
1022 }
1023 names = []*ast.Ident{ident}
1024 case *ast.SelectorExpr:
1025
1026 names = []*ast.Ident{ident.Sel}
1027 default:
1028
1029
1030 constraint = true
1031 }
1032 if names == nil && !constraint {
1033
1034 log.Print("invalid program: unexpected type for embedded field")
1035 }
1036 }
1037
1038 ok := true
1039 if !unexported {
1040 for _, name := range names {
1041 if !isExported(name.Name) {
1042 trimmed = true
1043 ok = false
1044 break
1045 }
1046 }
1047 }
1048 if ok {
1049 list = append(list, field)
1050 }
1051 }
1052 if !trimmed {
1053 return fields
1054 }
1055 unexportedField := &ast.Field{
1056 Type: &ast.Ident{
1057
1058
1059
1060
1061 Name: "",
1062 NamePos: fields.Closing - 1,
1063 },
1064 Comment: &ast.CommentGroup{
1065 List: []*ast.Comment{{Text: fmt.Sprintf("// Has unexported %s.\n", what)}},
1066 },
1067 }
1068 return &ast.FieldList{
1069 Opening: fields.Opening,
1070 List: append(list, unexportedField),
1071 Closing: fields.Closing,
1072 }
1073 }
1074
1075
1076
1077
1078 func (pkg *Package) printMethodDoc(symbol, method string) bool {
1079 types := pkg.findTypes(symbol)
1080 if types == nil {
1081 if symbol == "" {
1082 return false
1083 }
1084 pkg.Fatalf("symbol %s is not a type in package %s installed in %q", symbol, pkg.name, pkg.build.ImportPath)
1085 }
1086 found := false
1087 for _, typ := range types {
1088 if len(typ.Methods) > 0 {
1089 for _, meth := range typ.Methods {
1090 if match(method, meth.Name) {
1091 decl := meth.Decl
1092 pkg.emit(meth.Doc, decl)
1093 pkg.exampleSummary(meth.Examples, true)
1094 found = true
1095 }
1096 }
1097 continue
1098 }
1099 if symbol == "" {
1100 continue
1101 }
1102
1103
1104 spec := pkg.findTypeSpec(typ.Decl, typ.Name)
1105 inter, ok := spec.Type.(*ast.InterfaceType)
1106 if !ok {
1107
1108 continue
1109 }
1110
1111
1112 var methods []*ast.Field
1113 for _, iMethod := range inter.Methods.List {
1114
1115
1116 if len(iMethod.Names) == 0 {
1117 continue
1118 }
1119 name := iMethod.Names[0].Name
1120 if match(method, name) {
1121 methods = append(methods, iMethod)
1122 found = true
1123 }
1124 }
1125 if found {
1126 pkg.Printf("type %s ", spec.Name)
1127 inter.Methods.List, methods = methods, inter.Methods.List
1128 err := format.Node(&pkg.buf, pkg.fs, inter)
1129 if err != nil {
1130 log.Fatal(err)
1131 }
1132 pkg.newlines(1)
1133
1134 inter.Methods.List = methods
1135 }
1136 }
1137 return found
1138 }
1139
1140
1141
1142
1143 func (pkg *Package) printFieldDoc(symbol, fieldName string) bool {
1144 if symbol == "" || fieldName == "" {
1145 return false
1146 }
1147 types := pkg.findTypes(symbol)
1148 if types == nil {
1149 pkg.Fatalf("symbol %s is not a type in package %s installed in %q", symbol, pkg.name, pkg.build.ImportPath)
1150 }
1151 found := false
1152 numUnmatched := 0
1153 for _, typ := range types {
1154
1155 spec := pkg.findTypeSpec(typ.Decl, typ.Name)
1156 structType, ok := spec.Type.(*ast.StructType)
1157 if !ok {
1158
1159 continue
1160 }
1161 for _, field := range structType.Fields.List {
1162
1163 for _, name := range field.Names {
1164 if !match(fieldName, name.Name) {
1165 numUnmatched++
1166 continue
1167 }
1168 if !found {
1169 pkg.Printf("type %s struct {\n", typ.Name)
1170 }
1171 if field.Doc != nil {
1172
1173
1174 docBuf := new(bytes.Buffer)
1175 pkg.ToText(docBuf, field.Doc.Text(), "", indent)
1176 scanner := bufio.NewScanner(docBuf)
1177 for scanner.Scan() {
1178 fmt.Fprintf(&pkg.buf, "%s// %s\n", indent, scanner.Bytes())
1179 }
1180 }
1181 s := pkg.oneLineNode(field.Type)
1182 lineComment := ""
1183 if field.Comment != nil {
1184 lineComment = fmt.Sprintf(" %s", field.Comment.List[0].Text)
1185 }
1186 pkg.Printf("%s%s %s%s\n", indent, name, s, lineComment)
1187 found = true
1188 }
1189 }
1190 }
1191 if found {
1192 if numUnmatched > 0 {
1193 pkg.Printf("\n // ... other fields elided ...\n")
1194 }
1195 pkg.Printf("}\n")
1196 }
1197 return found
1198 }
1199
1200
1201
1202
1203 func match(user, program string) bool {
1204 if !isExported(program) {
1205 return false
1206 }
1207 if matchCase {
1208 return user == program
1209 }
1210 for _, u := range user {
1211 p, w := utf8.DecodeRuneInString(program)
1212 program = program[w:]
1213 if u == p {
1214 continue
1215 }
1216 if unicode.IsLower(u) && simpleFold(u) == simpleFold(p) {
1217 continue
1218 }
1219 return false
1220 }
1221 return program == ""
1222 }
1223
1224
1225
1226 func simpleFold(r rune) rune {
1227 for {
1228 r1 := unicode.SimpleFold(r)
1229 if r1 <= r {
1230 return r1
1231 }
1232 r = r1
1233 }
1234 }
1235
1236
1237 func (pkg *Package) emitExample(ex *doc.Example) {
1238 pkg.buf.printed = true
1239 var err error
1240 if ex.Play != nil {
1241 err = format.Node(&pkg.buf, pkg.fs, ex.Play)
1242 } else {
1243
1244
1245 b, ok := ex.Code.(*ast.BlockStmt)
1246 if !ok {
1247 err = format.Node(&pkg.buf, pkg.fs, ex.Code)
1248 } else {
1249 err = format.Node(&pkg.buf, pkg.fs, b.List)
1250 }
1251 }
1252 if err != nil {
1253 log.Fatal(err)
1254 }
1255 if ex.Output != "" {
1256 pkg.newlines(2)
1257 pkg.Printf("Output: ")
1258 if strings.Count(ex.Output, "\n") > 1 {
1259 pkg.newlines(1)
1260 }
1261 pkg.Printf("%s", ex.Output)
1262 }
1263 pkg.newlines(1)
1264 }
1265
View as plain text