1// Copyright 2015 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package main
6
7import (
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
27const (
28	punchedCardWidth = 80
29	indent           = "    "
30)
31
32type Package struct {
33	writer      io.Writer    // Destination for output.
34	name        string       // Package name, json for encoding/json.
35	userPath    string       // String the user used to find this package.
36	pkg         *ast.Package // Parsed package.
37	file        *ast.File    // Merged from all files in the package
38	doc         *doc.Package
39	build       *build.Package
40	typedValue  map[*doc.Value]bool // Consts and vars related to types.
41	constructor map[*doc.Func]bool  // Constructors.
42	fs          *token.FileSet      // Needed for printing.
43	buf         pkgBuffer
44}
45
46func (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// pkgBuffer is a wrapper for bytes.Buffer that prints a package clause the
55// first time Write is called.
56type pkgBuffer struct {
57	pkg     *Package
58	printed bool // Prevent repeated package clauses.
59	bytes.Buffer
60}
61
62func (pb *pkgBuffer) Write(p []byte) (int, error) {
63	pb.packageClause()
64	return pb.Buffer.Write(p)
65}
66
67func (pb *pkgBuffer) packageClause() {
68	if !pb.printed {
69		pb.printed = true
70		// Only show package clause for commands if requested explicitly.
71		if pb.pkg.pkg.Name != "main" || showCmd {
72			pb.pkg.packageClause()
73		}
74	}
75}
76
77type PackageError string // type returned by pkg.Fatalf.
78
79func (p PackageError) Error() string {
80	return string(p)
81}
82
83// prettyPath returns a version of the package path that is suitable for an
84// error message. It obeys the import comment if present. Also, since
85// pkg.build.ImportPath is sometimes the unhelpful "" or ".", it looks for a
86// directory name in GOROOT or GOPATH if that happens.
87func (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	// Convert the source directory into a more useful path.
96	// Also convert everything to slash-separated paths for uniform handling.
97	path = filepath.Clean(filepath.ToSlash(pkg.build.Dir))
98	// Can we find a decent prefix?
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// trim trims the directory prefix from the path, paying attention
114// to the path separator. If they are the same string or the prefix
115// is not present the original is returned. The boolean reports whether
116// the prefix is present. That path and prefix have slashes for separators.
117func 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 // Textual prefix but not a path prefix.
128}
129
130// pkg.Fatalf is like log.Fatalf, but panics so it can be recovered in the
131// main do function, so it doesn't cause an exit. Allows testing to work
132// without running a subprocess. The log prefix will be added when
133// logged in main; it is not added here.
134func (pkg *Package) Fatalf(format string, args ...any) {
135	panic(PackageError(fmt.Sprintf(format, args...)))
136}
137
138// parsePackage turns the build package we found into a parsed package
139// we can then use to generate documentation.
140func parsePackage(writer io.Writer, pkg *build.Package, userPath string) *Package {
141	// include tells parser.ParseDir which files to include.
142	// That means the file must be in the build package's GoFiles or CgoFiles
143	// list only (no tag-ignored files, tests, swig or other non-Go files).
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	// Make sure they are all in one package.
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	// TODO: go/doc does not include typed constants in the constants
172	// list, which is what we want. For instance, time.Sunday is of type
173	// time.Weekday, so it is defined in the type but not in the
174	// Consts list for the package. This prevents
175	//	go doc time.Sunday
176	// from finding the symbol. Work around this for now, but we
177	// should fix it in go/doc.
178	// A similar story applies to factory functions.
179	mode := doc.AllDecls
180	if showSrc {
181		mode |= doc.PreserveAST // See comment for Package.emit.
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				// We don't count it as a constructor bound to the type
199				// if the type itself is not exported.
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
221func (pkg *Package) Printf(format string, args ...any) {
222	fmt.Fprintf(&pkg.buf, format, args...)
223}
224
225func (pkg *Package) flush() {
226	_, err := pkg.writer.Write(pkg.buf.Bytes())
227	if err != nil {
228		log.Fatal(err)
229	}
230	pkg.buf.Reset() // Not needed, but it's a flush.
231}
232
233var newlineBytes = []byte("\n\n") // We never ask for more than 2.
234
235// newlines guarantees there are n newlines at the end of the buffer.
236func (pkg *Package) newlines(n int) {
237	for !bytes.HasSuffix(pkg.buf.Bytes(), newlineBytes[:n]) {
238		pkg.buf.WriteRune('\n')
239	}
240}
241
242// emit prints the node. If showSrc is true, it ignores the provided comment,
243// assuming the comment is in the node itself. Otherwise, the go/doc package
244// clears the stuff we don't want to print anyway. It's a bit of a magic trick.
245func (pkg *Package) emit(comment string, node ast.Node) {
246	if node != nil {
247		var arg any = node
248		if showSrc {
249			// Need an extra little dance to get internal comments to appear.
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) // Blank line after comment to separate from next item.
263		} else {
264			pkg.newlines(1)
265		}
266	}
267}
268
269// oneLineNode returns a one-line summary of the given input node.
270func (pkg *Package) oneLineNode(node ast.Node) string {
271	const maxDepth = 10
272	return pkg.oneLineNodeDepth(node, maxDepth)
273}
274
275// oneLineNodeDepth returns a one-line summary of the given input node.
276// The depth specifies the maximum depth when traversing the AST.
277func (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		// Formats const and var declarations.
290		trailer := ""
291		if len(n.Specs) > 1 {
292			trailer = " " + dotDotDot
293		}
294
295		// Find the first relevant spec.
296		typ := ""
297		for i, spec := range n.Specs {
298			valueSpec := spec.(*ast.ValueSpec) // Must succeed; we can't mix types in one GenDecl.
299
300			// The type name may carry over from a previous specification in the
301			// case of constants and iota.
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		// Formats func declarations.
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		// As a fallback, use default formatter for all unknown node types.
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
433func (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// oneLineField returns a one-line summary of the field.
445func (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// joinStrings formats the input as a comma-separated list,
457// but truncates the list at some reasonable length if necessary.
458func 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// printHeader prints a header for the section named s, adding a blank line on each side.
471func (pkg *Package) printHeader(s string) {
472	pkg.Printf("\n%s\n\n", s)
473}
474
475// constsDoc prints all const documentation, if any, including a header.
476// The one argument is the valueDoc registry.
477func (pkg *Package) constsDoc(printed map[*ast.GenDecl]bool) {
478	var header bool
479	for _, value := range pkg.doc.Consts {
480		// Constants and variables come in groups, and valueDoc prints
481		// all the items in the group. We only need to find one exported symbol.
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// varsDoc prints all var documentation, if any, including a header.
496// Printed is the valueDoc registry.
497func (pkg *Package) varsDoc(printed map[*ast.GenDecl]bool) {
498	var header bool
499	for _, value := range pkg.doc.Vars {
500		// Constants and variables come in groups, and valueDoc prints
501		// all the items in the group. We only need to find one exported symbol.
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// funcsDoc prints all func documentation, if any, including a header.
516func (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// funcsDoc prints all type documentation, if any, including a header.
530func (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// packageDoc prints the docs for the package.
544func (pkg *Package) packageDoc() {
545	pkg.Printf("") // Trigger the package clause; we know the package exists.
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) // valueDoc registry
554		pkg.constsDoc(printed)
555		pkg.varsDoc(printed)
556		pkg.funcsDoc()
557		pkg.typesDoc()
558
559	case pkg.pkg.Name == "main" && !showCmd:
560		// Show only package docs for commands.
561		return
562
563	default:
564		if !short {
565			pkg.newlines(2) // Guarantee blank line before the components.
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// packageClause prints the package clause.
579func (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	// If we're using modules, the import path derived from module code locations wins.
589	// If we did a file system scan, we knew the import path when we found the directory.
590	// But if we started with a directory name, we never knew the import path.
591	// Either way, we don't know it now, and it's cheap to (re)compute it.
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// valueSummary prints a one-line summary for each set of values and constants.
617// If all the types in a constant or variable declaration belong to the same
618// type they can be printed by typeSummary, and so can be suppressed here.
619func (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// funcSummary prints a one-line summary for each function. Constructors
646// are printed by typeSummary, below, and so can be suppressed here.
647func (pkg *Package) funcSummary(funcs []*doc.Func, showConstructors bool) {
648	for _, fun := range funcs {
649		// Exported functions only. The go/doc package does not include methods here.
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// typeSummary prints a one-line summary for each type, followed by its constructors.
659func (pkg *Package) typeSummary() {
660	for _, typ := range pkg.doc.Types {
661		for _, spec := range typ.Decl.Specs {
662			typeSpec := spec.(*ast.TypeSpec) // Must succeed.
663			if isExported(typeSpec.Name.Name) {
664				pkg.Printf("%s\n", pkg.oneLineNode(typeSpec))
665				// Now print the consts, vars, and constructors.
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// bugs prints the BUGS information for the package.
687// TODO: Provide access to TODOs and NOTEs as well (very noisy so off by default)?
688func (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// findValues finds the doc.Values that describe the symbol.
699func (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// findFuncs finds the doc.Funcs that describes the symbol.
711func (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// findTypes finds the doc.Types that describes the symbol.
721// If symbol is empty, it finds all exported types.
722func (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// findTypeSpec returns the ast.TypeSpec within the declaration that defines the symbol.
732// The name must match exactly.
733func (pkg *Package) findTypeSpec(decl *ast.GenDecl, symbol string) *ast.TypeSpec {
734	for _, spec := range decl.Specs {
735		typeSpec := spec.(*ast.TypeSpec) // Must succeed.
736		if symbol == typeSpec.Name.Name {
737			return typeSpec
738		}
739	}
740	return nil
741}
742
743// symbolDoc prints the docs for symbol. There may be multiple matches.
744// If symbol matches a type, output includes its methods factories and associated constants.
745// If there is no top-level symbol, symbolDoc looks for methods that match.
746func (pkg *Package) symbolDoc(symbol string) bool {
747	found := false
748	// Functions.
749	for _, fun := range pkg.findFuncs(symbol) {
750		// Symbol is a function.
751		decl := fun.Decl
752		pkg.emit(fun.Doc, decl)
753		found = true
754	}
755	// Constants and variables behave the same.
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) // valueDoc registry
759	for _, value := range values {
760		pkg.valueDoc(value, printed)
761		found = true
762	}
763	// Types.
764	for _, typ := range pkg.findTypes(symbol) {
765		pkg.typeDoc(typ)
766		found = true
767	}
768	if !found {
769		// See if there are methods.
770		if !pkg.printMethodDoc("", symbol) {
771			return false
772		}
773	}
774	return true
775}
776
777// valueDoc prints the docs for a constant or variable. The printed map records
778// which values have been printed already to avoid duplication. Otherwise, a
779// declaration like:
780//
781//	const ( c = 1; C = 2 )
782//
783// … could be printed twice if the -u flag is set, as it matches twice.
784func (pkg *Package) valueDoc(value *doc.Value, printed map[*ast.GenDecl]bool) {
785	if printed[value.Decl] {
786		return
787	}
788	// Print each spec only if there is at least one exported symbol in it.
789	// (See issue 11008.)
790	// TODO: Should we elide unexported symbols from a single spec?
791	// It's an unlikely scenario, probably not worth the trouble.
792	// TODO: Would be nice if go/doc did this for us.
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		// The type name may carry over from a previous specification in the
799		// case of constants and iota.
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					// This a standalone identifier, as in the case of iota usage.
808					// Thus, assume the type comes from the previous type.
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 // Only inject type on first exported identifier
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// typeDoc prints the docs for a type, including constructors and other items
830// related to it.
831func (pkg *Package) typeDoc(typ *doc.Type) {
832	decl := typ.Decl
833	spec := pkg.findTypeSpec(decl, typ.Name)
834	trimUnexportedElems(spec)
835	// If there are multiple types defined, reduce to just this one.
836	if len(decl.Specs) > 1 {
837		decl.Specs = []ast.Spec{spec}
838	}
839	pkg.emit(typ.Doc, decl)
840	pkg.newlines(2)
841	// Show associated methods, constants, etc.
842	if showAll {
843		printed := make(map[*ast.GenDecl]bool) // valueDoc registry
844		// We can use append here to print consts, then vars. Ditto for funcs and methods.
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// trimUnexportedElems modifies spec in place to elide unexported fields from
874// structs and methods from interfaces (unless the unexported flag is set or we
875// are asked to show the original source).
876func 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// trimUnexportedFields returns the field list trimmed of unexported fields.
889func 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		// When printing fields we normally print field.Doc.
899		// Here we are going to pass the AST to go/format,
900		// which will print the comments from the AST,
901		// not field.Doc which is from go/doc.
902		// The two are similar but not identical;
903		// for example, field.Doc does not include directives.
904		// In order to consistently print field.Doc,
905		// we replace the comment in the AST with field.Doc.
906		// That will cause go/format to print what we want.
907		// See issue #56592.
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				// Remove trailing newline.
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			// Embedded type. Use the name of the type. It must be of the form ident or
938			// pkg.ident (for structs and interfaces), or *ident or *pkg.ident (structs only).
939			// Or a type embedded in a constraint.
940			// Nothing else is allowed.
941			ty := field.Type
942			if se, ok := field.Type.(*ast.StarExpr); !isInterface && ok {
943				// The form *ident or *pkg.ident is only valid on
944				// embedded types in structs.
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					// For documentation purposes, we consider the builtin error
952					// type special when embedded in an interface, such that it
953					// always gets shown publicly.
954					list = append(list, field)
955					continue
956				}
957				names = []*ast.Ident{ident}
958			case *ast.SelectorExpr:
959				// An embedded type may refer to a type in another package.
960				names = []*ast.Ident{ident.Sel}
961			default:
962				// An approximation or union or type
963				// literal in an interface.
964				constraint = true
965			}
966			if names == nil && !constraint {
967				// Can only happen if AST is incorrect. Safe to continue with a nil list.
968				log.Print("invalid program: unexpected type for embedded field")
969			}
970		}
971		// Trims if any is unexported. Good enough in practice.
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			// Hack: printer will treat this as a field with a named type.
992			// Setting Name and NamePos to ("", fields.Closing-1) ensures that
993			// when Pos and End are called on this field, they return the
994			// position right before closing '}' character.
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// printMethodDoc prints the docs for matches of symbol.method.
1010// If symbol is empty, it prints all methods for any concrete type
1011// that match the name. It reports whether it found any methods.
1012func (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		// Type may be an interface. The go/doc package does not attach
1036		// an interface's methods to the doc.Type. We need to dig around.
1037		spec := pkg.findTypeSpec(typ.Decl, typ.Name)
1038		inter, ok := spec.Type.(*ast.InterfaceType)
1039		if !ok {
1040			// Not an interface type.
1041			continue
1042		}
1043
1044		// Collect and print only the methods that match.
1045		var methods []*ast.Field
1046		for _, iMethod := range inter.Methods.List {
1047			// This is an interface, so there can be only one name.
1048			// TODO: Anonymous methods (embedding)
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			// Restore the original methods.
1067			inter.Methods.List = methods
1068		}
1069	}
1070	return found
1071}
1072
1073// printFieldDoc prints the docs for matches of symbol.fieldName.
1074// It reports whether it found any field.
1075// Both symbol and fieldName must be non-empty or it returns false.
1076func (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		// Type must be a struct.
1088		spec := pkg.findTypeSpec(typ.Decl, typ.Name)
1089		structType, ok := spec.Type.(*ast.StructType)
1090		if !ok {
1091			// Not a struct type.
1092			continue
1093		}
1094		for _, field := range structType.Fields.List {
1095			// TODO: Anonymous fields.
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					// To present indented blocks in comments correctly, process the comment as
1106					// a unit before adding the leading // to each line.
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// match reports whether the user's symbol matches the program's.
1134// A lower-case character in the user's string matches either case in the program's.
1135// The program string must be exported.
1136func 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// simpleFold returns the minimum rune equivalent to r
1158// under Unicode-defined simple case folding.
1159func simpleFold(r rune) rune {
1160	for {
1161		r1 := unicode.SimpleFold(r)
1162		if r1 <= r {
1163			return r1 // wrapped around, found min
1164		}
1165		r = r1
1166	}
1167}
1168