github.com/rakyll/go@v0.0.0-20170216000551-64c02460d703/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 := strings.Join(params, ", ") 285 if len(results) == 0 { 286 return fmt.Sprintf("func(%s)", param) 287 } 288 result := strings.Join(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, strings.Join(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 strings.Join(names, ", ") + " " + pkg.oneLineNodeDepth(field.Type, depth) 371 } 372 373 // packageDoc prints the docs for the package (package doc plus one-liners of the rest). 374 func (pkg *Package) packageDoc() { 375 defer pkg.flush() 376 if pkg.showInternals() { 377 pkg.packageClause(false) 378 } 379 380 doc.ToText(&pkg.buf, pkg.doc.Doc, "", indent, indentedWidth) 381 pkg.newlines(1) 382 383 if !pkg.showInternals() { 384 // Show only package docs for commands. 385 return 386 } 387 388 pkg.newlines(2) // Guarantee blank line before the components. 389 pkg.valueSummary(pkg.doc.Consts, false) 390 pkg.valueSummary(pkg.doc.Vars, false) 391 pkg.funcSummary(pkg.doc.Funcs, false) 392 pkg.typeSummary() 393 pkg.bugs() 394 } 395 396 // showInternals reports whether we should show the internals 397 // of a package as opposed to just the package docs. 398 // Used to decide whether to suppress internals for commands. 399 // Called only by Package.packageDoc. 400 func (pkg *Package) showInternals() bool { 401 return pkg.pkg.Name != "main" || showCmd 402 } 403 404 // packageClause prints the package clause. 405 // The argument boolean, if true, suppresses the output if the 406 // user's argument is identical to the actual package path or 407 // is empty, meaning it's the current directory. 408 func (pkg *Package) packageClause(checkUserPath bool) { 409 if checkUserPath { 410 if pkg.userPath == "" || pkg.userPath == pkg.build.ImportPath { 411 return 412 } 413 } 414 importPath := pkg.build.ImportComment 415 if importPath == "" { 416 importPath = pkg.build.ImportPath 417 } 418 pkg.Printf("package %s // import %q\n\n", pkg.name, importPath) 419 if importPath != pkg.build.ImportPath { 420 pkg.Printf("WARNING: package source is installed in %q\n", pkg.build.ImportPath) 421 } 422 } 423 424 // valueSummary prints a one-line summary for each set of values and constants. 425 // If all the types in a constant or variable declaration belong to the same 426 // type they can be printed by typeSummary, and so can be suppressed here. 427 func (pkg *Package) valueSummary(values []*doc.Value, showGrouped bool) { 428 var isGrouped map[*doc.Value]bool 429 if !showGrouped { 430 isGrouped = make(map[*doc.Value]bool) 431 for _, typ := range pkg.doc.Types { 432 if !isExported(typ.Name) { 433 continue 434 } 435 for _, c := range typ.Consts { 436 isGrouped[c] = true 437 } 438 for _, v := range typ.Vars { 439 isGrouped[v] = true 440 } 441 } 442 } 443 444 for _, value := range values { 445 if !isGrouped[value] { 446 if decl := pkg.oneLineNode(value.Decl); decl != "" { 447 pkg.Printf("%s\n", decl) 448 } 449 } 450 } 451 } 452 453 // funcSummary prints a one-line summary for each function. Constructors 454 // are printed by typeSummary, below, and so can be suppressed here. 455 func (pkg *Package) funcSummary(funcs []*doc.Func, showConstructors bool) { 456 // First, identify the constructors. Don't bother figuring out if they're exported. 457 var isConstructor map[*doc.Func]bool 458 if !showConstructors { 459 isConstructor = make(map[*doc.Func]bool) 460 for _, typ := range pkg.doc.Types { 461 if isExported(typ.Name) { 462 for _, f := range typ.Funcs { 463 isConstructor[f] = true 464 } 465 } 466 } 467 } 468 for _, fun := range funcs { 469 // Exported functions only. The go/doc package does not include methods here. 470 if isExported(fun.Name) { 471 if !isConstructor[fun] { 472 pkg.Printf("%s\n", pkg.oneLineNode(fun.Decl)) 473 } 474 } 475 } 476 } 477 478 // typeSummary prints a one-line summary for each type, followed by its constructors. 479 func (pkg *Package) typeSummary() { 480 for _, typ := range pkg.doc.Types { 481 for _, spec := range typ.Decl.Specs { 482 typeSpec := spec.(*ast.TypeSpec) // Must succeed. 483 if isExported(typeSpec.Name.Name) { 484 pkg.Printf("%s\n", pkg.oneLineNode(typeSpec)) 485 // Now print the consts, vars, and constructors. 486 for _, c := range typ.Consts { 487 if decl := pkg.oneLineNode(c.Decl); decl != "" { 488 pkg.Printf(indent+"%s\n", decl) 489 } 490 } 491 for _, v := range typ.Vars { 492 if decl := pkg.oneLineNode(v.Decl); decl != "" { 493 pkg.Printf(indent+"%s\n", decl) 494 } 495 } 496 for _, constructor := range typ.Funcs { 497 if isExported(constructor.Name) { 498 pkg.Printf(indent+"%s\n", pkg.oneLineNode(constructor.Decl)) 499 } 500 } 501 } 502 } 503 } 504 } 505 506 // bugs prints the BUGS information for the package. 507 // TODO: Provide access to TODOs and NOTEs as well (very noisy so off by default)? 508 func (pkg *Package) bugs() { 509 if pkg.doc.Notes["BUG"] == nil { 510 return 511 } 512 pkg.Printf("\n") 513 for _, note := range pkg.doc.Notes["BUG"] { 514 pkg.Printf("%s: %v\n", "BUG", note.Body) 515 } 516 } 517 518 // findValues finds the doc.Values that describe the symbol. 519 func (pkg *Package) findValues(symbol string, docValues []*doc.Value) (values []*doc.Value) { 520 for _, value := range docValues { 521 for _, name := range value.Names { 522 if match(symbol, name) { 523 values = append(values, value) 524 } 525 } 526 } 527 return 528 } 529 530 // findFuncs finds the doc.Funcs that describes the symbol. 531 func (pkg *Package) findFuncs(symbol string) (funcs []*doc.Func) { 532 for _, fun := range pkg.doc.Funcs { 533 if match(symbol, fun.Name) { 534 funcs = append(funcs, fun) 535 } 536 } 537 return 538 } 539 540 // findTypes finds the doc.Types that describes the symbol. 541 // If symbol is empty, it finds all exported types. 542 func (pkg *Package) findTypes(symbol string) (types []*doc.Type) { 543 for _, typ := range pkg.doc.Types { 544 if symbol == "" && isExported(typ.Name) || match(symbol, typ.Name) { 545 types = append(types, typ) 546 } 547 } 548 return 549 } 550 551 // findTypeSpec returns the ast.TypeSpec within the declaration that defines the symbol. 552 // The name must match exactly. 553 func (pkg *Package) findTypeSpec(decl *ast.GenDecl, symbol string) *ast.TypeSpec { 554 for _, spec := range decl.Specs { 555 typeSpec := spec.(*ast.TypeSpec) // Must succeed. 556 if symbol == typeSpec.Name.Name { 557 return typeSpec 558 } 559 } 560 return nil 561 } 562 563 // symbolDoc prints the docs for symbol. There may be multiple matches. 564 // If symbol matches a type, output includes its methods factories and associated constants. 565 // If there is no top-level symbol, symbolDoc looks for methods that match. 566 func (pkg *Package) symbolDoc(symbol string) bool { 567 defer pkg.flush() 568 found := false 569 // Functions. 570 for _, fun := range pkg.findFuncs(symbol) { 571 if !found { 572 pkg.packageClause(true) 573 } 574 // Symbol is a function. 575 decl := fun.Decl 576 decl.Body = nil 577 pkg.emit(fun.Doc, decl) 578 found = true 579 } 580 // Constants and variables behave the same. 581 values := pkg.findValues(symbol, pkg.doc.Consts) 582 values = append(values, pkg.findValues(symbol, pkg.doc.Vars)...) 583 for _, value := range values { 584 // Print each spec only if there is at least one exported symbol in it. 585 // (See issue 11008.) 586 // TODO: Should we elide unexported symbols from a single spec? 587 // It's an unlikely scenario, probably not worth the trouble. 588 // TODO: Would be nice if go/doc did this for us. 589 specs := make([]ast.Spec, 0, len(value.Decl.Specs)) 590 var typ ast.Expr 591 for _, spec := range value.Decl.Specs { 592 vspec := spec.(*ast.ValueSpec) 593 594 // The type name may carry over from a previous specification in the 595 // case of constants and iota. 596 if vspec.Type != nil { 597 typ = vspec.Type 598 } 599 600 for _, ident := range vspec.Names { 601 if isExported(ident.Name) { 602 if vspec.Type == nil && vspec.Values == nil && typ != nil { 603 // This a standalone identifier, as in the case of iota usage. 604 // Thus, assume the type comes from the previous type. 605 vspec.Type = &ast.Ident{ 606 Name: string(pkg.oneLineNode(typ)), 607 NamePos: vspec.End() - 1, 608 } 609 } 610 611 specs = append(specs, vspec) 612 typ = nil // Only inject type on first exported identifier 613 break 614 } 615 } 616 } 617 if len(specs) == 0 { 618 continue 619 } 620 value.Decl.Specs = specs 621 if !found { 622 pkg.packageClause(true) 623 } 624 pkg.emit(value.Doc, value.Decl) 625 found = true 626 } 627 // Types. 628 for _, typ := range pkg.findTypes(symbol) { 629 if !found { 630 pkg.packageClause(true) 631 } 632 decl := typ.Decl 633 spec := pkg.findTypeSpec(decl, typ.Name) 634 trimUnexportedElems(spec) 635 // If there are multiple types defined, reduce to just this one. 636 if len(decl.Specs) > 1 { 637 decl.Specs = []ast.Spec{spec} 638 } 639 pkg.emit(typ.Doc, decl) 640 // Show associated methods, constants, etc. 641 if len(typ.Consts) > 0 || len(typ.Vars) > 0 || len(typ.Funcs) > 0 || len(typ.Methods) > 0 { 642 pkg.Printf("\n") 643 } 644 pkg.valueSummary(typ.Consts, true) 645 pkg.valueSummary(typ.Vars, true) 646 pkg.funcSummary(typ.Funcs, true) 647 pkg.funcSummary(typ.Methods, true) 648 found = true 649 } 650 if !found { 651 // See if there are methods. 652 if !pkg.printMethodDoc("", symbol) { 653 return false 654 } 655 } 656 return true 657 } 658 659 // trimUnexportedElems modifies spec in place to elide unexported fields from 660 // structs and methods from interfaces (unless the unexported flag is set). 661 func trimUnexportedElems(spec *ast.TypeSpec) { 662 if unexported { 663 return 664 } 665 switch typ := spec.Type.(type) { 666 case *ast.StructType: 667 typ.Fields = trimUnexportedFields(typ.Fields, false) 668 case *ast.InterfaceType: 669 typ.Methods = trimUnexportedFields(typ.Methods, true) 670 } 671 } 672 673 // trimUnexportedFields returns the field list trimmed of unexported fields. 674 func trimUnexportedFields(fields *ast.FieldList, isInterface bool) *ast.FieldList { 675 what := "methods" 676 if !isInterface { 677 what = "fields" 678 } 679 680 trimmed := false 681 list := make([]*ast.Field, 0, len(fields.List)) 682 for _, field := range fields.List { 683 names := field.Names 684 if len(names) == 0 { 685 // Embedded type. Use the name of the type. It must be of type ident or *ident. 686 // Nothing else is allowed. 687 switch ident := field.Type.(type) { 688 case *ast.Ident: 689 if isInterface && ident.Name == "error" && ident.Obj == nil { 690 // For documentation purposes, we consider the builtin error 691 // type special when embedded in an interface, such that it 692 // always gets shown publicly. 693 list = append(list, field) 694 continue 695 } 696 names = []*ast.Ident{ident} 697 case *ast.StarExpr: 698 // Must have the form *identifier. 699 // This is only valid on embedded types in structs. 700 if ident, ok := ident.X.(*ast.Ident); ok && !isInterface { 701 names = []*ast.Ident{ident} 702 } 703 case *ast.SelectorExpr: 704 // An embedded type may refer to a type in another package. 705 names = []*ast.Ident{ident.Sel} 706 } 707 if names == nil { 708 // Can only happen if AST is incorrect. Safe to continue with a nil list. 709 log.Print("invalid program: unexpected type for embedded field") 710 } 711 } 712 // Trims if any is unexported. Good enough in practice. 713 ok := true 714 for _, name := range names { 715 if !isExported(name.Name) { 716 trimmed = true 717 ok = false 718 break 719 } 720 } 721 if ok { 722 list = append(list, field) 723 } 724 } 725 if !trimmed { 726 return fields 727 } 728 unexportedField := &ast.Field{ 729 Type: &ast.Ident{ 730 // Hack: printer will treat this as a field with a named type. 731 // Setting Name and NamePos to ("", fields.Closing-1) ensures that 732 // when Pos and End are called on this field, they return the 733 // position right before closing '}' character. 734 Name: "", 735 NamePos: fields.Closing - 1, 736 }, 737 Comment: &ast.CommentGroup{ 738 List: []*ast.Comment{{Text: fmt.Sprintf("// Has unexported %s.\n", what)}}, 739 }, 740 } 741 return &ast.FieldList{ 742 Opening: fields.Opening, 743 List: append(list, unexportedField), 744 Closing: fields.Closing, 745 } 746 } 747 748 // printMethodDoc prints the docs for matches of symbol.method. 749 // If symbol is empty, it prints all methods that match the name. 750 // It reports whether it found any methods. 751 func (pkg *Package) printMethodDoc(symbol, method string) bool { 752 defer pkg.flush() 753 types := pkg.findTypes(symbol) 754 if types == nil { 755 if symbol == "" { 756 return false 757 } 758 pkg.Fatalf("symbol %s is not a type in package %s installed in %q", symbol, pkg.name, pkg.build.ImportPath) 759 } 760 found := false 761 for _, typ := range types { 762 if len(typ.Methods) > 0 { 763 for _, meth := range typ.Methods { 764 if match(method, meth.Name) { 765 decl := meth.Decl 766 decl.Body = nil 767 pkg.emit(meth.Doc, decl) 768 found = true 769 } 770 } 771 continue 772 } 773 // Type may be an interface. The go/doc package does not attach 774 // an interface's methods to the doc.Type. We need to dig around. 775 spec := pkg.findTypeSpec(typ.Decl, typ.Name) 776 inter, ok := spec.Type.(*ast.InterfaceType) 777 if !ok { 778 // Not an interface type. 779 // TODO? Maybe handle struct fields here. 780 continue 781 } 782 for _, iMethod := range inter.Methods.List { 783 // This is an interface, so there can be only one name. 784 // TODO: Anonymous methods (embedding) 785 if len(iMethod.Names) == 0 { 786 continue 787 } 788 name := iMethod.Names[0].Name 789 if match(method, name) { 790 // pkg.oneLineField(iMethod, 0) 791 if iMethod.Doc != nil { 792 for _, comment := range iMethod.Doc.List { 793 doc.ToText(&pkg.buf, comment.Text, "", indent, indentedWidth) 794 } 795 } 796 s := pkg.oneLineNode(iMethod.Type) 797 // Hack: s starts "func" but there is no name present. 798 // We could instead build a FuncDecl but it's not worthwhile. 799 lineComment := "" 800 if iMethod.Comment != nil { 801 lineComment = fmt.Sprintf(" %s", iMethod.Comment.List[0].Text) 802 } 803 pkg.Printf("func %s%s%s\n", name, s[4:], lineComment) 804 found = true 805 } 806 } 807 } 808 return found 809 } 810 811 // methodDoc prints the docs for matches of symbol.method. 812 func (pkg *Package) methodDoc(symbol, method string) bool { 813 defer pkg.flush() 814 return pkg.printMethodDoc(symbol, method) 815 } 816 817 // match reports whether the user's symbol matches the program's. 818 // A lower-case character in the user's string matches either case in the program's. 819 // The program string must be exported. 820 func match(user, program string) bool { 821 if !isExported(program) { 822 return false 823 } 824 if matchCase { 825 return user == program 826 } 827 for _, u := range user { 828 p, w := utf8.DecodeRuneInString(program) 829 program = program[w:] 830 if u == p { 831 continue 832 } 833 if unicode.IsLower(u) && simpleFold(u) == simpleFold(p) { 834 continue 835 } 836 return false 837 } 838 return program == "" 839 } 840 841 // simpleFold returns the minimum rune equivalent to r 842 // under Unicode-defined simple case folding. 843 func simpleFold(r rune) rune { 844 for { 845 r1 := unicode.SimpleFold(r) 846 if r1 <= r { 847 return r1 // wrapped around, found min 848 } 849 r = r1 850 } 851 }