github.com/yukk001/go1.10.8@v0.0.0-20190813125351-6df2d3982e20/src/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 "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(build.Default.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 importPath := pkg.build.ImportComment 429 if importPath == "" { 430 importPath = pkg.build.ImportPath 431 } 432 pkg.Printf("package %s // import %q\n\n", pkg.name, importPath) 433 if importPath != pkg.build.ImportPath { 434 pkg.Printf("WARNING: package source is installed in %q\n", pkg.build.ImportPath) 435 } 436 } 437 438 // valueSummary prints a one-line summary for each set of values and constants. 439 // If all the types in a constant or variable declaration belong to the same 440 // type they can be printed by typeSummary, and so can be suppressed here. 441 func (pkg *Package) valueSummary(values []*doc.Value, showGrouped bool) { 442 var isGrouped map[*doc.Value]bool 443 if !showGrouped { 444 isGrouped = make(map[*doc.Value]bool) 445 for _, typ := range pkg.doc.Types { 446 if !isExported(typ.Name) { 447 continue 448 } 449 for _, c := range typ.Consts { 450 isGrouped[c] = true 451 } 452 for _, v := range typ.Vars { 453 isGrouped[v] = true 454 } 455 } 456 } 457 458 for _, value := range values { 459 if !isGrouped[value] { 460 if decl := pkg.oneLineNode(value.Decl); decl != "" { 461 pkg.Printf("%s\n", decl) 462 } 463 } 464 } 465 } 466 467 // funcSummary prints a one-line summary for each function. Constructors 468 // are printed by typeSummary, below, and so can be suppressed here. 469 func (pkg *Package) funcSummary(funcs []*doc.Func, showConstructors bool) { 470 // First, identify the constructors. Don't bother figuring out if they're exported. 471 var isConstructor map[*doc.Func]bool 472 if !showConstructors { 473 isConstructor = make(map[*doc.Func]bool) 474 for _, typ := range pkg.doc.Types { 475 if isExported(typ.Name) { 476 for _, f := range typ.Funcs { 477 isConstructor[f] = true 478 } 479 } 480 } 481 } 482 for _, fun := range funcs { 483 // Exported functions only. The go/doc package does not include methods here. 484 if isExported(fun.Name) { 485 if !isConstructor[fun] { 486 pkg.Printf("%s\n", pkg.oneLineNode(fun.Decl)) 487 } 488 } 489 } 490 } 491 492 // typeSummary prints a one-line summary for each type, followed by its constructors. 493 func (pkg *Package) typeSummary() { 494 for _, typ := range pkg.doc.Types { 495 for _, spec := range typ.Decl.Specs { 496 typeSpec := spec.(*ast.TypeSpec) // Must succeed. 497 if isExported(typeSpec.Name.Name) { 498 pkg.Printf("%s\n", pkg.oneLineNode(typeSpec)) 499 // Now print the consts, vars, and constructors. 500 for _, c := range typ.Consts { 501 if decl := pkg.oneLineNode(c.Decl); decl != "" { 502 pkg.Printf(indent+"%s\n", decl) 503 } 504 } 505 for _, v := range typ.Vars { 506 if decl := pkg.oneLineNode(v.Decl); decl != "" { 507 pkg.Printf(indent+"%s\n", decl) 508 } 509 } 510 for _, constructor := range typ.Funcs { 511 if isExported(constructor.Name) { 512 pkg.Printf(indent+"%s\n", pkg.oneLineNode(constructor.Decl)) 513 } 514 } 515 } 516 } 517 } 518 } 519 520 // bugs prints the BUGS information for the package. 521 // TODO: Provide access to TODOs and NOTEs as well (very noisy so off by default)? 522 func (pkg *Package) bugs() { 523 if pkg.doc.Notes["BUG"] == nil { 524 return 525 } 526 pkg.Printf("\n") 527 for _, note := range pkg.doc.Notes["BUG"] { 528 pkg.Printf("%s: %v\n", "BUG", note.Body) 529 } 530 } 531 532 // findValues finds the doc.Values that describe the symbol. 533 func (pkg *Package) findValues(symbol string, docValues []*doc.Value) (values []*doc.Value) { 534 for _, value := range docValues { 535 for _, name := range value.Names { 536 if match(symbol, name) { 537 values = append(values, value) 538 } 539 } 540 } 541 return 542 } 543 544 // findFuncs finds the doc.Funcs that describes the symbol. 545 func (pkg *Package) findFuncs(symbol string) (funcs []*doc.Func) { 546 for _, fun := range pkg.doc.Funcs { 547 if match(symbol, fun.Name) { 548 funcs = append(funcs, fun) 549 } 550 } 551 return 552 } 553 554 // findTypes finds the doc.Types that describes the symbol. 555 // If symbol is empty, it finds all exported types. 556 func (pkg *Package) findTypes(symbol string) (types []*doc.Type) { 557 for _, typ := range pkg.doc.Types { 558 if symbol == "" && isExported(typ.Name) || match(symbol, typ.Name) { 559 types = append(types, typ) 560 } 561 } 562 return 563 } 564 565 // findTypeSpec returns the ast.TypeSpec within the declaration that defines the symbol. 566 // The name must match exactly. 567 func (pkg *Package) findTypeSpec(decl *ast.GenDecl, symbol string) *ast.TypeSpec { 568 for _, spec := range decl.Specs { 569 typeSpec := spec.(*ast.TypeSpec) // Must succeed. 570 if symbol == typeSpec.Name.Name { 571 return typeSpec 572 } 573 } 574 return nil 575 } 576 577 // symbolDoc prints the docs for symbol. There may be multiple matches. 578 // If symbol matches a type, output includes its methods factories and associated constants. 579 // If there is no top-level symbol, symbolDoc looks for methods that match. 580 func (pkg *Package) symbolDoc(symbol string) bool { 581 defer pkg.flush() 582 found := false 583 // Functions. 584 for _, fun := range pkg.findFuncs(symbol) { 585 if !found { 586 pkg.packageClause(true) 587 } 588 // Symbol is a function. 589 decl := fun.Decl 590 decl.Body = nil 591 pkg.emit(fun.Doc, decl) 592 found = true 593 } 594 // Constants and variables behave the same. 595 values := pkg.findValues(symbol, pkg.doc.Consts) 596 values = append(values, pkg.findValues(symbol, pkg.doc.Vars)...) 597 // A declaration like 598 // const ( c = 1; C = 2 ) 599 // could be printed twice if the -u flag is set, as it matches twice. 600 // So we remember which declarations we've printed to avoid duplication. 601 printed := make(map[*ast.GenDecl]bool) 602 for _, value := range values { 603 // Print each spec only if there is at least one exported symbol in it. 604 // (See issue 11008.) 605 // TODO: Should we elide unexported symbols from a single spec? 606 // It's an unlikely scenario, probably not worth the trouble. 607 // TODO: Would be nice if go/doc did this for us. 608 specs := make([]ast.Spec, 0, len(value.Decl.Specs)) 609 var typ ast.Expr 610 for _, spec := range value.Decl.Specs { 611 vspec := spec.(*ast.ValueSpec) 612 613 // The type name may carry over from a previous specification in the 614 // case of constants and iota. 615 if vspec.Type != nil { 616 typ = vspec.Type 617 } 618 619 for _, ident := range vspec.Names { 620 if isExported(ident.Name) { 621 if vspec.Type == nil && vspec.Values == nil && typ != nil { 622 // This a standalone identifier, as in the case of iota usage. 623 // Thus, assume the type comes from the previous type. 624 vspec.Type = &ast.Ident{ 625 Name: string(pkg.oneLineNode(typ)), 626 NamePos: vspec.End() - 1, 627 } 628 } 629 630 specs = append(specs, vspec) 631 typ = nil // Only inject type on first exported identifier 632 break 633 } 634 } 635 } 636 if len(specs) == 0 || printed[value.Decl] { 637 continue 638 } 639 value.Decl.Specs = specs 640 if !found { 641 pkg.packageClause(true) 642 } 643 pkg.emit(value.Doc, value.Decl) 644 printed[value.Decl] = true 645 found = true 646 } 647 // Types. 648 for _, typ := range pkg.findTypes(symbol) { 649 if !found { 650 pkg.packageClause(true) 651 } 652 decl := typ.Decl 653 spec := pkg.findTypeSpec(decl, typ.Name) 654 trimUnexportedElems(spec) 655 // If there are multiple types defined, reduce to just this one. 656 if len(decl.Specs) > 1 { 657 decl.Specs = []ast.Spec{spec} 658 } 659 pkg.emit(typ.Doc, decl) 660 // Show associated methods, constants, etc. 661 if len(typ.Consts) > 0 || len(typ.Vars) > 0 || len(typ.Funcs) > 0 || len(typ.Methods) > 0 { 662 pkg.Printf("\n") 663 } 664 pkg.valueSummary(typ.Consts, true) 665 pkg.valueSummary(typ.Vars, true) 666 pkg.funcSummary(typ.Funcs, true) 667 pkg.funcSummary(typ.Methods, true) 668 found = true 669 } 670 if !found { 671 // See if there are methods. 672 if !pkg.printMethodDoc("", symbol) { 673 return false 674 } 675 } 676 return true 677 } 678 679 // trimUnexportedElems modifies spec in place to elide unexported fields from 680 // structs and methods from interfaces (unless the unexported flag is set). 681 func trimUnexportedElems(spec *ast.TypeSpec) { 682 if unexported { 683 return 684 } 685 switch typ := spec.Type.(type) { 686 case *ast.StructType: 687 typ.Fields = trimUnexportedFields(typ.Fields, false) 688 case *ast.InterfaceType: 689 typ.Methods = trimUnexportedFields(typ.Methods, true) 690 } 691 } 692 693 // trimUnexportedFields returns the field list trimmed of unexported fields. 694 func trimUnexportedFields(fields *ast.FieldList, isInterface bool) *ast.FieldList { 695 what := "methods" 696 if !isInterface { 697 what = "fields" 698 } 699 700 trimmed := false 701 list := make([]*ast.Field, 0, len(fields.List)) 702 for _, field := range fields.List { 703 names := field.Names 704 if len(names) == 0 { 705 // Embedded type. Use the name of the type. It must be of the form ident or 706 // pkg.ident (for structs and interfaces), or *ident or *pkg.ident (structs only). 707 // Nothing else is allowed. 708 ty := field.Type 709 if se, ok := field.Type.(*ast.StarExpr); !isInterface && ok { 710 // The form *ident or *pkg.ident is only valid on 711 // embedded types in structs. 712 ty = se.X 713 } 714 switch ident := ty.(type) { 715 case *ast.Ident: 716 if isInterface && ident.Name == "error" && ident.Obj == nil { 717 // For documentation purposes, we consider the builtin error 718 // type special when embedded in an interface, such that it 719 // always gets shown publicly. 720 list = append(list, field) 721 continue 722 } 723 names = []*ast.Ident{ident} 724 case *ast.SelectorExpr: 725 // An embedded type may refer to a type in another package. 726 names = []*ast.Ident{ident.Sel} 727 } 728 if names == nil { 729 // Can only happen if AST is incorrect. Safe to continue with a nil list. 730 log.Print("invalid program: unexpected type for embedded field") 731 } 732 } 733 // Trims if any is unexported. Good enough in practice. 734 ok := true 735 for _, name := range names { 736 if !isExported(name.Name) { 737 trimmed = true 738 ok = false 739 break 740 } 741 } 742 if ok { 743 list = append(list, field) 744 } 745 } 746 if !trimmed { 747 return fields 748 } 749 unexportedField := &ast.Field{ 750 Type: &ast.Ident{ 751 // Hack: printer will treat this as a field with a named type. 752 // Setting Name and NamePos to ("", fields.Closing-1) ensures that 753 // when Pos and End are called on this field, they return the 754 // position right before closing '}' character. 755 Name: "", 756 NamePos: fields.Closing - 1, 757 }, 758 Comment: &ast.CommentGroup{ 759 List: []*ast.Comment{{Text: fmt.Sprintf("// Has unexported %s.\n", what)}}, 760 }, 761 } 762 return &ast.FieldList{ 763 Opening: fields.Opening, 764 List: append(list, unexportedField), 765 Closing: fields.Closing, 766 } 767 } 768 769 // printMethodDoc prints the docs for matches of symbol.method. 770 // If symbol is empty, it prints all methods that match the name. 771 // It reports whether it found any methods. 772 func (pkg *Package) printMethodDoc(symbol, method string) bool { 773 defer pkg.flush() 774 types := pkg.findTypes(symbol) 775 if types == nil { 776 if symbol == "" { 777 return false 778 } 779 pkg.Fatalf("symbol %s is not a type in package %s installed in %q", symbol, pkg.name, pkg.build.ImportPath) 780 } 781 found := false 782 for _, typ := range types { 783 if len(typ.Methods) > 0 { 784 for _, meth := range typ.Methods { 785 if match(method, meth.Name) { 786 decl := meth.Decl 787 decl.Body = nil 788 pkg.emit(meth.Doc, decl) 789 found = true 790 } 791 } 792 continue 793 } 794 // Type may be an interface. The go/doc package does not attach 795 // an interface's methods to the doc.Type. We need to dig around. 796 spec := pkg.findTypeSpec(typ.Decl, typ.Name) 797 inter, ok := spec.Type.(*ast.InterfaceType) 798 if !ok { 799 // Not an interface type. 800 continue 801 } 802 for _, iMethod := range inter.Methods.List { 803 // This is an interface, so there can be only one name. 804 // TODO: Anonymous methods (embedding) 805 if len(iMethod.Names) == 0 { 806 continue 807 } 808 name := iMethod.Names[0].Name 809 if match(method, name) { 810 if iMethod.Doc != nil { 811 for _, comment := range iMethod.Doc.List { 812 doc.ToText(&pkg.buf, comment.Text, "", indent, indentedWidth) 813 } 814 } 815 s := pkg.oneLineNode(iMethod.Type) 816 // Hack: s starts "func" but there is no name present. 817 // We could instead build a FuncDecl but it's not worthwhile. 818 lineComment := "" 819 if iMethod.Comment != nil { 820 lineComment = fmt.Sprintf(" %s", iMethod.Comment.List[0].Text) 821 } 822 pkg.Printf("func %s%s%s\n", name, s[4:], lineComment) 823 found = true 824 } 825 } 826 } 827 return found 828 } 829 830 // printFieldDoc prints the docs for matches of symbol.fieldName. 831 // It reports whether it found any field. 832 // Both symbol and fieldName must be non-empty or it returns false. 833 func (pkg *Package) printFieldDoc(symbol, fieldName string) bool { 834 if symbol == "" || fieldName == "" { 835 return false 836 } 837 defer pkg.flush() 838 types := pkg.findTypes(symbol) 839 if types == nil { 840 pkg.Fatalf("symbol %s is not a type in package %s installed in %q", symbol, pkg.name, pkg.build.ImportPath) 841 } 842 found := false 843 numUnmatched := 0 844 for _, typ := range types { 845 // Type must be a struct. 846 spec := pkg.findTypeSpec(typ.Decl, typ.Name) 847 structType, ok := spec.Type.(*ast.StructType) 848 if !ok { 849 // Not a struct type. 850 continue 851 } 852 for _, field := range structType.Fields.List { 853 // TODO: Anonymous fields. 854 for _, name := range field.Names { 855 if !match(fieldName, name.Name) { 856 numUnmatched++ 857 continue 858 } 859 if !found { 860 pkg.Printf("type %s struct {\n", typ.Name) 861 } 862 if field.Doc != nil { 863 for _, comment := range field.Doc.List { 864 doc.ToText(&pkg.buf, comment.Text, indent, indent, indentedWidth) 865 } 866 } 867 s := pkg.oneLineNode(field.Type) 868 lineComment := "" 869 if field.Comment != nil { 870 lineComment = fmt.Sprintf(" %s", field.Comment.List[0].Text) 871 } 872 pkg.Printf("%s%s %s%s\n", indent, name, s, lineComment) 873 found = true 874 } 875 } 876 } 877 if found { 878 if numUnmatched > 0 { 879 pkg.Printf("\n // ... other fields elided ...\n") 880 } 881 pkg.Printf("}\n") 882 } 883 return found 884 } 885 886 // methodDoc prints the docs for matches of symbol.method. 887 func (pkg *Package) methodDoc(symbol, method string) bool { 888 defer pkg.flush() 889 return pkg.printMethodDoc(symbol, method) 890 } 891 892 // fieldDoc prints the docs for matches of symbol.field. 893 func (pkg *Package) fieldDoc(symbol, field string) bool { 894 defer pkg.flush() 895 return pkg.printFieldDoc(symbol, field) 896 } 897 898 // match reports whether the user's symbol matches the program's. 899 // A lower-case character in the user's string matches either case in the program's. 900 // The program string must be exported. 901 func match(user, program string) bool { 902 if !isExported(program) { 903 return false 904 } 905 if matchCase { 906 return user == program 907 } 908 for _, u := range user { 909 p, w := utf8.DecodeRuneInString(program) 910 program = program[w:] 911 if u == p { 912 continue 913 } 914 if unicode.IsLower(u) && simpleFold(u) == simpleFold(p) { 915 continue 916 } 917 return false 918 } 919 return program == "" 920 } 921 922 // simpleFold returns the minimum rune equivalent to r 923 // under Unicode-defined simple case folding. 924 func simpleFold(r rune) rune { 925 for { 926 r1 := unicode.SimpleFold(r) 927 if r1 <= r { 928 return r1 // wrapped around, found min 929 } 930 r = r1 931 } 932 }