...
Run Format

Source file src/cmd/doc/pkg.go

Documentation: cmd/doc

     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  
     5  package main
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"go/ast"
    11  	"go/build"
    12  	"go/doc"
    13  	"go/format"
    14  	"go/parser"
    15  	"go/token"
    16  	"io"
    17  	"log"
    18  	"os"
    19  	"path/filepath"
    20  	"strings"
    21  	"unicode"
    22  	"unicode/utf8"
    23  )
    24  
    25  const (
    26  	punchedCardWidth = 80 // These things just won't leave us alone.
    27  	indentedWidth    = punchedCardWidth - len(indent)
    28  	indent           = "    "
    29  )
    30  
    31  type Package struct {
    32  	writer   io.Writer    // Destination for output.
    33  	name     string       // Package name, json for encoding/json.
    34  	userPath string       // String the user used to find this package.
    35  	pkg      *ast.Package // Parsed package.
    36  	file     *ast.File    // Merged from all files in the package
    37  	doc      *doc.Package
    38  	build    *build.Package
    39  	fs       *token.FileSet // Needed for printing.
    40  	buf      bytes.Buffer
    41  }
    42  
    43  type PackageError string // type returned by pkg.Fatalf.
    44  
    45  func (p PackageError) Error() string {
    46  	return string(p)
    47  }
    48  
    49  // prettyPath returns a version of the package path that is suitable for an
    50  // error message. It obeys the import comment if present. Also, since
    51  // pkg.build.ImportPath is sometimes the unhelpful "" or ".", it looks for a
    52  // directory name in GOROOT or GOPATH if that happens.
    53  func (pkg *Package) prettyPath() string {
    54  	path := pkg.build.ImportComment
    55  	if path == "" {
    56  		path = pkg.build.ImportPath
    57  	}
    58  	if path != "." && path != "" {
    59  		return path
    60  	}
    61  	// Convert the source directory into a more useful path.
    62  	// Also convert everything to slash-separated paths for uniform handling.
    63  	path = filepath.Clean(filepath.ToSlash(pkg.build.Dir))
    64  	// Can we find a decent prefix?
    65  	goroot := filepath.Join(buildCtx.GOROOT, "src")
    66  	if p, ok := trim(path, filepath.ToSlash(goroot)); ok {
    67  		return p
    68  	}
    69  	for _, gopath := range splitGopath() {
    70  		if p, ok := trim(path, filepath.ToSlash(gopath)); ok {
    71  			return p
    72  		}
    73  	}
    74  	return path
    75  }
    76  
    77  // trim trims the directory prefix from the path, paying attention
    78  // to the path separator. If they are the same string or the prefix
    79  // is not present the original is returned. The boolean reports whether
    80  // the prefix is present. That path and prefix have slashes for separators.
    81  func trim(path, prefix string) (string, bool) {
    82  	if !strings.HasPrefix(path, prefix) {
    83  		return path, false
    84  	}
    85  	if path == prefix {
    86  		return path, true
    87  	}
    88  	if path[len(prefix)] == '/' {
    89  		return path[len(prefix)+1:], true
    90  	}
    91  	return path, false // Textual prefix but not a path prefix.
    92  }
    93  
    94  // pkg.Fatalf is like log.Fatalf, but panics so it can be recovered in the
    95  // main do function, so it doesn't cause an exit. Allows testing to work
    96  // without running a subprocess. The log prefix will be added when
    97  // logged in main; it is not added here.
    98  func (pkg *Package) Fatalf(format string, args ...interface{}) {
    99  	panic(PackageError(fmt.Sprintf(format, args...)))
   100  }
   101  
   102  // parsePackage turns the build package we found into a parsed package
   103  // we can then use to generate documentation.
   104  func parsePackage(writer io.Writer, pkg *build.Package, userPath string) *Package {
   105  	fs := token.NewFileSet()
   106  	// include tells parser.ParseDir which files to include.
   107  	// That means the file must be in the build package's GoFiles or CgoFiles
   108  	// list only (no tag-ignored files, tests, swig or other non-Go files).
   109  	include := func(info os.FileInfo) bool {
   110  		for _, name := range pkg.GoFiles {
   111  			if name == info.Name() {
   112  				return true
   113  			}
   114  		}
   115  		for _, name := range pkg.CgoFiles {
   116  			if name == info.Name() {
   117  				return true
   118  			}
   119  		}
   120  		return false
   121  	}
   122  	pkgs, err := parser.ParseDir(fs, pkg.Dir, include, parser.ParseComments)
   123  	if err != nil {
   124  		log.Fatal(err)
   125  	}
   126  	// Make sure they are all in one package.
   127  	if len(pkgs) != 1 {
   128  		log.Fatalf("multiple packages in directory %s", pkg.Dir)
   129  	}
   130  	astPkg := pkgs[pkg.Name]
   131  
   132  	// TODO: go/doc does not include typed constants in the constants
   133  	// list, which is what we want. For instance, time.Sunday is of type
   134  	// time.Weekday, so it is defined in the type but not in the
   135  	// Consts list for the package. This prevents
   136  	//	go doc time.Sunday
   137  	// from finding the symbol. Work around this for now, but we
   138  	// should fix it in go/doc.
   139  	// A similar story applies to factory functions.
   140  	docPkg := doc.New(astPkg, pkg.ImportPath, doc.AllDecls)
   141  	for _, typ := range docPkg.Types {
   142  		docPkg.Consts = append(docPkg.Consts, typ.Consts...)
   143  		docPkg.Vars = append(docPkg.Vars, typ.Vars...)
   144  		docPkg.Funcs = append(docPkg.Funcs, typ.Funcs...)
   145  	}
   146  
   147  	return &Package{
   148  		writer:   writer,
   149  		name:     pkg.Name,
   150  		userPath: userPath,
   151  		pkg:      astPkg,
   152  		file:     ast.MergePackageFiles(astPkg, 0),
   153  		doc:      docPkg,
   154  		build:    pkg,
   155  		fs:       fs,
   156  	}
   157  }
   158  
   159  func (pkg *Package) Printf(format string, args ...interface{}) {
   160  	fmt.Fprintf(&pkg.buf, format, args...)
   161  }
   162  
   163  func (pkg *Package) flush() {
   164  	_, err := pkg.writer.Write(pkg.buf.Bytes())
   165  	if err != nil {
   166  		log.Fatal(err)
   167  	}
   168  	pkg.buf.Reset() // Not needed, but it's a flush.
   169  }
   170  
   171  var newlineBytes = []byte("\n\n") // We never ask for more than 2.
   172  
   173  // newlines guarantees there are n newlines at the end of the buffer.
   174  func (pkg *Package) newlines(n int) {
   175  	for !bytes.HasSuffix(pkg.buf.Bytes(), newlineBytes[:n]) {
   176  		pkg.buf.WriteRune('\n')
   177  	}
   178  }
   179  
   180  // emit prints the node.
   181  func (pkg *Package) emit(comment string, node ast.Node) {
   182  	if node != nil {
   183  		err := format.Node(&pkg.buf, pkg.fs, node)
   184  		if err != nil {
   185  			log.Fatal(err)
   186  		}
   187  		if comment != "" {
   188  			pkg.newlines(1)
   189  			doc.ToText(&pkg.buf, comment, "    ", indent, indentedWidth)
   190  			pkg.newlines(2) // Blank line after comment to separate from next item.
   191  		} else {
   192  			pkg.newlines(1)
   193  		}
   194  	}
   195  }
   196  
   197  // oneLineNode returns a one-line summary of the given input node.
   198  func (pkg *Package) oneLineNode(node ast.Node) string {
   199  	const maxDepth = 10
   200  	return pkg.oneLineNodeDepth(node, maxDepth)
   201  }
   202  
   203  // oneLineNodeDepth returns a one-line summary of the given input node.
   204  // The depth specifies the maximum depth when traversing the AST.
   205  func (pkg *Package) oneLineNodeDepth(node ast.Node, depth int) string {
   206  	const dotDotDot = "..."
   207  	if depth == 0 {
   208  		return dotDotDot
   209  	}
   210  	depth--
   211  
   212  	switch n := node.(type) {
   213  	case nil:
   214  		return ""
   215  
   216  	case *ast.GenDecl:
   217  		// Formats const and var declarations.
   218  		trailer := ""
   219  		if len(n.Specs) > 1 {
   220  			trailer = " " + dotDotDot
   221  		}
   222  
   223  		// Find the first relevant spec.
   224  		typ := ""
   225  		for i, spec := range n.Specs {
   226  			valueSpec := spec.(*ast.ValueSpec) // Must succeed; we can't mix types in one GenDecl.
   227  
   228  			// The type name may carry over from a previous specification in the
   229  			// case of constants and iota.
   230  			if valueSpec.Type != nil {
   231  				typ = fmt.Sprintf(" %s", pkg.oneLineNodeDepth(valueSpec.Type, depth))
   232  			} else if len(valueSpec.Values) > 0 {
   233  				typ = ""
   234  			}
   235  
   236  			if !isExported(valueSpec.Names[0].Name) {
   237  				continue
   238  			}
   239  			val := ""
   240  			if i < len(valueSpec.Values) && valueSpec.Values[i] != nil {
   241  				val = fmt.Sprintf(" = %s", pkg.oneLineNodeDepth(valueSpec.Values[i], depth))
   242  			}
   243  			return fmt.Sprintf("%s %s%s%s%s", n.Tok, valueSpec.Names[0], typ, val, trailer)
   244  		}
   245  		return ""
   246  
   247  	case *ast.FuncDecl:
   248  		// Formats func declarations.
   249  		name := n.Name.Name
   250  		recv := pkg.oneLineNodeDepth(n.Recv, depth)
   251  		if len(recv) > 0 {
   252  			recv = "(" + recv + ") "
   253  		}
   254  		fnc := pkg.oneLineNodeDepth(n.Type, depth)
   255  		if strings.Index(fnc, "func") == 0 {
   256  			fnc = fnc[4:]
   257  		}
   258  		return fmt.Sprintf("func %s%s%s", recv, name, fnc)
   259  
   260  	case *ast.TypeSpec:
   261  		sep := " "
   262  		if n.Assign.IsValid() {
   263  			sep = " = "
   264  		}
   265  		return fmt.Sprintf("type %s%s%s", n.Name.Name, sep, pkg.oneLineNodeDepth(n.Type, depth))
   266  
   267  	case *ast.FuncType:
   268  		var params []string
   269  		if n.Params != nil {
   270  			for _, field := range n.Params.List {
   271  				params = append(params, pkg.oneLineField(field, depth))
   272  			}
   273  		}
   274  		needParens := false
   275  		var results []string
   276  		if n.Results != nil {
   277  			needParens = needParens || len(n.Results.List) > 1
   278  			for _, field := range n.Results.List {
   279  				needParens = needParens || len(field.Names) > 0
   280  				results = append(results, pkg.oneLineField(field, depth))
   281  			}
   282  		}
   283  
   284  		param := joinStrings(params)
   285  		if len(results) == 0 {
   286  			return fmt.Sprintf("func(%s)", param)
   287  		}
   288  		result := joinStrings(results)
   289  		if !needParens {
   290  			return fmt.Sprintf("func(%s) %s", param, result)
   291  		}
   292  		return fmt.Sprintf("func(%s) (%s)", param, result)
   293  
   294  	case *ast.StructType:
   295  		if n.Fields == nil || len(n.Fields.List) == 0 {
   296  			return "struct{}"
   297  		}
   298  		return "struct{ ... }"
   299  
   300  	case *ast.InterfaceType:
   301  		if n.Methods == nil || len(n.Methods.List) == 0 {
   302  			return "interface{}"
   303  		}
   304  		return "interface{ ... }"
   305  
   306  	case *ast.FieldList:
   307  		if n == nil || len(n.List) == 0 {
   308  			return ""
   309  		}
   310  		if len(n.List) == 1 {
   311  			return pkg.oneLineField(n.List[0], depth)
   312  		}
   313  		return dotDotDot
   314  
   315  	case *ast.FuncLit:
   316  		return pkg.oneLineNodeDepth(n.Type, depth) + " { ... }"
   317  
   318  	case *ast.CompositeLit:
   319  		typ := pkg.oneLineNodeDepth(n.Type, depth)
   320  		if len(n.Elts) == 0 {
   321  			return fmt.Sprintf("%s{}", typ)
   322  		}
   323  		return fmt.Sprintf("%s{ %s }", typ, dotDotDot)
   324  
   325  	case *ast.ArrayType:
   326  		length := pkg.oneLineNodeDepth(n.Len, depth)
   327  		element := pkg.oneLineNodeDepth(n.Elt, depth)
   328  		return fmt.Sprintf("[%s]%s", length, element)
   329  
   330  	case *ast.MapType:
   331  		key := pkg.oneLineNodeDepth(n.Key, depth)
   332  		value := pkg.oneLineNodeDepth(n.Value, depth)
   333  		return fmt.Sprintf("map[%s]%s", key, value)
   334  
   335  	case *ast.CallExpr:
   336  		fnc := pkg.oneLineNodeDepth(n.Fun, depth)
   337  		var args []string
   338  		for _, arg := range n.Args {
   339  			args = append(args, pkg.oneLineNodeDepth(arg, depth))
   340  		}
   341  		return fmt.Sprintf("%s(%s)", fnc, joinStrings(args))
   342  
   343  	case *ast.UnaryExpr:
   344  		return fmt.Sprintf("%s%s", n.Op, pkg.oneLineNodeDepth(n.X, depth))
   345  
   346  	case *ast.Ident:
   347  		return n.Name
   348  
   349  	default:
   350  		// As a fallback, use default formatter for all unknown node types.
   351  		buf := new(bytes.Buffer)
   352  		format.Node(buf, pkg.fs, node)
   353  		s := buf.String()
   354  		if strings.Contains(s, "\n") {
   355  			return dotDotDot
   356  		}
   357  		return s
   358  	}
   359  }
   360  
   361  // oneLineField returns a one-line summary of the field.
   362  func (pkg *Package) oneLineField(field *ast.Field, depth int) string {
   363  	var names []string
   364  	for _, name := range field.Names {
   365  		names = append(names, name.Name)
   366  	}
   367  	if len(names) == 0 {
   368  		return pkg.oneLineNodeDepth(field.Type, depth)
   369  	}
   370  	return joinStrings(names) + " " + pkg.oneLineNodeDepth(field.Type, depth)
   371  }
   372  
   373  // joinStrings formats the input as a comma-separated list,
   374  // but truncates the list at some reasonable length if necessary.
   375  func joinStrings(ss []string) string {
   376  	var n int
   377  	for i, s := range ss {
   378  		n += len(s) + len(", ")
   379  		if n > punchedCardWidth {
   380  			ss = append(ss[:i:i], "...")
   381  			break
   382  		}
   383  	}
   384  	return strings.Join(ss, ", ")
   385  }
   386  
   387  // packageDoc prints the docs for the package (package doc plus one-liners of the rest).
   388  func (pkg *Package) packageDoc() {
   389  	defer pkg.flush()
   390  	if pkg.showInternals() {
   391  		pkg.packageClause(false)
   392  	}
   393  
   394  	doc.ToText(&pkg.buf, pkg.doc.Doc, "", indent, indentedWidth)
   395  	pkg.newlines(1)
   396  
   397  	if !pkg.showInternals() {
   398  		// Show only package docs for commands.
   399  		return
   400  	}
   401  
   402  	pkg.newlines(2) // Guarantee blank line before the components.
   403  	pkg.valueSummary(pkg.doc.Consts, false)
   404  	pkg.valueSummary(pkg.doc.Vars, false)
   405  	pkg.funcSummary(pkg.doc.Funcs, false)
   406  	pkg.typeSummary()
   407  	pkg.bugs()
   408  }
   409  
   410  // showInternals reports whether we should show the internals
   411  // of a package as opposed to just the package docs.
   412  // Used to decide whether to suppress internals for commands.
   413  // Called only by Package.packageDoc.
   414  func (pkg *Package) showInternals() bool {
   415  	return pkg.pkg.Name != "main" || showCmd
   416  }
   417  
   418  // packageClause prints the package clause.
   419  // The argument boolean, if true, suppresses the output if the
   420  // user's argument is identical to the actual package path or
   421  // is empty, meaning it's the current directory.
   422  func (pkg *Package) packageClause(checkUserPath bool) {
   423  	if checkUserPath {
   424  		if pkg.userPath == "" || pkg.userPath == pkg.build.ImportPath {
   425  			return
   426  		}
   427  	}
   428  
   429  	importPath := pkg.build.ImportComment
   430  	if importPath == "" {
   431  		importPath = pkg.build.ImportPath
   432  	}
   433  
   434  	// If we're using modules, the import path derived from module code locations wins.
   435  	// If we did a file system scan, we knew the import path when we found the directory.
   436  	// But if we started with a directory name, we never knew the import path.
   437  	// Either way, we don't know it now, and it's cheap to (re)compute it.
   438  	if usingModules {
   439  		for _, root := range codeRoots() {
   440  			if pkg.build.Dir == root.dir {
   441  				importPath = root.importPath
   442  				break
   443  			}
   444  			if strings.HasPrefix(pkg.build.Dir, root.dir+string(filepath.Separator)) {
   445  				suffix := filepath.ToSlash(pkg.build.Dir[len(root.dir)+1:])
   446  				if root.importPath == "" {
   447  					importPath = suffix
   448  				} else {
   449  					importPath = root.importPath + "/" + suffix
   450  				}
   451  				break
   452  			}
   453  		}
   454  	}
   455  
   456  	pkg.Printf("package %s // import %q\n\n", pkg.name, importPath)
   457  	if !usingModules && importPath != pkg.build.ImportPath {
   458  		pkg.Printf("WARNING: package source is installed in %q\n", pkg.build.ImportPath)
   459  	}
   460  }
   461  
   462  // valueSummary prints a one-line summary for each set of values and constants.
   463  // If all the types in a constant or variable declaration belong to the same
   464  // type they can be printed by typeSummary, and so can be suppressed here.
   465  func (pkg *Package) valueSummary(values []*doc.Value, showGrouped bool) {
   466  	var isGrouped map[*doc.Value]bool
   467  	if !showGrouped {
   468  		isGrouped = make(map[*doc.Value]bool)
   469  		for _, typ := range pkg.doc.Types {
   470  			if !isExported(typ.Name) {
   471  				continue
   472  			}
   473  			for _, c := range typ.Consts {
   474  				isGrouped[c] = true
   475  			}
   476  			for _, v := range typ.Vars {
   477  				isGrouped[v] = true
   478  			}
   479  		}
   480  	}
   481  
   482  	for _, value := range values {
   483  		if !isGrouped[value] {
   484  			if decl := pkg.oneLineNode(value.Decl); decl != "" {
   485  				pkg.Printf("%s\n", decl)
   486  			}
   487  		}
   488  	}
   489  }
   490  
   491  // funcSummary prints a one-line summary for each function. Constructors
   492  // are printed by typeSummary, below, and so can be suppressed here.
   493  func (pkg *Package) funcSummary(funcs []*doc.Func, showConstructors bool) {
   494  	// First, identify the constructors. Don't bother figuring out if they're exported.
   495  	var isConstructor map[*doc.Func]bool
   496  	if !showConstructors {
   497  		isConstructor = make(map[*doc.Func]bool)
   498  		for _, typ := range pkg.doc.Types {
   499  			if isExported(typ.Name) {
   500  				for _, f := range typ.Funcs {
   501  					isConstructor[f] = true
   502  				}
   503  			}
   504  		}
   505  	}
   506  	for _, fun := range funcs {
   507  		// Exported functions only. The go/doc package does not include methods here.
   508  		if isExported(fun.Name) {
   509  			if !isConstructor[fun] {
   510  				pkg.Printf("%s\n", pkg.oneLineNode(fun.Decl))
   511  			}
   512  		}
   513  	}
   514  }
   515  
   516  // typeSummary prints a one-line summary for each type, followed by its constructors.
   517  func (pkg *Package) typeSummary() {
   518  	for _, typ := range pkg.doc.Types {
   519  		for _, spec := range typ.Decl.Specs {
   520  			typeSpec := spec.(*ast.TypeSpec) // Must succeed.
   521  			if isExported(typeSpec.Name.Name) {
   522  				pkg.Printf("%s\n", pkg.oneLineNode(typeSpec))
   523  				// Now print the consts, vars, and constructors.
   524  				for _, c := range typ.Consts {
   525  					if decl := pkg.oneLineNode(c.Decl); decl != "" {
   526  						pkg.Printf(indent+"%s\n", decl)
   527  					}
   528  				}
   529  				for _, v := range typ.Vars {
   530  					if decl := pkg.oneLineNode(v.Decl); decl != "" {
   531  						pkg.Printf(indent+"%s\n", decl)
   532  					}
   533  				}
   534  				for _, constructor := range typ.Funcs {
   535  					if isExported(constructor.Name) {
   536  						pkg.Printf(indent+"%s\n", pkg.oneLineNode(constructor.Decl))
   537  					}
   538  				}
   539  			}
   540  		}
   541  	}
   542  }
   543  
   544  // bugs prints the BUGS information for the package.
   545  // TODO: Provide access to TODOs and NOTEs as well (very noisy so off by default)?
   546  func (pkg *Package) bugs() {
   547  	if pkg.doc.Notes["BUG"] == nil {
   548  		return
   549  	}
   550  	pkg.Printf("\n")
   551  	for _, note := range pkg.doc.Notes["BUG"] {
   552  		pkg.Printf("%s: %v\n", "BUG", note.Body)
   553  	}
   554  }
   555  
   556  // findValues finds the doc.Values that describe the symbol.
   557  func (pkg *Package) findValues(symbol string, docValues []*doc.Value) (values []*doc.Value) {
   558  	for _, value := range docValues {
   559  		for _, name := range value.Names {
   560  			if match(symbol, name) {
   561  				values = append(values, value)
   562  			}
   563  		}
   564  	}
   565  	return
   566  }
   567  
   568  // findFuncs finds the doc.Funcs that describes the symbol.
   569  func (pkg *Package) findFuncs(symbol string) (funcs []*doc.Func) {
   570  	for _, fun := range pkg.doc.Funcs {
   571  		if match(symbol, fun.Name) {
   572  			funcs = append(funcs, fun)
   573  		}
   574  	}
   575  	return
   576  }
   577  
   578  // findTypes finds the doc.Types that describes the symbol.
   579  // If symbol is empty, it finds all exported types.
   580  func (pkg *Package) findTypes(symbol string) (types []*doc.Type) {
   581  	for _, typ := range pkg.doc.Types {
   582  		if symbol == "" && isExported(typ.Name) || match(symbol, typ.Name) {
   583  			types = append(types, typ)
   584  		}
   585  	}
   586  	return
   587  }
   588  
   589  // findTypeSpec returns the ast.TypeSpec within the declaration that defines the symbol.
   590  // The name must match exactly.
   591  func (pkg *Package) findTypeSpec(decl *ast.GenDecl, symbol string) *ast.TypeSpec {
   592  	for _, spec := range decl.Specs {
   593  		typeSpec := spec.(*ast.TypeSpec) // Must succeed.
   594  		if symbol == typeSpec.Name.Name {
   595  			return typeSpec
   596  		}
   597  	}
   598  	return nil
   599  }
   600  
   601  // symbolDoc prints the docs for symbol. There may be multiple matches.
   602  // If symbol matches a type, output includes its methods factories and associated constants.
   603  // If there is no top-level symbol, symbolDoc looks for methods that match.
   604  func (pkg *Package) symbolDoc(symbol string) bool {
   605  	defer pkg.flush()
   606  	found := false
   607  	// Functions.
   608  	for _, fun := range pkg.findFuncs(symbol) {
   609  		if !found {
   610  			pkg.packageClause(true)
   611  		}
   612  		// Symbol is a function.
   613  		decl := fun.Decl
   614  		decl.Body = nil
   615  		pkg.emit(fun.Doc, decl)
   616  		found = true
   617  	}
   618  	// Constants and variables behave the same.
   619  	values := pkg.findValues(symbol, pkg.doc.Consts)
   620  	values = append(values, pkg.findValues(symbol, pkg.doc.Vars)...)
   621  	// A declaration like
   622  	//	const ( c = 1; C = 2 )
   623  	// could be printed twice if the -u flag is set, as it matches twice.
   624  	// So we remember which declarations we've printed to avoid duplication.
   625  	printed := make(map[*ast.GenDecl]bool)
   626  	for _, value := range values {
   627  		// Print each spec only if there is at least one exported symbol in it.
   628  		// (See issue 11008.)
   629  		// TODO: Should we elide unexported symbols from a single spec?
   630  		// It's an unlikely scenario, probably not worth the trouble.
   631  		// TODO: Would be nice if go/doc did this for us.
   632  		specs := make([]ast.Spec, 0, len(value.Decl.Specs))
   633  		var typ ast.Expr
   634  		for _, spec := range value.Decl.Specs {
   635  			vspec := spec.(*ast.ValueSpec)
   636  
   637  			// The type name may carry over from a previous specification in the
   638  			// case of constants and iota.
   639  			if vspec.Type != nil {
   640  				typ = vspec.Type
   641  			}
   642  
   643  			for _, ident := range vspec.Names {
   644  				if isExported(ident.Name) {
   645  					if vspec.Type == nil && vspec.Values == nil && typ != nil {
   646  						// This a standalone identifier, as in the case of iota usage.
   647  						// Thus, assume the type comes from the previous type.
   648  						vspec.Type = &ast.Ident{
   649  							Name:    pkg.oneLineNode(typ),
   650  							NamePos: vspec.End() - 1,
   651  						}
   652  					}
   653  
   654  					specs = append(specs, vspec)
   655  					typ = nil // Only inject type on first exported identifier
   656  					break
   657  				}
   658  			}
   659  		}
   660  		if len(specs) == 0 || printed[value.Decl] {
   661  			continue
   662  		}
   663  		value.Decl.Specs = specs
   664  		if !found {
   665  			pkg.packageClause(true)
   666  		}
   667  		pkg.emit(value.Doc, value.Decl)
   668  		printed[value.Decl] = true
   669  		found = true
   670  	}
   671  	// Types.
   672  	for _, typ := range pkg.findTypes(symbol) {
   673  		if !found {
   674  			pkg.packageClause(true)
   675  		}
   676  		decl := typ.Decl
   677  		spec := pkg.findTypeSpec(decl, typ.Name)
   678  		trimUnexportedElems(spec)
   679  		// If there are multiple types defined, reduce to just this one.
   680  		if len(decl.Specs) > 1 {
   681  			decl.Specs = []ast.Spec{spec}
   682  		}
   683  		pkg.emit(typ.Doc, decl)
   684  		// Show associated methods, constants, etc.
   685  		if len(typ.Consts) > 0 || len(typ.Vars) > 0 || len(typ.Funcs) > 0 || len(typ.Methods) > 0 {
   686  			pkg.Printf("\n")
   687  		}
   688  		pkg.valueSummary(typ.Consts, true)
   689  		pkg.valueSummary(typ.Vars, true)
   690  		pkg.funcSummary(typ.Funcs, true)
   691  		pkg.funcSummary(typ.Methods, true)
   692  		found = true
   693  	}
   694  	if !found {
   695  		// See if there are methods.
   696  		if !pkg.printMethodDoc("", symbol) {
   697  			return false
   698  		}
   699  	}
   700  	return true
   701  }
   702  
   703  // trimUnexportedElems modifies spec in place to elide unexported fields from
   704  // structs and methods from interfaces (unless the unexported flag is set).
   705  func trimUnexportedElems(spec *ast.TypeSpec) {
   706  	if unexported {
   707  		return
   708  	}
   709  	switch typ := spec.Type.(type) {
   710  	case *ast.StructType:
   711  		typ.Fields = trimUnexportedFields(typ.Fields, false)
   712  	case *ast.InterfaceType:
   713  		typ.Methods = trimUnexportedFields(typ.Methods, true)
   714  	}
   715  }
   716  
   717  // trimUnexportedFields returns the field list trimmed of unexported fields.
   718  func trimUnexportedFields(fields *ast.FieldList, isInterface bool) *ast.FieldList {
   719  	what := "methods"
   720  	if !isInterface {
   721  		what = "fields"
   722  	}
   723  
   724  	trimmed := false
   725  	list := make([]*ast.Field, 0, len(fields.List))
   726  	for _, field := range fields.List {
   727  		names := field.Names
   728  		if len(names) == 0 {
   729  			// Embedded type. Use the name of the type. It must be of the form ident or
   730  			// pkg.ident (for structs and interfaces), or *ident or *pkg.ident (structs only).
   731  			// Nothing else is allowed.
   732  			ty := field.Type
   733  			if se, ok := field.Type.(*ast.StarExpr); !isInterface && ok {
   734  				// The form *ident or *pkg.ident is only valid on
   735  				// embedded types in structs.
   736  				ty = se.X
   737  			}
   738  			switch ident := ty.(type) {
   739  			case *ast.Ident:
   740  				if isInterface && ident.Name == "error" && ident.Obj == nil {
   741  					// For documentation purposes, we consider the builtin error
   742  					// type special when embedded in an interface, such that it
   743  					// always gets shown publicly.
   744  					list = append(list, field)
   745  					continue
   746  				}
   747  				names = []*ast.Ident{ident}
   748  			case *ast.SelectorExpr:
   749  				// An embedded type may refer to a type in another package.
   750  				names = []*ast.Ident{ident.Sel}
   751  			}
   752  			if names == nil {
   753  				// Can only happen if AST is incorrect. Safe to continue with a nil list.
   754  				log.Print("invalid program: unexpected type for embedded field")
   755  			}
   756  		}
   757  		// Trims if any is unexported. Good enough in practice.
   758  		ok := true
   759  		for _, name := range names {
   760  			if !isExported(name.Name) {
   761  				trimmed = true
   762  				ok = false
   763  				break
   764  			}
   765  		}
   766  		if ok {
   767  			list = append(list, field)
   768  		}
   769  	}
   770  	if !trimmed {
   771  		return fields
   772  	}
   773  	unexportedField := &ast.Field{
   774  		Type: &ast.Ident{
   775  			// Hack: printer will treat this as a field with a named type.
   776  			// Setting Name and NamePos to ("", fields.Closing-1) ensures that
   777  			// when Pos and End are called on this field, they return the
   778  			// position right before closing '}' character.
   779  			Name:    "",
   780  			NamePos: fields.Closing - 1,
   781  		},
   782  		Comment: &ast.CommentGroup{
   783  			List: []*ast.Comment{{Text: fmt.Sprintf("// Has unexported %s.\n", what)}},
   784  		},
   785  	}
   786  	return &ast.FieldList{
   787  		Opening: fields.Opening,
   788  		List:    append(list, unexportedField),
   789  		Closing: fields.Closing,
   790  	}
   791  }
   792  
   793  // printMethodDoc prints the docs for matches of symbol.method.
   794  // If symbol is empty, it prints all methods that match the name.
   795  // It reports whether it found any methods.
   796  func (pkg *Package) printMethodDoc(symbol, method string) bool {
   797  	defer pkg.flush()
   798  	types := pkg.findTypes(symbol)
   799  	if types == nil {
   800  		if symbol == "" {
   801  			return false
   802  		}
   803  		pkg.Fatalf("symbol %s is not a type in package %s installed in %q", symbol, pkg.name, pkg.build.ImportPath)
   804  	}
   805  	found := false
   806  	for _, typ := range types {
   807  		if len(typ.Methods) > 0 {
   808  			for _, meth := range typ.Methods {
   809  				if match(method, meth.Name) {
   810  					decl := meth.Decl
   811  					decl.Body = nil
   812  					pkg.emit(meth.Doc, decl)
   813  					found = true
   814  				}
   815  			}
   816  			continue
   817  		}
   818  		// Type may be an interface. The go/doc package does not attach
   819  		// an interface's methods to the doc.Type. We need to dig around.
   820  		spec := pkg.findTypeSpec(typ.Decl, typ.Name)
   821  		inter, ok := spec.Type.(*ast.InterfaceType)
   822  		if !ok {
   823  			// Not an interface type.
   824  			continue
   825  		}
   826  		for _, iMethod := range inter.Methods.List {
   827  			// This is an interface, so there can be only one name.
   828  			// TODO: Anonymous methods (embedding)
   829  			if len(iMethod.Names) == 0 {
   830  				continue
   831  			}
   832  			name := iMethod.Names[0].Name
   833  			if match(method, name) {
   834  				if iMethod.Doc != nil {
   835  					for _, comment := range iMethod.Doc.List {
   836  						doc.ToText(&pkg.buf, comment.Text, "", indent, indentedWidth)
   837  					}
   838  				}
   839  				s := pkg.oneLineNode(iMethod.Type)
   840  				// Hack: s starts "func" but there is no name present.
   841  				// We could instead build a FuncDecl but it's not worthwhile.
   842  				lineComment := ""
   843  				if iMethod.Comment != nil {
   844  					lineComment = fmt.Sprintf("  %s", iMethod.Comment.List[0].Text)
   845  				}
   846  				pkg.Printf("func %s%s%s\n", name, s[4:], lineComment)
   847  				found = true
   848  			}
   849  		}
   850  	}
   851  	return found
   852  }
   853  
   854  // printFieldDoc prints the docs for matches of symbol.fieldName.
   855  // It reports whether it found any field.
   856  // Both symbol and fieldName must be non-empty or it returns false.
   857  func (pkg *Package) printFieldDoc(symbol, fieldName string) bool {
   858  	if symbol == "" || fieldName == "" {
   859  		return false
   860  	}
   861  	defer pkg.flush()
   862  	types := pkg.findTypes(symbol)
   863  	if types == nil {
   864  		pkg.Fatalf("symbol %s is not a type in package %s installed in %q", symbol, pkg.name, pkg.build.ImportPath)
   865  	}
   866  	found := false
   867  	numUnmatched := 0
   868  	for _, typ := range types {
   869  		// Type must be a struct.
   870  		spec := pkg.findTypeSpec(typ.Decl, typ.Name)
   871  		structType, ok := spec.Type.(*ast.StructType)
   872  		if !ok {
   873  			// Not a struct type.
   874  			continue
   875  		}
   876  		for _, field := range structType.Fields.List {
   877  			// TODO: Anonymous fields.
   878  			for _, name := range field.Names {
   879  				if !match(fieldName, name.Name) {
   880  					numUnmatched++
   881  					continue
   882  				}
   883  				if !found {
   884  					pkg.Printf("type %s struct {\n", typ.Name)
   885  				}
   886  				if field.Doc != nil {
   887  					for _, comment := range field.Doc.List {
   888  						doc.ToText(&pkg.buf, comment.Text, indent, indent, indentedWidth)
   889  					}
   890  				}
   891  				s := pkg.oneLineNode(field.Type)
   892  				lineComment := ""
   893  				if field.Comment != nil {
   894  					lineComment = fmt.Sprintf("  %s", field.Comment.List[0].Text)
   895  				}
   896  				pkg.Printf("%s%s %s%s\n", indent, name, s, lineComment)
   897  				found = true
   898  			}
   899  		}
   900  	}
   901  	if found {
   902  		if numUnmatched > 0 {
   903  			pkg.Printf("\n    // ... other fields elided ...\n")
   904  		}
   905  		pkg.Printf("}\n")
   906  	}
   907  	return found
   908  }
   909  
   910  // methodDoc prints the docs for matches of symbol.method.
   911  func (pkg *Package) methodDoc(symbol, method string) bool {
   912  	defer pkg.flush()
   913  	return pkg.printMethodDoc(symbol, method)
   914  }
   915  
   916  // fieldDoc prints the docs for matches of symbol.field.
   917  func (pkg *Package) fieldDoc(symbol, field string) bool {
   918  	defer pkg.flush()
   919  	return pkg.printFieldDoc(symbol, field)
   920  }
   921  
   922  // match reports whether the user's symbol matches the program's.
   923  // A lower-case character in the user's string matches either case in the program's.
   924  // The program string must be exported.
   925  func match(user, program string) bool {
   926  	if !isExported(program) {
   927  		return false
   928  	}
   929  	if matchCase {
   930  		return user == program
   931  	}
   932  	for _, u := range user {
   933  		p, w := utf8.DecodeRuneInString(program)
   934  		program = program[w:]
   935  		if u == p {
   936  			continue
   937  		}
   938  		if unicode.IsLower(u) && simpleFold(u) == simpleFold(p) {
   939  			continue
   940  		}
   941  		return false
   942  	}
   943  	return program == ""
   944  }
   945  
   946  // simpleFold returns the minimum rune equivalent to r
   947  // under Unicode-defined simple case folding.
   948  func simpleFold(r rune) rune {
   949  	for {
   950  		r1 := unicode.SimpleFold(r)
   951  		if r1 <= r {
   952  			return r1 // wrapped around, found min
   953  		}
   954  		r = r1
   955  	}
   956  }
   957  

View as plain text