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 "unicode" 20 "unicode/utf8" 21 ) 22 23 const ( 24 punchedCardWidth = 80 // These things just won't leave us alone. 25 indentedWidth = punchedCardWidth - len(indent) 26 indent = " " 27 ) 28 29 type Package struct { 30 writer io.Writer // Destination for output. 31 name string // Package name, json for encoding/json. 32 userPath string // String the user used to find this package. 33 unexported bool 34 matchCase bool 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 // pkg.Fatalf is like log.Fatalf, but panics so it can be recovered in the 50 // main do function, so it doesn't cause an exit. Allows testing to work 51 // without running a subprocess. The log prefix will be added when 52 // logged in main; it is not added here. 53 func (pkg *Package) Fatalf(format string, args ...interface{}) { 54 panic(PackageError(fmt.Sprintf(format, args...))) 55 } 56 57 // parsePackage turns the build package we found into a parsed package 58 // we can then use to generate documentation. 59 func parsePackage(writer io.Writer, pkg *build.Package, userPath string) *Package { 60 fs := token.NewFileSet() 61 // include tells parser.ParseDir which files to include. 62 // That means the file must be in the build package's GoFiles or CgoFiles 63 // list only (no tag-ignored files, tests, swig or other non-Go files). 64 include := func(info os.FileInfo) bool { 65 for _, name := range pkg.GoFiles { 66 if name == info.Name() { 67 return true 68 } 69 } 70 for _, name := range pkg.CgoFiles { 71 if name == info.Name() { 72 return true 73 } 74 } 75 return false 76 } 77 pkgs, err := parser.ParseDir(fs, pkg.Dir, include, parser.ParseComments) 78 if err != nil { 79 log.Fatal(err) 80 } 81 // Make sure they are all in one package. 82 if len(pkgs) != 1 { 83 log.Fatalf("multiple packages in directory %s", pkg.Dir) 84 } 85 astPkg := pkgs[pkg.Name] 86 87 // TODO: go/doc does not include typed constants in the constants 88 // list, which is what we want. For instance, time.Sunday is of type 89 // time.Weekday, so it is defined in the type but not in the 90 // Consts list for the package. This prevents 91 // go doc time.Sunday 92 // from finding the symbol. Work around this for now, but we 93 // should fix it in go/doc. 94 // A similar story applies to factory functions. 95 docPkg := doc.New(astPkg, pkg.ImportPath, doc.AllDecls) 96 for _, typ := range docPkg.Types { 97 docPkg.Consts = append(docPkg.Consts, typ.Consts...) 98 docPkg.Vars = append(docPkg.Vars, typ.Vars...) 99 docPkg.Funcs = append(docPkg.Funcs, typ.Funcs...) 100 } 101 102 return &Package{ 103 writer: writer, 104 name: pkg.Name, 105 userPath: userPath, 106 pkg: astPkg, 107 file: ast.MergePackageFiles(astPkg, 0), 108 doc: docPkg, 109 build: pkg, 110 fs: fs, 111 } 112 } 113 114 func (pkg *Package) Printf(format string, args ...interface{}) { 115 fmt.Fprintf(&pkg.buf, format, args...) 116 } 117 118 func (pkg *Package) flush() { 119 _, err := pkg.writer.Write(pkg.buf.Bytes()) 120 if err != nil { 121 log.Fatal(err) 122 } 123 pkg.buf.Reset() // Not needed, but it's a flush. 124 } 125 126 var newlineBytes = []byte("\n\n") // We never ask for more than 2. 127 128 // newlines guarantees there are n newlines at the end of the buffer. 129 func (pkg *Package) newlines(n int) { 130 for !bytes.HasSuffix(pkg.buf.Bytes(), newlineBytes[:n]) { 131 pkg.buf.WriteRune('\n') 132 } 133 } 134 135 // emit prints the node. 136 func (pkg *Package) emit(comment string, node ast.Node) { 137 if node != nil { 138 err := format.Node(&pkg.buf, pkg.fs, node) 139 if err != nil { 140 log.Fatal(err) 141 } 142 if comment != "" { 143 pkg.newlines(2) // Guarantee blank line before comment. 144 doc.ToText(&pkg.buf, comment, " ", indent, indentedWidth) 145 } 146 pkg.newlines(1) 147 } 148 } 149 150 var formatBuf bytes.Buffer // Reusable to avoid allocation. 151 152 // formatNode is a helper function for printing. 153 func (pkg *Package) formatNode(node ast.Node) []byte { 154 formatBuf.Reset() 155 format.Node(&formatBuf, pkg.fs, node) 156 return formatBuf.Bytes() 157 } 158 159 // oneLineFunc prints a function declaration as a single line. 160 func (pkg *Package) oneLineFunc(decl *ast.FuncDecl) { 161 decl.Doc = nil 162 decl.Body = nil 163 pkg.emit("", decl) 164 } 165 166 // oneLineValueGenDecl prints a var or const declaration as a single line. 167 func (pkg *Package) oneLineValueGenDecl(decl *ast.GenDecl) { 168 decl.Doc = nil 169 dotDotDot := "" 170 if len(decl.Specs) > 1 { 171 dotDotDot = " ..." 172 } 173 // Find the first relevant spec. 174 for i, spec := range decl.Specs { 175 valueSpec := spec.(*ast.ValueSpec) // Must succeed; we can't mix types in one genDecl. 176 if !isExported(valueSpec.Names[0].Name) { 177 continue 178 } 179 typ := "" 180 if valueSpec.Type != nil { 181 typ = fmt.Sprintf(" %s", pkg.formatNode(valueSpec.Type)) 182 } 183 val := "" 184 if i < len(valueSpec.Values) && valueSpec.Values[i] != nil { 185 val = fmt.Sprintf(" = %s", pkg.formatNode(valueSpec.Values[i])) 186 } 187 pkg.Printf("%s %s%s%s%s\n", decl.Tok, valueSpec.Names[0], typ, val, dotDotDot) 188 break 189 } 190 } 191 192 // oneLineTypeDecl prints a type declaration as a single line. 193 func (pkg *Package) oneLineTypeDecl(spec *ast.TypeSpec) { 194 spec.Doc = nil 195 spec.Comment = nil 196 switch spec.Type.(type) { 197 case *ast.InterfaceType: 198 pkg.Printf("type %s interface { ... }\n", spec.Name) 199 case *ast.StructType: 200 pkg.Printf("type %s struct { ... }\n", spec.Name) 201 default: 202 pkg.Printf("type %s %s\n", spec.Name, pkg.formatNode(spec.Type)) 203 } 204 } 205 206 // packageDoc prints the docs for the package (package doc plus one-liners of the rest). 207 func (pkg *Package) packageDoc() { 208 defer pkg.flush() 209 if pkg.showInternals() { 210 pkg.packageClause(false) 211 } 212 213 doc.ToText(&pkg.buf, pkg.doc.Doc, "", indent, indentedWidth) 214 pkg.newlines(1) 215 216 if !pkg.showInternals() { 217 // Show only package docs for commands. 218 return 219 } 220 221 pkg.newlines(1) 222 pkg.valueSummary(pkg.doc.Consts) 223 pkg.valueSummary(pkg.doc.Vars) 224 pkg.funcSummary(pkg.doc.Funcs) 225 pkg.typeSummary() 226 pkg.bugs() 227 } 228 229 // showInternals reports whether we should show the internals 230 // of a package as opposed to just the package docs. 231 // Used to decide whether to suppress internals for commands. 232 // Called only by Package.packageDoc. 233 func (pkg *Package) showInternals() bool { 234 return pkg.pkg.Name != "main" || showCmd 235 } 236 237 // packageClause prints the package clause. 238 // The argument boolean, if true, suppresses the output if the 239 // user's argument is identical to the actual package path or 240 // is empty, meaning it's the current directory. 241 func (pkg *Package) packageClause(checkUserPath bool) { 242 if checkUserPath { 243 if pkg.userPath == "" || pkg.userPath == pkg.build.ImportPath { 244 return 245 } 246 } 247 importPath := pkg.build.ImportComment 248 if importPath == "" { 249 importPath = pkg.build.ImportPath 250 } 251 pkg.Printf("package %s // import %q\n\n", pkg.name, importPath) 252 if importPath != pkg.build.ImportPath { 253 pkg.Printf("WARNING: package source is installed in %q\n", pkg.build.ImportPath) 254 } 255 } 256 257 // valueSummary prints a one-line summary for each set of values and constants. 258 func (pkg *Package) valueSummary(values []*doc.Value) { 259 for _, value := range values { 260 // Only print first item in spec, show ... to stand for the rest. 261 spec := value.Decl.Specs[0].(*ast.ValueSpec) // Must succeed. 262 exported := true 263 for _, name := range spec.Names { 264 if !isExported(name.Name) { 265 exported = false 266 break 267 } 268 } 269 if exported { 270 pkg.oneLineValueGenDecl(value.Decl) 271 } 272 } 273 } 274 275 // funcSummary prints a one-line summary for each function. 276 func (pkg *Package) funcSummary(funcs []*doc.Func) { 277 for _, fun := range funcs { 278 decl := fun.Decl 279 // Exported functions only. The go/doc package does not include methods here. 280 if isExported(fun.Name) { 281 pkg.oneLineFunc(decl) 282 } 283 } 284 } 285 286 // typeSummary prints a one-line summary for each type. 287 func (pkg *Package) typeSummary() { 288 for _, typ := range pkg.doc.Types { 289 for _, spec := range typ.Decl.Specs { 290 typeSpec := spec.(*ast.TypeSpec) // Must succeed. 291 if isExported(typeSpec.Name.Name) { 292 pkg.oneLineTypeDecl(typeSpec) 293 } 294 } 295 } 296 } 297 298 // bugs prints the BUGS information for the package. 299 // TODO: Provide access to TODOs and NOTEs as well (very noisy so off by default)? 300 func (pkg *Package) bugs() { 301 if pkg.doc.Notes["BUG"] == nil { 302 return 303 } 304 pkg.Printf("\n") 305 for _, note := range pkg.doc.Notes["BUG"] { 306 pkg.Printf("%s: %v\n", "BUG", note.Body) 307 } 308 } 309 310 // findValues finds the doc.Values that describe the symbol. 311 func (pkg *Package) findValues(symbol string, docValues []*doc.Value) (values []*doc.Value) { 312 for _, value := range docValues { 313 for _, name := range value.Names { 314 if match(symbol, name) { 315 values = append(values, value) 316 } 317 } 318 } 319 return 320 } 321 322 // findFuncs finds the doc.Funcs that describes the symbol. 323 func (pkg *Package) findFuncs(symbol string) (funcs []*doc.Func) { 324 for _, fun := range pkg.doc.Funcs { 325 if match(symbol, fun.Name) { 326 funcs = append(funcs, fun) 327 } 328 } 329 return 330 } 331 332 // findTypes finds the doc.Types that describes the symbol. 333 // If symbol is empty, it finds all exported types. 334 func (pkg *Package) findTypes(symbol string) (types []*doc.Type) { 335 for _, typ := range pkg.doc.Types { 336 if symbol == "" && isExported(typ.Name) || match(symbol, typ.Name) { 337 types = append(types, typ) 338 } 339 } 340 return 341 } 342 343 // findTypeSpec returns the ast.TypeSpec within the declaration that defines the symbol. 344 // The name must match exactly. 345 func (pkg *Package) findTypeSpec(decl *ast.GenDecl, symbol string) *ast.TypeSpec { 346 for _, spec := range decl.Specs { 347 typeSpec := spec.(*ast.TypeSpec) // Must succeed. 348 if symbol == typeSpec.Name.Name { 349 return typeSpec 350 } 351 } 352 return nil 353 } 354 355 // symbolDoc prints the docs for symbol. There may be multiple matches. 356 // If symbol matches a type, output includes its methods factories and associated constants. 357 // If there is no top-level symbol, symbolDoc looks for methods that match. 358 func (pkg *Package) symbolDoc(symbol string) { 359 defer pkg.flush() 360 found := false 361 // Functions. 362 for _, fun := range pkg.findFuncs(symbol) { 363 if !found { 364 pkg.packageClause(true) 365 } 366 // Symbol is a function. 367 decl := fun.Decl 368 decl.Body = nil 369 pkg.emit(fun.Doc, decl) 370 found = true 371 } 372 // Constants and variables behave the same. 373 values := pkg.findValues(symbol, pkg.doc.Consts) 374 values = append(values, pkg.findValues(symbol, pkg.doc.Vars)...) 375 for _, value := range values { 376 // Print each spec only if there is at least one exported symbol in it. 377 // (See issue 11008.) 378 // TODO: Should we elide unexported symbols from a single spec? 379 // It's an unlikely scenario, probably not worth the trouble. 380 // TODO: Would be nice if go/doc did this for us. 381 specs := make([]ast.Spec, 0, len(value.Decl.Specs)) 382 for _, spec := range value.Decl.Specs { 383 vspec := spec.(*ast.ValueSpec) 384 for _, ident := range vspec.Names { 385 if isExported(ident.Name) { 386 specs = append(specs, vspec) 387 break 388 } 389 } 390 } 391 if len(specs) == 0 { 392 continue 393 } 394 value.Decl.Specs = specs 395 if !found { 396 pkg.packageClause(true) 397 } 398 pkg.emit(value.Doc, value.Decl) 399 found = true 400 } 401 // Types. 402 for _, typ := range pkg.findTypes(symbol) { 403 if !found { 404 pkg.packageClause(true) 405 } 406 decl := typ.Decl 407 spec := pkg.findTypeSpec(decl, typ.Name) 408 trimUnexportedElems(spec) 409 // If there are multiple types defined, reduce to just this one. 410 if len(decl.Specs) > 1 { 411 decl.Specs = []ast.Spec{spec} 412 } 413 pkg.emit(typ.Doc, decl) 414 // Show associated methods, constants, etc. 415 if len(typ.Consts) > 0 || len(typ.Vars) > 0 || len(typ.Funcs) > 0 || len(typ.Methods) > 0 { 416 pkg.Printf("\n") 417 } 418 pkg.valueSummary(typ.Consts) 419 pkg.valueSummary(typ.Vars) 420 pkg.funcSummary(typ.Funcs) 421 pkg.funcSummary(typ.Methods) 422 found = true 423 } 424 if !found { 425 // See if there are methods. 426 if !pkg.printMethodDoc("", symbol) { 427 log.Printf("symbol %s not present in package %s installed in %q", symbol, pkg.name, pkg.build.ImportPath) 428 } 429 } 430 } 431 432 // trimUnexportedElems modifies spec in place to elide unexported fields from 433 // structs and methods from interfaces (unless the unexported flag is set). 434 func trimUnexportedElems(spec *ast.TypeSpec) { 435 if unexported { 436 return 437 } 438 switch typ := spec.Type.(type) { 439 case *ast.StructType: 440 typ.Fields = trimUnexportedFields(typ.Fields, "fields") 441 case *ast.InterfaceType: 442 typ.Methods = trimUnexportedFields(typ.Methods, "methods") 443 } 444 } 445 446 // trimUnexportedFields returns the field list trimmed of unexported fields. 447 func trimUnexportedFields(fields *ast.FieldList, what string) *ast.FieldList { 448 trimmed := false 449 list := make([]*ast.Field, 0, len(fields.List)) 450 for _, field := range fields.List { 451 // Trims if any is unexported. Good enough in practice. 452 ok := true 453 for _, name := range field.Names { 454 if !isExported(name.Name) { 455 trimmed = true 456 ok = false 457 break 458 } 459 } 460 if ok { 461 list = append(list, field) 462 } 463 } 464 if !trimmed { 465 return fields 466 } 467 unexportedField := &ast.Field{ 468 Type: ast.NewIdent(""), // Hack: printer will treat this as a field with a named type. 469 Comment: &ast.CommentGroup{ 470 List: []*ast.Comment{ 471 &ast.Comment{ 472 Text: fmt.Sprintf("// Has unexported %s.\n", what), 473 }, 474 }, 475 }, 476 } 477 return &ast.FieldList{ 478 Opening: fields.Opening, 479 List: append(list, unexportedField), 480 Closing: fields.Closing, 481 } 482 } 483 484 // printMethodDoc prints the docs for matches of symbol.method. 485 // If symbol is empty, it prints all methods that match the name. 486 // It reports whether it found any methods. 487 func (pkg *Package) printMethodDoc(symbol, method string) bool { 488 defer pkg.flush() 489 types := pkg.findTypes(symbol) 490 if types == nil { 491 if symbol == "" { 492 return false 493 } 494 pkg.Fatalf("symbol %s is not a type in package %s installed in %q", symbol, pkg.name, pkg.build.ImportPath) 495 } 496 found := false 497 for _, typ := range types { 498 for _, meth := range typ.Methods { 499 if match(method, meth.Name) { 500 decl := meth.Decl 501 decl.Body = nil 502 pkg.emit(meth.Doc, decl) 503 found = true 504 } 505 } 506 } 507 return found 508 } 509 510 // methodDoc prints the docs for matches of symbol.method. 511 func (pkg *Package) methodDoc(symbol, method string) { 512 defer pkg.flush() 513 if !pkg.printMethodDoc(symbol, method) { 514 pkg.Fatalf("no method %s.%s in package %s installed in %q", symbol, method, pkg.name, pkg.build.ImportPath) 515 } 516 } 517 518 // match reports whether the user's symbol matches the program's. 519 // A lower-case character in the user's string matches either case in the program's. 520 // The program string must be exported. 521 func match(user, program string) bool { 522 if !isExported(program) { 523 return false 524 } 525 if matchCase { 526 return user == program 527 } 528 for _, u := range user { 529 p, w := utf8.DecodeRuneInString(program) 530 program = program[w:] 531 if u == p { 532 continue 533 } 534 if unicode.IsLower(u) && simpleFold(u) == simpleFold(p) { 535 continue 536 } 537 return false 538 } 539 return program == "" 540 } 541 542 // simpleFold returns the minimum rune equivalent to r 543 // under Unicode-defined simple case folding. 544 func simpleFold(r rune) rune { 545 for { 546 r1 := unicode.SimpleFold(r) 547 if r1 <= r { 548 return r1 // wrapped around, found min 549 } 550 r = r1 551 } 552 } 553