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