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