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