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 found = true
812 if short {
813 pkg.Printf("%s\n", pkg.oneLineNode(decl))
814 pkg.exampleSummary(fun.Examples, false)
815 continue
816 }
817 pkg.emit(fun.Doc, decl)
818 pkg.exampleSummary(fun.Examples, true)
819 }
820 for _, ex := range pkg.findExamples(symbol) {
821 pkg.emitExample(ex)
822 found = true
823 }
824
825 values := pkg.findValues(symbol, pkg.doc.Consts)
826 values = append(values, pkg.findValues(symbol, pkg.doc.Vars)...)
827 printed := make(map[*ast.GenDecl]bool)
828 for _, value := range values {
829 pkg.valueDoc(value, printed)
830 found = true
831 }
832
833 for _, typ := range pkg.findTypes(symbol) {
834 pkg.typeDoc(typ)
835 found = true
836 }
837 if !found {
838
839 if !pkg.printMethodDoc("", symbol) {
840 return false
841 }
842 }
843 return true
844 }
845
846
847
848
849
850
851
852
853 func (pkg *Package) valueDoc(value *doc.Value, printed map[*ast.GenDecl]bool) {
854 if printed[value.Decl] {
855 return
856 }
857
858
859
860
861
862 specs := make([]ast.Spec, 0, len(value.Decl.Specs))
863 var typ ast.Expr
864 for _, spec := range value.Decl.Specs {
865 vspec := spec.(*ast.ValueSpec)
866
867
868
869 if vspec.Type != nil {
870 typ = vspec.Type
871 }
872
873 for _, ident := range vspec.Names {
874 if showSrc || isExported(ident.Name) {
875 if vspec.Type == nil && vspec.Values == nil && typ != nil {
876
877
878 vspec.Type = &ast.Ident{
879 Name: pkg.oneLineNode(typ),
880 NamePos: vspec.End() - 1,
881 }
882 }
883
884 specs = append(specs, vspec)
885 typ = nil
886 break
887 }
888 }
889 }
890 if len(specs) == 0 {
891 return
892 }
893 value.Decl.Specs = specs
894 printed[value.Decl] = true
895 if short {
896 pkg.Printf("%s\n", pkg.oneLineNode(value.Decl))
897 return
898 }
899 pkg.emit(value.Doc, value.Decl)
900 }
901
902
903
904 func (pkg *Package) typeDoc(typ *doc.Type) {
905 decl := typ.Decl
906 spec := pkg.findTypeSpec(decl, typ.Name)
907 trimUnexportedElems(spec)
908
909 if len(decl.Specs) > 1 {
910 decl.Specs = []ast.Spec{spec}
911 }
912 if short {
913 pkg.Printf("%s\n", pkg.oneLineNode(spec))
914 return
915 }
916 pkg.emit(typ.Doc, decl)
917 pkg.newlines(2)
918
919 if showAll {
920 printed := make(map[*ast.GenDecl]bool)
921
922 values := typ.Consts
923 values = append(values, typ.Vars...)
924 for _, value := range values {
925 for _, name := range value.Names {
926 if isExported(name) {
927 pkg.valueDoc(value, printed)
928 break
929 }
930 }
931 }
932 funcs := typ.Funcs
933 funcs = append(funcs, typ.Methods...)
934 for _, fun := range funcs {
935 if isExported(fun.Name) {
936 pkg.emit(fun.Doc, fun.Decl)
937 if fun.Doc == "" {
938 pkg.newlines(2)
939 }
940 }
941 }
942 } else {
943 pkg.valueSummary(typ.Consts, true)
944 pkg.valueSummary(typ.Vars, true)
945 pkg.funcSummary(typ.Funcs, true)
946 pkg.exampleSummary(typ.Examples, false)
947 pkg.funcSummary(typ.Methods, true)
948 }
949 }
950
951
952
953
954 func trimUnexportedElems(spec *ast.TypeSpec) {
955 if showSrc {
956 return
957 }
958 switch typ := spec.Type.(type) {
959 case *ast.StructType:
960 typ.Fields = trimUnexportedFields(typ.Fields, false)
961 case *ast.InterfaceType:
962 typ.Methods = trimUnexportedFields(typ.Methods, true)
963 }
964 }
965
966
967 func trimUnexportedFields(fields *ast.FieldList, isInterface bool) *ast.FieldList {
968 what := "methods"
969 if !isInterface {
970 what = "fields"
971 }
972
973 trimmed := false
974 list := make([]*ast.Field, 0, len(fields.List))
975 for _, field := range fields.List {
976
977
978
979
980
981
982
983
984
985
986 if field.Doc != nil {
987 doc := field.Doc
988 text := doc.Text()
989
990 trailingBlankLine := len(doc.List[len(doc.List)-1].Text) == 2
991 if !trailingBlankLine {
992
993 lt := len(text)
994 if lt > 0 && text[lt-1] == '\n' {
995 text = text[:lt-1]
996 }
997 }
998
999 start := doc.List[0].Slash
1000 doc.List = doc.List[:0]
1001 for line := range strings.SplitSeq(text, "\n") {
1002 prefix := "// "
1003 if len(line) > 0 && line[0] == '\t' {
1004 prefix = "//"
1005 }
1006 doc.List = append(doc.List, &ast.Comment{
1007 Text: prefix + line,
1008 })
1009 }
1010 doc.List[0].Slash = start
1011 }
1012
1013 names := field.Names
1014 if len(names) == 0 {
1015
1016
1017
1018
1019 ty := field.Type
1020 if se, ok := field.Type.(*ast.StarExpr); !isInterface && ok {
1021
1022
1023 ty = se.X
1024 }
1025 constraint := false
1026 switch ident := ty.(type) {
1027 case *ast.Ident:
1028 if isInterface && ident.Obj == nil &&
1029 (ident.Name == "error" || ident.Name == "comparable") {
1030
1031
1032
1033 list = append(list, field)
1034 continue
1035 }
1036 names = []*ast.Ident{ident}
1037 case *ast.SelectorExpr:
1038
1039 names = []*ast.Ident{ident.Sel}
1040 default:
1041
1042
1043 constraint = true
1044 }
1045 if names == nil && !constraint {
1046
1047 log.Print("invalid program: unexpected type for embedded field")
1048 }
1049 }
1050
1051 ok := true
1052 if !unexported {
1053 for _, name := range names {
1054 if !isExported(name.Name) {
1055 trimmed = true
1056 ok = false
1057 break
1058 }
1059 }
1060 }
1061 if ok {
1062 list = append(list, field)
1063 }
1064 }
1065 if !trimmed {
1066 return fields
1067 }
1068 unexportedField := &ast.Field{
1069 Type: &ast.Ident{
1070
1071
1072
1073
1074 Name: "",
1075 NamePos: fields.Closing - 1,
1076 },
1077 Comment: &ast.CommentGroup{
1078 List: []*ast.Comment{{Text: fmt.Sprintf("// Has unexported %s.\n", what)}},
1079 },
1080 }
1081 return &ast.FieldList{
1082 Opening: fields.Opening,
1083 List: append(list, unexportedField),
1084 Closing: fields.Closing,
1085 }
1086 }
1087
1088
1089
1090
1091 func (pkg *Package) printMethodDoc(symbol, method string) bool {
1092 types := pkg.findTypes(symbol)
1093 if types == nil {
1094 if symbol == "" {
1095 return false
1096 }
1097 pkg.Fatalf("symbol %s is not a type in package %s installed in %q", symbol, pkg.name, pkg.build.ImportPath)
1098 }
1099 found := false
1100 for _, typ := range types {
1101 if len(typ.Methods) > 0 {
1102 for _, meth := range typ.Methods {
1103 if match(method, meth.Name) {
1104 decl := meth.Decl
1105 pkg.emit(meth.Doc, decl)
1106 pkg.exampleSummary(meth.Examples, true)
1107 found = true
1108 }
1109 }
1110 continue
1111 }
1112 if symbol == "" {
1113 continue
1114 }
1115
1116
1117 spec := pkg.findTypeSpec(typ.Decl, typ.Name)
1118 inter, ok := spec.Type.(*ast.InterfaceType)
1119 if !ok {
1120
1121 continue
1122 }
1123
1124
1125 var methods []*ast.Field
1126 for _, iMethod := range inter.Methods.List {
1127
1128
1129 if len(iMethod.Names) == 0 {
1130 continue
1131 }
1132 name := iMethod.Names[0].Name
1133 if match(method, name) {
1134 methods = append(methods, iMethod)
1135 found = true
1136 }
1137 }
1138 if found {
1139 pkg.Printf("type %s ", spec.Name)
1140 inter.Methods.List, methods = methods, inter.Methods.List
1141 err := format.Node(&pkg.buf, pkg.fs, inter)
1142 if err != nil {
1143 log.Fatal(err)
1144 }
1145 pkg.newlines(1)
1146
1147 inter.Methods.List = methods
1148 }
1149 }
1150 return found
1151 }
1152
1153
1154
1155
1156 func (pkg *Package) printFieldDoc(symbol, fieldName string) bool {
1157 if symbol == "" || fieldName == "" {
1158 return false
1159 }
1160 types := pkg.findTypes(symbol)
1161 if types == nil {
1162 pkg.Fatalf("symbol %s is not a type in package %s installed in %q", symbol, pkg.name, pkg.build.ImportPath)
1163 }
1164 found := false
1165 numUnmatched := 0
1166 for _, typ := range types {
1167
1168 spec := pkg.findTypeSpec(typ.Decl, typ.Name)
1169 structType, ok := spec.Type.(*ast.StructType)
1170 if !ok {
1171
1172 continue
1173 }
1174 for _, field := range structType.Fields.List {
1175
1176 for _, name := range field.Names {
1177 if !match(fieldName, name.Name) {
1178 numUnmatched++
1179 continue
1180 }
1181 if !found {
1182 pkg.Printf("type %s struct {\n", typ.Name)
1183 }
1184 if field.Doc != nil {
1185
1186
1187 docBuf := new(bytes.Buffer)
1188 pkg.ToText(docBuf, field.Doc.Text(), "", indent)
1189 scanner := bufio.NewScanner(docBuf)
1190 for scanner.Scan() {
1191 fmt.Fprintf(&pkg.buf, "%s// %s\n", indent, scanner.Bytes())
1192 }
1193 }
1194 s := pkg.oneLineNode(field.Type)
1195 lineComment := ""
1196 if field.Comment != nil {
1197 lineComment = fmt.Sprintf(" %s", field.Comment.List[0].Text)
1198 }
1199 pkg.Printf("%s%s %s%s\n", indent, name, s, lineComment)
1200 found = true
1201 }
1202 }
1203 }
1204 if found {
1205 if numUnmatched > 0 {
1206 pkg.Printf("\n // ... other fields elided ...\n")
1207 }
1208 pkg.Printf("}\n")
1209 }
1210 return found
1211 }
1212
1213
1214
1215
1216 func match(user, program string) bool {
1217 if !isExported(program) {
1218 return false
1219 }
1220 if matchCase {
1221 return user == program
1222 }
1223 for _, u := range user {
1224 p, w := utf8.DecodeRuneInString(program)
1225 program = program[w:]
1226 if u == p {
1227 continue
1228 }
1229 if unicode.IsLower(u) && simpleFold(u) == simpleFold(p) {
1230 continue
1231 }
1232 return false
1233 }
1234 return program == ""
1235 }
1236
1237
1238
1239 func simpleFold(r rune) rune {
1240 for {
1241 r1 := unicode.SimpleFold(r)
1242 if r1 <= r {
1243 return r1
1244 }
1245 r = r1
1246 }
1247 }
1248
1249
1250 func (pkg *Package) emitExample(ex *doc.Example) {
1251 pkg.buf.printed = true
1252 var err error
1253 if ex.Play != nil {
1254 err = format.Node(&pkg.buf, pkg.fs, ex.Play)
1255 } else {
1256
1257
1258 b, ok := ex.Code.(*ast.BlockStmt)
1259 if !ok {
1260 err = format.Node(&pkg.buf, pkg.fs, ex.Code)
1261 } else {
1262 err = format.Node(&pkg.buf, pkg.fs, b.List)
1263 }
1264 }
1265 if err != nil {
1266 log.Fatal(err)
1267 }
1268 if ex.Output != "" {
1269 pkg.newlines(2)
1270 pkg.Printf("Output: ")
1271 if strings.Count(ex.Output, "\n") > 1 {
1272 pkg.newlines(1)
1273 }
1274 pkg.Printf("%s", ex.Output)
1275 }
1276 pkg.newlines(1)
1277 }
1278
View as plain text