github.com/zebozhuang/go@v0.0.0-20200207033046-f8a98f6f5c5d/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 for _, value := range values { 598 // Print each spec only if there is at least one exported symbol in it. 599 // (See issue 11008.) 600 // TODO: Should we elide unexported symbols from a single spec? 601 // It's an unlikely scenario, probably not worth the trouble. 602 // TODO: Would be nice if go/doc did this for us. 603 specs := make([]ast.Spec, 0, len(value.Decl.Specs)) 604 var typ ast.Expr 605 for _, spec := range value.Decl.Specs { 606 vspec := spec.(*ast.ValueSpec) 607 608 // The type name may carry over from a previous specification in the 609 // case of constants and iota. 610 if vspec.Type != nil { 611 typ = vspec.Type 612 } 613 614 for _, ident := range vspec.Names { 615 if isExported(ident.Name) { 616 if vspec.Type == nil && vspec.Values == nil && typ != nil { 617 // This a standalone identifier, as in the case of iota usage. 618 // Thus, assume the type comes from the previous type. 619 vspec.Type = &ast.Ident{ 620 Name: string(pkg.oneLineNode(typ)), 621 NamePos: vspec.End() - 1, 622 } 623 } 624 625 specs = append(specs, vspec) 626 typ = nil // Only inject type on first exported identifier 627 break 628 } 629 } 630 } 631 if len(specs) == 0 { 632 continue 633 } 634 value.Decl.Specs = specs 635 if !found { 636 pkg.packageClause(true) 637 } 638 pkg.emit(value.Doc, value.Decl) 639 found = true 640 } 641 // Types. 642 for _, typ := range pkg.findTypes(symbol) { 643 if !found { 644 pkg.packageClause(true) 645 } 646 decl := typ.Decl 647 spec := pkg.findTypeSpec(decl, typ.Name) 648 trimUnexportedElems(spec) 649 // If there are multiple types defined, reduce to just this one. 650 if len(decl.Specs) > 1 { 651 decl.Specs = []ast.Spec{spec} 652 } 653 pkg.emit(typ.Doc, decl) 654 // Show associated methods, constants, etc. 655 if len(typ.Consts) > 0 || len(typ.Vars) > 0 || len(typ.Funcs) > 0 || len(typ.Methods) > 0 { 656 pkg.Printf("\n") 657 } 658 pkg.valueSummary(typ.Consts, true) 659 pkg.valueSummary(typ.Vars, true) 660 pkg.funcSummary(typ.Funcs, true) 661 pkg.funcSummary(typ.Methods, true) 662 found = true 663 } 664 if !found { 665 // See if there are methods. 666 if !pkg.printMethodDoc("", symbol) { 667 return false 668 } 669 } 670 return true 671 } 672 673 // trimUnexportedElems modifies spec in place to elide unexported fields from 674 // structs and methods from interfaces (unless the unexported flag is set). 675 func trimUnexportedElems(spec *ast.TypeSpec) { 676 if unexported { 677 return 678 } 679 switch typ := spec.Type.(type) { 680 case *ast.StructType: 681 typ.Fields = trimUnexportedFields(typ.Fields, false) 682 case *ast.InterfaceType: 683 typ.Methods = trimUnexportedFields(typ.Methods, true) 684 } 685 } 686 687 // trimUnexportedFields returns the field list trimmed of unexported fields. 688 func trimUnexportedFields(fields *ast.FieldList, isInterface bool) *ast.FieldList { 689 what := "methods" 690 if !isInterface { 691 what = "fields" 692 } 693 694 trimmed := false 695 list := make([]*ast.Field, 0, len(fields.List)) 696 for _, field := range fields.List { 697 names := field.Names 698 if len(names) == 0 { 699 // Embedded type. Use the name of the type. It must be of type ident or *ident. 700 // Nothing else is allowed. 701 switch ident := field.Type.(type) { 702 case *ast.Ident: 703 if isInterface && ident.Name == "error" && ident.Obj == nil { 704 // For documentation purposes, we consider the builtin error 705 // type special when embedded in an interface, such that it 706 // always gets shown publicly. 707 list = append(list, field) 708 continue 709 } 710 names = []*ast.Ident{ident} 711 case *ast.StarExpr: 712 // Must have the form *identifier. 713 // This is only valid on embedded types in structs. 714 if ident, ok := ident.X.(*ast.Ident); ok && !isInterface { 715 names = []*ast.Ident{ident} 716 } 717 case *ast.SelectorExpr: 718 // An embedded type may refer to a type in another package. 719 names = []*ast.Ident{ident.Sel} 720 } 721 if names == nil { 722 // Can only happen if AST is incorrect. Safe to continue with a nil list. 723 log.Print("invalid program: unexpected type for embedded field") 724 } 725 } 726 // Trims if any is unexported. Good enough in practice. 727 ok := true 728 for _, name := range names { 729 if !isExported(name.Name) { 730 trimmed = true 731 ok = false 732 break 733 } 734 } 735 if ok { 736 list = append(list, field) 737 } 738 } 739 if !trimmed { 740 return fields 741 } 742 unexportedField := &ast.Field{ 743 Type: &ast.Ident{ 744 // Hack: printer will treat this as a field with a named type. 745 // Setting Name and NamePos to ("", fields.Closing-1) ensures that 746 // when Pos and End are called on this field, they return the 747 // position right before closing '}' character. 748 Name: "", 749 NamePos: fields.Closing - 1, 750 }, 751 Comment: &ast.CommentGroup{ 752 List: []*ast.Comment{{Text: fmt.Sprintf("// Has unexported %s.\n", what)}}, 753 }, 754 } 755 return &ast.FieldList{ 756 Opening: fields.Opening, 757 List: append(list, unexportedField), 758 Closing: fields.Closing, 759 } 760 } 761 762 // printMethodDoc prints the docs for matches of symbol.method. 763 // If symbol is empty, it prints all methods that match the name. 764 // It reports whether it found any methods. 765 func (pkg *Package) printMethodDoc(symbol, method string) bool { 766 defer pkg.flush() 767 types := pkg.findTypes(symbol) 768 if types == nil { 769 if symbol == "" { 770 return false 771 } 772 pkg.Fatalf("symbol %s is not a type in package %s installed in %q", symbol, pkg.name, pkg.build.ImportPath) 773 } 774 found := false 775 for _, typ := range types { 776 if len(typ.Methods) > 0 { 777 for _, meth := range typ.Methods { 778 if match(method, meth.Name) { 779 decl := meth.Decl 780 decl.Body = nil 781 pkg.emit(meth.Doc, decl) 782 found = true 783 } 784 } 785 continue 786 } 787 // Type may be an interface. The go/doc package does not attach 788 // an interface's methods to the doc.Type. We need to dig around. 789 spec := pkg.findTypeSpec(typ.Decl, typ.Name) 790 inter, ok := spec.Type.(*ast.InterfaceType) 791 if !ok { 792 // Not an interface type. 793 continue 794 } 795 for _, iMethod := range inter.Methods.List { 796 // This is an interface, so there can be only one name. 797 // TODO: Anonymous methods (embedding) 798 if len(iMethod.Names) == 0 { 799 continue 800 } 801 name := iMethod.Names[0].Name 802 if match(method, name) { 803 if iMethod.Doc != nil { 804 for _, comment := range iMethod.Doc.List { 805 doc.ToText(&pkg.buf, comment.Text, "", indent, indentedWidth) 806 } 807 } 808 s := pkg.oneLineNode(iMethod.Type) 809 // Hack: s starts "func" but there is no name present. 810 // We could instead build a FuncDecl but it's not worthwhile. 811 lineComment := "" 812 if iMethod.Comment != nil { 813 lineComment = fmt.Sprintf(" %s", iMethod.Comment.List[0].Text) 814 } 815 pkg.Printf("func %s%s%s\n", name, s[4:], lineComment) 816 found = true 817 } 818 } 819 } 820 return found 821 } 822 823 // printFieldDoc prints the docs for matches of symbol.fieldName. 824 // It reports whether it found any field. 825 // Both symbol and fieldName must be non-empty or it returns false. 826 func (pkg *Package) printFieldDoc(symbol, fieldName string) bool { 827 if symbol == "" || fieldName == "" { 828 return false 829 } 830 defer pkg.flush() 831 types := pkg.findTypes(symbol) 832 if types == nil { 833 pkg.Fatalf("symbol %s is not a type in package %s installed in %q", symbol, pkg.name, pkg.build.ImportPath) 834 } 835 found := false 836 numUnmatched := 0 837 for _, typ := range types { 838 // Type must be a struct. 839 spec := pkg.findTypeSpec(typ.Decl, typ.Name) 840 structType, ok := spec.Type.(*ast.StructType) 841 if !ok { 842 // Not a struct type. 843 continue 844 } 845 for _, field := range structType.Fields.List { 846 // TODO: Anonymous fields. 847 for _, name := range field.Names { 848 if !match(fieldName, name.Name) { 849 numUnmatched++ 850 continue 851 } 852 if !found { 853 pkg.Printf("type %s struct {\n", typ.Name) 854 } 855 if field.Doc != nil { 856 for _, comment := range field.Doc.List { 857 doc.ToText(&pkg.buf, comment.Text, indent, indent, indentedWidth) 858 } 859 } 860 s := pkg.oneLineNode(field.Type) 861 lineComment := "" 862 if field.Comment != nil { 863 lineComment = fmt.Sprintf(" %s", field.Comment.List[0].Text) 864 } 865 pkg.Printf("%s%s %s%s\n", indent, name, s, lineComment) 866 found = true 867 } 868 } 869 } 870 if found { 871 if numUnmatched > 0 { 872 pkg.Printf("\n // ... other fields elided ...\n") 873 } 874 pkg.Printf("}\n") 875 } 876 return found 877 } 878 879 // methodDoc prints the docs for matches of symbol.method. 880 func (pkg *Package) methodDoc(symbol, method string) bool { 881 defer pkg.flush() 882 return pkg.printMethodDoc(symbol, method) 883 } 884 885 // fieldDoc prints the docs for matches of symbol.field. 886 func (pkg *Package) fieldDoc(symbol, field string) bool { 887 defer pkg.flush() 888 return pkg.printFieldDoc(symbol, field) 889 } 890 891 // match reports whether the user's symbol matches the program's. 892 // A lower-case character in the user's string matches either case in the program's. 893 // The program string must be exported. 894 func match(user, program string) bool { 895 if !isExported(program) { 896 return false 897 } 898 if matchCase { 899 return user == program 900 } 901 for _, u := range user { 902 p, w := utf8.DecodeRuneInString(program) 903 program = program[w:] 904 if u == p { 905 continue 906 } 907 if unicode.IsLower(u) && simpleFold(u) == simpleFold(p) { 908 continue 909 } 910 return false 911 } 912 return program == "" 913 } 914 915 // simpleFold returns the minimum rune equivalent to r 916 // under Unicode-defined simple case folding. 917 func simpleFold(r rune) rune { 918 for { 919 r1 := unicode.SimpleFold(r) 920 if r1 <= r { 921 return r1 // wrapped around, found min 922 } 923 r = r1 924 } 925 }