github.com/zxy12/golang151_with_comment@v0.0.0-20190507085033-721809559d3c/cmd/doc/pkg.go (about)

     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  	"unicode"
    20  	"unicode/utf8"
    21  )
    22  
    23  const (
    24  	punchedCardWidth = 80 // These things just won't leave us alone.
    25  	indentedWidth    = punchedCardWidth - len(indent)
    26  	indent           = "    "
    27  )
    28  
    29  type Package struct {
    30  	writer     io.Writer // Destination for output.
    31  	name       string    // Package name, json for encoding/json.
    32  	userPath   string    // String the user used to find this package.
    33  	unexported bool
    34  	matchCase  bool
    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  // pkg.Fatalf is like log.Fatalf, but panics so it can be recovered in the
    50  // main do function, so it doesn't cause an exit. Allows testing to work
    51  // without running a subprocess. The log prefix will be added when
    52  // logged in main; it is not added here.
    53  func (pkg *Package) Fatalf(format string, args ...interface{}) {
    54  	panic(PackageError(fmt.Sprintf(format, args...)))
    55  }
    56  
    57  // parsePackage turns the build package we found into a parsed package
    58  // we can then use to generate documentation.
    59  func parsePackage(writer io.Writer, pkg *build.Package, userPath string) *Package {
    60  	fs := token.NewFileSet()
    61  	// include tells parser.ParseDir which files to include.
    62  	// That means the file must be in the build package's GoFiles or CgoFiles
    63  	// list only (no tag-ignored files, tests, swig or other non-Go files).
    64  	include := func(info os.FileInfo) bool {
    65  		for _, name := range pkg.GoFiles {
    66  			if name == info.Name() {
    67  				return true
    68  			}
    69  		}
    70  		for _, name := range pkg.CgoFiles {
    71  			if name == info.Name() {
    72  				return true
    73  			}
    74  		}
    75  		return false
    76  	}
    77  	pkgs, err := parser.ParseDir(fs, pkg.Dir, include, parser.ParseComments)
    78  	if err != nil {
    79  		log.Fatal(err)
    80  	}
    81  	// Make sure they are all in one package.
    82  	if len(pkgs) != 1 {
    83  		log.Fatalf("multiple packages in directory %s", pkg.Dir)
    84  	}
    85  	astPkg := pkgs[pkg.Name]
    86  
    87  	// TODO: go/doc does not include typed constants in the constants
    88  	// list, which is what we want. For instance, time.Sunday is of type
    89  	// time.Weekday, so it is defined in the type but not in the
    90  	// Consts list for the package. This prevents
    91  	//	go doc time.Sunday
    92  	// from finding the symbol. Work around this for now, but we
    93  	// should fix it in go/doc.
    94  	// A similar story applies to factory functions.
    95  	docPkg := doc.New(astPkg, pkg.ImportPath, doc.AllDecls)
    96  	for _, typ := range docPkg.Types {
    97  		docPkg.Consts = append(docPkg.Consts, typ.Consts...)
    98  		docPkg.Vars = append(docPkg.Vars, typ.Vars...)
    99  		docPkg.Funcs = append(docPkg.Funcs, typ.Funcs...)
   100  	}
   101  
   102  	return &Package{
   103  		writer:   writer,
   104  		name:     pkg.Name,
   105  		userPath: userPath,
   106  		pkg:      astPkg,
   107  		file:     ast.MergePackageFiles(astPkg, 0),
   108  		doc:      docPkg,
   109  		build:    pkg,
   110  		fs:       fs,
   111  	}
   112  }
   113  
   114  func (pkg *Package) Printf(format string, args ...interface{}) {
   115  	fmt.Fprintf(&pkg.buf, format, args...)
   116  }
   117  
   118  func (pkg *Package) flush() {
   119  	_, err := pkg.writer.Write(pkg.buf.Bytes())
   120  	if err != nil {
   121  		log.Fatal(err)
   122  	}
   123  	pkg.buf.Reset() // Not needed, but it's a flush.
   124  }
   125  
   126  var newlineBytes = []byte("\n\n") // We never ask for more than 2.
   127  
   128  // newlines guarantees there are n newlines at the end of the buffer.
   129  func (pkg *Package) newlines(n int) {
   130  	for !bytes.HasSuffix(pkg.buf.Bytes(), newlineBytes[:n]) {
   131  		pkg.buf.WriteRune('\n')
   132  	}
   133  }
   134  
   135  // emit prints the node.
   136  func (pkg *Package) emit(comment string, node ast.Node) {
   137  	if node != nil {
   138  		err := format.Node(&pkg.buf, pkg.fs, node)
   139  		if err != nil {
   140  			log.Fatal(err)
   141  		}
   142  		if comment != "" {
   143  			pkg.newlines(2) // Guarantee blank line before comment.
   144  			doc.ToText(&pkg.buf, comment, "    ", indent, indentedWidth)
   145  		}
   146  		pkg.newlines(1)
   147  	}
   148  }
   149  
   150  var formatBuf bytes.Buffer // Reusable to avoid allocation.
   151  
   152  // formatNode is a helper function for printing.
   153  func (pkg *Package) formatNode(node ast.Node) []byte {
   154  	formatBuf.Reset()
   155  	format.Node(&formatBuf, pkg.fs, node)
   156  	return formatBuf.Bytes()
   157  }
   158  
   159  // oneLineFunc prints a function declaration as a single line.
   160  func (pkg *Package) oneLineFunc(decl *ast.FuncDecl) {
   161  	decl.Doc = nil
   162  	decl.Body = nil
   163  	pkg.emit("", decl)
   164  }
   165  
   166  // oneLineValueGenDecl prints a var or const declaration as a single line.
   167  func (pkg *Package) oneLineValueGenDecl(decl *ast.GenDecl) {
   168  	decl.Doc = nil
   169  	dotDotDot := ""
   170  	if len(decl.Specs) > 1 {
   171  		dotDotDot = " ..."
   172  	}
   173  	// Find the first relevant spec.
   174  	for i, spec := range decl.Specs {
   175  		valueSpec := spec.(*ast.ValueSpec) // Must succeed; we can't mix types in one genDecl.
   176  		if !isExported(valueSpec.Names[0].Name) {
   177  			continue
   178  		}
   179  		typ := ""
   180  		if valueSpec.Type != nil {
   181  			typ = fmt.Sprintf(" %s", pkg.formatNode(valueSpec.Type))
   182  		}
   183  		val := ""
   184  		if i < len(valueSpec.Values) && valueSpec.Values[i] != nil {
   185  			val = fmt.Sprintf(" = %s", pkg.formatNode(valueSpec.Values[i]))
   186  		}
   187  		pkg.Printf("%s %s%s%s%s\n", decl.Tok, valueSpec.Names[0], typ, val, dotDotDot)
   188  		break
   189  	}
   190  }
   191  
   192  // oneLineTypeDecl prints a type declaration as a single line.
   193  func (pkg *Package) oneLineTypeDecl(spec *ast.TypeSpec) {
   194  	spec.Doc = nil
   195  	spec.Comment = nil
   196  	switch spec.Type.(type) {
   197  	case *ast.InterfaceType:
   198  		pkg.Printf("type %s interface { ... }\n", spec.Name)
   199  	case *ast.StructType:
   200  		pkg.Printf("type %s struct { ... }\n", spec.Name)
   201  	default:
   202  		pkg.Printf("type %s %s\n", spec.Name, pkg.formatNode(spec.Type))
   203  	}
   204  }
   205  
   206  // packageDoc prints the docs for the package (package doc plus one-liners of the rest).
   207  func (pkg *Package) packageDoc() {
   208  	defer pkg.flush()
   209  	if pkg.showInternals() {
   210  		pkg.packageClause(false)
   211  	}
   212  
   213  	doc.ToText(&pkg.buf, pkg.doc.Doc, "", indent, indentedWidth)
   214  	pkg.newlines(1)
   215  
   216  	if !pkg.showInternals() {
   217  		// Show only package docs for commands.
   218  		return
   219  	}
   220  
   221  	pkg.newlines(1)
   222  	pkg.valueSummary(pkg.doc.Consts)
   223  	pkg.valueSummary(pkg.doc.Vars)
   224  	pkg.funcSummary(pkg.doc.Funcs)
   225  	pkg.typeSummary()
   226  	pkg.bugs()
   227  }
   228  
   229  // showInternals reports whether we should show the internals
   230  // of a package as opposed to just the package docs.
   231  // Used to decide whether to suppress internals for commands.
   232  // Called only by Package.packageDoc.
   233  func (pkg *Package) showInternals() bool {
   234  	return pkg.pkg.Name != "main" || showCmd
   235  }
   236  
   237  // packageClause prints the package clause.
   238  // The argument boolean, if true, suppresses the output if the
   239  // user's argument is identical to the actual package path or
   240  // is empty, meaning it's the current directory.
   241  func (pkg *Package) packageClause(checkUserPath bool) {
   242  	if checkUserPath {
   243  		if pkg.userPath == "" || pkg.userPath == pkg.build.ImportPath {
   244  			return
   245  		}
   246  	}
   247  	importPath := pkg.build.ImportComment
   248  	if importPath == "" {
   249  		importPath = pkg.build.ImportPath
   250  	}
   251  	pkg.Printf("package %s // import %q\n\n", pkg.name, importPath)
   252  	if importPath != pkg.build.ImportPath {
   253  		pkg.Printf("WARNING: package source is installed in %q\n", pkg.build.ImportPath)
   254  	}
   255  }
   256  
   257  // valueSummary prints a one-line summary for each set of values and constants.
   258  func (pkg *Package) valueSummary(values []*doc.Value) {
   259  	for _, value := range values {
   260  		// Only print first item in spec, show ... to stand for the rest.
   261  		spec := value.Decl.Specs[0].(*ast.ValueSpec) // Must succeed.
   262  		exported := true
   263  		for _, name := range spec.Names {
   264  			if !isExported(name.Name) {
   265  				exported = false
   266  				break
   267  			}
   268  		}
   269  		if exported {
   270  			pkg.oneLineValueGenDecl(value.Decl)
   271  		}
   272  	}
   273  }
   274  
   275  // funcSummary prints a one-line summary for each function.
   276  func (pkg *Package) funcSummary(funcs []*doc.Func) {
   277  	for _, fun := range funcs {
   278  		decl := fun.Decl
   279  		// Exported functions only. The go/doc package does not include methods here.
   280  		if isExported(fun.Name) {
   281  			pkg.oneLineFunc(decl)
   282  		}
   283  	}
   284  }
   285  
   286  // typeSummary prints a one-line summary for each type.
   287  func (pkg *Package) typeSummary() {
   288  	for _, typ := range pkg.doc.Types {
   289  		for _, spec := range typ.Decl.Specs {
   290  			typeSpec := spec.(*ast.TypeSpec) // Must succeed.
   291  			if isExported(typeSpec.Name.Name) {
   292  				pkg.oneLineTypeDecl(typeSpec)
   293  			}
   294  		}
   295  	}
   296  }
   297  
   298  // bugs prints the BUGS information for the package.
   299  // TODO: Provide access to TODOs and NOTEs as well (very noisy so off by default)?
   300  func (pkg *Package) bugs() {
   301  	if pkg.doc.Notes["BUG"] == nil {
   302  		return
   303  	}
   304  	pkg.Printf("\n")
   305  	for _, note := range pkg.doc.Notes["BUG"] {
   306  		pkg.Printf("%s: %v\n", "BUG", note.Body)
   307  	}
   308  }
   309  
   310  // findValues finds the doc.Values that describe the symbol.
   311  func (pkg *Package) findValues(symbol string, docValues []*doc.Value) (values []*doc.Value) {
   312  	for _, value := range docValues {
   313  		for _, name := range value.Names {
   314  			if match(symbol, name) {
   315  				values = append(values, value)
   316  			}
   317  		}
   318  	}
   319  	return
   320  }
   321  
   322  // findFuncs finds the doc.Funcs that describes the symbol.
   323  func (pkg *Package) findFuncs(symbol string) (funcs []*doc.Func) {
   324  	for _, fun := range pkg.doc.Funcs {
   325  		if match(symbol, fun.Name) {
   326  			funcs = append(funcs, fun)
   327  		}
   328  	}
   329  	return
   330  }
   331  
   332  // findTypes finds the doc.Types that describes the symbol.
   333  // If symbol is empty, it finds all exported types.
   334  func (pkg *Package) findTypes(symbol string) (types []*doc.Type) {
   335  	for _, typ := range pkg.doc.Types {
   336  		if symbol == "" && isExported(typ.Name) || match(symbol, typ.Name) {
   337  			types = append(types, typ)
   338  		}
   339  	}
   340  	return
   341  }
   342  
   343  // findTypeSpec returns the ast.TypeSpec within the declaration that defines the symbol.
   344  // The name must match exactly.
   345  func (pkg *Package) findTypeSpec(decl *ast.GenDecl, symbol string) *ast.TypeSpec {
   346  	for _, spec := range decl.Specs {
   347  		typeSpec := spec.(*ast.TypeSpec) // Must succeed.
   348  		if symbol == typeSpec.Name.Name {
   349  			return typeSpec
   350  		}
   351  	}
   352  	return nil
   353  }
   354  
   355  // symbolDoc prints the docs for symbol. There may be multiple matches.
   356  // If symbol matches a type, output includes its methods factories and associated constants.
   357  // If there is no top-level symbol, symbolDoc looks for methods that match.
   358  func (pkg *Package) symbolDoc(symbol string) {
   359  	defer pkg.flush()
   360  	found := false
   361  	// Functions.
   362  	for _, fun := range pkg.findFuncs(symbol) {
   363  		if !found {
   364  			pkg.packageClause(true)
   365  		}
   366  		// Symbol is a function.
   367  		decl := fun.Decl
   368  		decl.Body = nil
   369  		pkg.emit(fun.Doc, decl)
   370  		found = true
   371  	}
   372  	// Constants and variables behave the same.
   373  	values := pkg.findValues(symbol, pkg.doc.Consts)
   374  	values = append(values, pkg.findValues(symbol, pkg.doc.Vars)...)
   375  	for _, value := range values {
   376  		// Print each spec only if there is at least one exported symbol in it.
   377  		// (See issue 11008.)
   378  		// TODO: Should we elide unexported symbols from a single spec?
   379  		// It's an unlikely scenario, probably not worth the trouble.
   380  		// TODO: Would be nice if go/doc did this for us.
   381  		specs := make([]ast.Spec, 0, len(value.Decl.Specs))
   382  		for _, spec := range value.Decl.Specs {
   383  			vspec := spec.(*ast.ValueSpec)
   384  			for _, ident := range vspec.Names {
   385  				if isExported(ident.Name) {
   386  					specs = append(specs, vspec)
   387  					break
   388  				}
   389  			}
   390  		}
   391  		if len(specs) == 0 {
   392  			continue
   393  		}
   394  		value.Decl.Specs = specs
   395  		if !found {
   396  			pkg.packageClause(true)
   397  		}
   398  		pkg.emit(value.Doc, value.Decl)
   399  		found = true
   400  	}
   401  	// Types.
   402  	for _, typ := range pkg.findTypes(symbol) {
   403  		if !found {
   404  			pkg.packageClause(true)
   405  		}
   406  		decl := typ.Decl
   407  		spec := pkg.findTypeSpec(decl, typ.Name)
   408  		trimUnexportedElems(spec)
   409  		// If there are multiple types defined, reduce to just this one.
   410  		if len(decl.Specs) > 1 {
   411  			decl.Specs = []ast.Spec{spec}
   412  		}
   413  		pkg.emit(typ.Doc, decl)
   414  		// Show associated methods, constants, etc.
   415  		if len(typ.Consts) > 0 || len(typ.Vars) > 0 || len(typ.Funcs) > 0 || len(typ.Methods) > 0 {
   416  			pkg.Printf("\n")
   417  		}
   418  		pkg.valueSummary(typ.Consts)
   419  		pkg.valueSummary(typ.Vars)
   420  		pkg.funcSummary(typ.Funcs)
   421  		pkg.funcSummary(typ.Methods)
   422  		found = true
   423  	}
   424  	if !found {
   425  		// See if there are methods.
   426  		if !pkg.printMethodDoc("", symbol) {
   427  			log.Printf("symbol %s not present in package %s installed in %q", symbol, pkg.name, pkg.build.ImportPath)
   428  		}
   429  	}
   430  }
   431  
   432  // trimUnexportedElems modifies spec in place to elide unexported fields from
   433  // structs and methods from interfaces (unless the unexported flag is set).
   434  func trimUnexportedElems(spec *ast.TypeSpec) {
   435  	if unexported {
   436  		return
   437  	}
   438  	switch typ := spec.Type.(type) {
   439  	case *ast.StructType:
   440  		typ.Fields = trimUnexportedFields(typ.Fields, "fields")
   441  	case *ast.InterfaceType:
   442  		typ.Methods = trimUnexportedFields(typ.Methods, "methods")
   443  	}
   444  }
   445  
   446  // trimUnexportedFields returns the field list trimmed of unexported fields.
   447  func trimUnexportedFields(fields *ast.FieldList, what string) *ast.FieldList {
   448  	trimmed := false
   449  	list := make([]*ast.Field, 0, len(fields.List))
   450  	for _, field := range fields.List {
   451  		// Trims if any is unexported. Good enough in practice.
   452  		ok := true
   453  		for _, name := range field.Names {
   454  			if !isExported(name.Name) {
   455  				trimmed = true
   456  				ok = false
   457  				break
   458  			}
   459  		}
   460  		if ok {
   461  			list = append(list, field)
   462  		}
   463  	}
   464  	if !trimmed {
   465  		return fields
   466  	}
   467  	unexportedField := &ast.Field{
   468  		Type: ast.NewIdent(""), // Hack: printer will treat this as a field with a named type.
   469  		Comment: &ast.CommentGroup{
   470  			List: []*ast.Comment{
   471  				&ast.Comment{
   472  					Text: fmt.Sprintf("// Has unexported %s.\n", what),
   473  				},
   474  			},
   475  		},
   476  	}
   477  	return &ast.FieldList{
   478  		Opening: fields.Opening,
   479  		List:    append(list, unexportedField),
   480  		Closing: fields.Closing,
   481  	}
   482  }
   483  
   484  // printMethodDoc prints the docs for matches of symbol.method.
   485  // If symbol is empty, it prints all methods that match the name.
   486  // It reports whether it found any methods.
   487  func (pkg *Package) printMethodDoc(symbol, method string) bool {
   488  	defer pkg.flush()
   489  	types := pkg.findTypes(symbol)
   490  	if types == nil {
   491  		if symbol == "" {
   492  			return false
   493  		}
   494  		pkg.Fatalf("symbol %s is not a type in package %s installed in %q", symbol, pkg.name, pkg.build.ImportPath)
   495  	}
   496  	found := false
   497  	for _, typ := range types {
   498  		for _, meth := range typ.Methods {
   499  			if match(method, meth.Name) {
   500  				decl := meth.Decl
   501  				decl.Body = nil
   502  				pkg.emit(meth.Doc, decl)
   503  				found = true
   504  			}
   505  		}
   506  	}
   507  	return found
   508  }
   509  
   510  // methodDoc prints the docs for matches of symbol.method.
   511  func (pkg *Package) methodDoc(symbol, method string) {
   512  	defer pkg.flush()
   513  	if !pkg.printMethodDoc(symbol, method) {
   514  		pkg.Fatalf("no method %s.%s in package %s installed in %q", symbol, method, pkg.name, pkg.build.ImportPath)
   515  	}
   516  }
   517  
   518  // match reports whether the user's symbol matches the program's.
   519  // A lower-case character in the user's string matches either case in the program's.
   520  // The program string must be exported.
   521  func match(user, program string) bool {
   522  	if !isExported(program) {
   523  		return false
   524  	}
   525  	if matchCase {
   526  		return user == program
   527  	}
   528  	for _, u := range user {
   529  		p, w := utf8.DecodeRuneInString(program)
   530  		program = program[w:]
   531  		if u == p {
   532  			continue
   533  		}
   534  		if unicode.IsLower(u) && simpleFold(u) == simpleFold(p) {
   535  			continue
   536  		}
   537  		return false
   538  	}
   539  	return program == ""
   540  }
   541  
   542  // simpleFold returns the minimum rune equivalent to r
   543  // under Unicode-defined simple case folding.
   544  func simpleFold(r rune) rune {
   545  	for {
   546  		r1 := unicode.SimpleFold(r)
   547  		if r1 <= r {
   548  			return r1 // wrapped around, found min
   549  		}
   550  		r = r1
   551  	}
   552  }