summaryrefslogtreecommitdiff
path: root/src/pkg/go/doc
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/go/doc')
-rw-r--r--src/pkg/go/doc/comment.go5
-rw-r--r--src/pkg/go/doc/doc.go12
-rw-r--r--src/pkg/go/doc/doc_test.go2
-rw-r--r--src/pkg/go/doc/example.go270
-rw-r--r--src/pkg/go/doc/example_test.go111
-rw-r--r--src/pkg/go/doc/exports.go2
-rw-r--r--src/pkg/go/doc/reader.go113
-rw-r--r--src/pkg/go/doc/synopsis.go41
-rw-r--r--src/pkg/go/doc/synopsis_test.go5
-rw-r--r--src/pkg/go/doc/testdata/a.0.golden11
-rw-r--r--src/pkg/go/doc/testdata/a.1.golden11
-rw-r--r--src/pkg/go/doc/testdata/a.2.golden11
-rw-r--r--src/pkg/go/doc/testdata/a0.go9
-rw-r--r--src/pkg/go/doc/testdata/a1.go4
-rw-r--r--src/pkg/go/doc/testdata/benchmark.go4
-rw-r--r--src/pkg/go/doc/testdata/e.go2
-rw-r--r--src/pkg/go/doc/testdata/error2.1.golden2
-rw-r--r--src/pkg/go/doc/testdata/error2.go2
-rw-r--r--src/pkg/go/doc/testdata/template.txt7
-rw-r--r--src/pkg/go/doc/testdata/testing.1.golden2
-rw-r--r--src/pkg/go/doc/testdata/testing.go4
21 files changed, 543 insertions, 87 deletions
diff --git a/src/pkg/go/doc/comment.go b/src/pkg/go/doc/comment.go
index 6f0edd4ba..c4b7e6ae6 100644
--- a/src/pkg/go/doc/comment.go
+++ b/src/pkg/go/doc/comment.go
@@ -174,7 +174,7 @@ func unindent(block []string) {
}
// heading returns the trimmed line if it passes as a section heading;
-// otherwise it returns the empty string.
+// otherwise it returns the empty string.
func heading(line string) string {
line = strings.TrimSpace(line)
if len(line) == 0 {
@@ -229,7 +229,8 @@ type block struct {
var nonAlphaNumRx = regexp.MustCompile(`[^a-zA-Z0-9]`)
func anchorID(line string) string {
- return nonAlphaNumRx.ReplaceAllString(line, "_")
+ // Add a "hdr-" prefix to avoid conflicting with IDs used for package symbols.
+ return "hdr-" + nonAlphaNumRx.ReplaceAllString(line, "_")
}
// ToHTML converts comment text to formatted HTML.
diff --git a/src/pkg/go/doc/doc.go b/src/pkg/go/doc/doc.go
index 9c606315d..65b1b83eb 100644
--- a/src/pkg/go/doc/doc.go
+++ b/src/pkg/go/doc/doc.go
@@ -17,7 +17,16 @@ type Package struct {
ImportPath string
Imports []string
Filenames []string
- Bugs []string
+ // DEPRECATED. For backward compatibility Bugs is still populated,
+ // but all new code should use Notes instead.
+ Bugs []string
+
+ // Notes such as TODO(userid): or SECURITY(userid):
+ // along the lines of BUG(userid). Any marker with 2 or more upper
+ // case [A-Z] letters is recognised.
+ // BUG is explicitly not included in these notes but will
+ // be in a subsequent change when the Bugs field above is removed.
+ Notes map[string][]string
// declarations
Consts []*Value
@@ -89,6 +98,7 @@ func New(pkg *ast.Package, importPath string, mode Mode) *Package {
Imports: sortedKeys(r.imports),
Filenames: r.filenames,
Bugs: r.bugs,
+ Notes: r.notes,
Consts: sortedValues(r.values, token.CONST),
Types: sortedTypes(r.types, mode&AllMethods != 0),
Vars: sortedValues(r.values, token.VAR),
diff --git a/src/pkg/go/doc/doc_test.go b/src/pkg/go/doc/doc_test.go
index f957ede4a..8043038b4 100644
--- a/src/pkg/go/doc/doc_test.go
+++ b/src/pkg/go/doc/doc_test.go
@@ -123,7 +123,7 @@ func test(t *testing.T, mode Mode) {
}
// compare
- if bytes.Compare(got, want) != 0 {
+ if !bytes.Equal(got, want) {
t.Errorf("package %s\n\tgot:\n%s\n\twant:\n%s", pkg.Name, got, want)
}
}
diff --git a/src/pkg/go/doc/example.go b/src/pkg/go/doc/example.go
index a7e0e250a..693ad5b94 100644
--- a/src/pkg/go/doc/example.go
+++ b/src/pkg/go/doc/example.go
@@ -9,21 +9,29 @@ package doc
import (
"go/ast"
"go/token"
+ "path"
"regexp"
"sort"
+ "strconv"
"strings"
"unicode"
"unicode/utf8"
)
+// An Example represents an example function found in a source files.
type Example struct {
- Name string // name of the item being exemplified
- Doc string // example function doc string
- Code ast.Node
- Comments []*ast.CommentGroup
- Output string // expected output
+ Name string // name of the item being exemplified
+ Doc string // example function doc string
+ Code ast.Node
+ Play *ast.File // a whole program version of the example
+ Comments []*ast.CommentGroup
+ Output string // expected output
+ EmptyOutput bool // expect empty output
+ Order int // original source code order
}
+// Examples returns the examples found in the files, sorted by Name field.
+// The Order fields record the order in which the examples were encountered.
func Examples(files ...*ast.File) []*Example {
var list []*Example
for _, file := range files {
@@ -52,12 +60,16 @@ func Examples(files ...*ast.File) []*Example {
if f.Doc != nil {
doc = f.Doc.Text()
}
+ output, hasOutput := exampleOutput(f.Body, file.Comments)
flist = append(flist, &Example{
- Name: name[len("Example"):],
- Doc: doc,
- Code: f.Body,
- Comments: file.Comments,
- Output: exampleOutput(f, file.Comments),
+ Name: name[len("Example"):],
+ Doc: doc,
+ Code: f.Body,
+ Play: playExample(file, f.Body),
+ Comments: file.Comments,
+ Output: output,
+ EmptyOutput: output == "" && hasOutput,
+ Order: len(flist),
})
}
if !hasTests && numDecl > 1 && len(flist) == 1 {
@@ -65,6 +77,7 @@ func Examples(files ...*ast.File) []*Example {
// other top-level declarations, and no tests or
// benchmarks, use the whole file as the example.
flist[0].Code = file
+ flist[0].Play = playExampleFile(file)
}
list = append(list, flist...)
}
@@ -74,26 +87,22 @@ func Examples(files ...*ast.File) []*Example {
var outputPrefix = regexp.MustCompile(`(?i)^[[:space:]]*output:`)
-func exampleOutput(fun *ast.FuncDecl, comments []*ast.CommentGroup) string {
- // find the last comment in the function
- var last *ast.CommentGroup
- for _, cg := range comments {
- if cg.Pos() < fun.Pos() {
- continue
- }
- if cg.End() > fun.End() {
- break
- }
- last = cg
- }
- if last != nil {
+// Extracts the expected output and whether there was a valid output comment
+func exampleOutput(b *ast.BlockStmt, comments []*ast.CommentGroup) (output string, ok bool) {
+ if _, last := lastComment(b, comments); last != nil {
// test that it begins with the correct prefix
text := last.Text()
if loc := outputPrefix.FindStringIndex(text); loc != nil {
- return strings.TrimSpace(text[loc[1]:])
+ text = text[loc[1]:]
+ // Strip zero or more spaces followed by \n or a single space.
+ text = strings.TrimLeft(text, " ")
+ if len(text) > 0 && text[0] == '\n' {
+ text = text[1:]
+ }
+ return text, true
}
}
- return "" // no suitable comment found
+ return "", false // no suitable comment found
}
// isTest tells whether name looks like a test, example, or benchmark.
@@ -115,3 +124,214 @@ type exampleByName []*Example
func (s exampleByName) Len() int { return len(s) }
func (s exampleByName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s exampleByName) Less(i, j int) bool { return s[i].Name < s[j].Name }
+
+// playExample synthesizes a new *ast.File based on the provided
+// file with the provided function body as the body of main.
+func playExample(file *ast.File, body *ast.BlockStmt) *ast.File {
+ if !strings.HasSuffix(file.Name.Name, "_test") {
+ // We don't support examples that are part of the
+ // greater package (yet).
+ return nil
+ }
+
+ // Find top-level declarations in the file.
+ topDecls := make(map[*ast.Object]bool)
+ for _, decl := range file.Decls {
+ switch d := decl.(type) {
+ case *ast.FuncDecl:
+ topDecls[d.Name.Obj] = true
+ case *ast.GenDecl:
+ for _, spec := range d.Specs {
+ switch s := spec.(type) {
+ case *ast.TypeSpec:
+ topDecls[s.Name.Obj] = true
+ case *ast.ValueSpec:
+ for _, id := range s.Names {
+ topDecls[id.Obj] = true
+ }
+ }
+ }
+ }
+ }
+
+ // Find unresolved identifiers and uses of top-level declarations.
+ unresolved := make(map[string]bool)
+ usesTopDecl := false
+ var inspectFunc func(ast.Node) bool
+ inspectFunc = func(n ast.Node) bool {
+ // For selector expressions, only inspect the left hand side.
+ // (For an expression like fmt.Println, only add "fmt" to the
+ // set of unresolved names, not "Println".)
+ if e, ok := n.(*ast.SelectorExpr); ok {
+ ast.Inspect(e.X, inspectFunc)
+ return false
+ }
+ if id, ok := n.(*ast.Ident); ok {
+ if id.Obj == nil {
+ unresolved[id.Name] = true
+ } else if topDecls[id.Obj] {
+ usesTopDecl = true
+ }
+ }
+ return true
+ }
+ ast.Inspect(body, inspectFunc)
+ if usesTopDecl {
+ // We don't support examples that are not self-contained (yet).
+ return nil
+ }
+
+ // Remove predeclared identifiers from unresolved list.
+ for n := range unresolved {
+ if predeclaredTypes[n] || predeclaredConstants[n] || predeclaredFuncs[n] {
+ delete(unresolved, n)
+ }
+ }
+
+ // Use unresolved identifiers to determine the imports used by this
+ // example. The heuristic assumes package names match base import
+ // paths for imports w/o renames (should be good enough most of the time).
+ namedImports := make(map[string]string) // [name]path
+ var blankImports []ast.Spec // _ imports
+ for _, s := range file.Imports {
+ p, err := strconv.Unquote(s.Path.Value)
+ if err != nil {
+ continue
+ }
+ n := path.Base(p)
+ if s.Name != nil {
+ n = s.Name.Name
+ switch n {
+ case "_":
+ blankImports = append(blankImports, s)
+ continue
+ case ".":
+ // We can't resolve dot imports (yet).
+ return nil
+ }
+ }
+ if unresolved[n] {
+ namedImports[n] = p
+ delete(unresolved, n)
+ }
+ }
+
+ // If there are other unresolved identifiers, give up because this
+ // synthesized file is not going to build.
+ if len(unresolved) > 0 {
+ return nil
+ }
+
+ // Include documentation belonging to blank imports.
+ var comments []*ast.CommentGroup
+ for _, s := range blankImports {
+ if c := s.(*ast.ImportSpec).Doc; c != nil {
+ comments = append(comments, c)
+ }
+ }
+
+ // Include comments that are inside the function body.
+ for _, c := range file.Comments {
+ if body.Pos() <= c.Pos() && c.End() <= body.End() {
+ comments = append(comments, c)
+ }
+ }
+
+ // Strip "Output:" commment and adjust body end position.
+ body, comments = stripOutputComment(body, comments)
+
+ // Synthesize import declaration.
+ importDecl := &ast.GenDecl{
+ Tok: token.IMPORT,
+ Lparen: 1, // Need non-zero Lparen and Rparen so that printer
+ Rparen: 1, // treats this as a factored import.
+ }
+ for n, p := range namedImports {
+ s := &ast.ImportSpec{Path: &ast.BasicLit{Value: strconv.Quote(p)}}
+ if path.Base(p) != n {
+ s.Name = ast.NewIdent(n)
+ }
+ importDecl.Specs = append(importDecl.Specs, s)
+ }
+ importDecl.Specs = append(importDecl.Specs, blankImports...)
+
+ // Synthesize main function.
+ funcDecl := &ast.FuncDecl{
+ Name: ast.NewIdent("main"),
+ Type: &ast.FuncType{},
+ Body: body,
+ }
+
+ // Synthesize file.
+ return &ast.File{
+ Name: ast.NewIdent("main"),
+ Decls: []ast.Decl{importDecl, funcDecl},
+ Comments: comments,
+ }
+}
+
+// playExampleFile takes a whole file example and synthesizes a new *ast.File
+// such that the example is function main in package main.
+func playExampleFile(file *ast.File) *ast.File {
+ // Strip copyright comment if present.
+ comments := file.Comments
+ if len(comments) > 0 && strings.HasPrefix(comments[0].Text(), "Copyright") {
+ comments = comments[1:]
+ }
+
+ // Copy declaration slice, rewriting the ExampleX function to main.
+ var decls []ast.Decl
+ for _, d := range file.Decls {
+ if f, ok := d.(*ast.FuncDecl); ok && isTest(f.Name.Name, "Example") {
+ // Copy the FuncDecl, as it may be used elsewhere.
+ newF := *f
+ newF.Name = ast.NewIdent("main")
+ newF.Body, comments = stripOutputComment(f.Body, comments)
+ d = &newF
+ }
+ decls = append(decls, d)
+ }
+
+ // Copy the File, as it may be used elsewhere.
+ f := *file
+ f.Name = ast.NewIdent("main")
+ f.Decls = decls
+ f.Comments = comments
+ return &f
+}
+
+// stripOutputComment finds and removes an "Output:" commment from body
+// and comments, and adjusts the body block's end position.
+func stripOutputComment(body *ast.BlockStmt, comments []*ast.CommentGroup) (*ast.BlockStmt, []*ast.CommentGroup) {
+ // Do nothing if no "Output:" comment found.
+ i, last := lastComment(body, comments)
+ if last == nil || !outputPrefix.MatchString(last.Text()) {
+ return body, comments
+ }
+
+ // Copy body and comments, as the originals may be used elsewhere.
+ newBody := &ast.BlockStmt{
+ Lbrace: body.Lbrace,
+ List: body.List,
+ Rbrace: last.Pos(),
+ }
+ newComments := make([]*ast.CommentGroup, len(comments)-1)
+ copy(newComments, comments[:i])
+ copy(newComments[i:], comments[i+1:])
+ return newBody, newComments
+}
+
+// lastComment returns the last comment inside the provided block.
+func lastComment(b *ast.BlockStmt, c []*ast.CommentGroup) (i int, last *ast.CommentGroup) {
+ pos, end := b.Pos(), b.End()
+ for j, cg := range c {
+ if cg.Pos() < pos {
+ continue
+ }
+ if cg.End() > end {
+ break
+ }
+ i, last = j, cg
+ }
+ return
+}
diff --git a/src/pkg/go/doc/example_test.go b/src/pkg/go/doc/example_test.go
new file mode 100644
index 000000000..b70efd93d
--- /dev/null
+++ b/src/pkg/go/doc/example_test.go
@@ -0,0 +1,111 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package doc_test
+
+import (
+ "bytes"
+ "go/doc"
+ "go/format"
+ "go/parser"
+ "go/token"
+ "strings"
+ "testing"
+)
+
+const exampleTestFile = `
+package foo_test
+
+import (
+ "fmt"
+ "log"
+ "os/exec"
+)
+
+func ExampleHello() {
+ fmt.Println("Hello, world!")
+ // Output: Hello, world!
+}
+
+func ExampleImport() {
+ out, err := exec.Command("date").Output()
+ if err != nil {
+ log.Fatal(err)
+ }
+ fmt.Printf("The date is %s\n", out)
+}
+`
+
+var exampleTestCases = []struct {
+ Name, Play, Output string
+}{
+ {
+ Name: "Hello",
+ Play: exampleHelloPlay,
+ Output: "Hello, world!\n",
+ },
+ {
+ Name: "Import",
+ Play: exampleImportPlay,
+ },
+}
+
+const exampleHelloPlay = `package main
+
+import (
+ "fmt"
+)
+
+func main() {
+ fmt.Println("Hello, world!")
+}
+`
+const exampleImportPlay = `package main
+
+import (
+ "fmt"
+ "log"
+ "os/exec"
+)
+
+func main() {
+ out, err := exec.Command("date").Output()
+ if err != nil {
+ log.Fatal(err)
+ }
+ fmt.Printf("The date is %s\n", out)
+}
+`
+
+func TestExamples(t *testing.T) {
+ fs := token.NewFileSet()
+ file, err := parser.ParseFile(fs, "test.go", strings.NewReader(exampleTestFile), parser.ParseComments)
+ if err != nil {
+ t.Fatal(err)
+ }
+ for i, e := range doc.Examples(file) {
+ c := exampleTestCases[i]
+ if e.Name != c.Name {
+ t.Errorf("got Name == %q, want %q", e.Name, c.Name)
+ }
+ if w := c.Play; w != "" {
+ var g string // hah
+ if e.Play == nil {
+ g = "<nil>"
+ } else {
+ b := new(bytes.Buffer)
+ if err := format.Node(b, fs, e.Play); err != nil {
+ t.Fatal(err)
+ }
+ g = b.String()
+ }
+ if g != w {
+ t.Errorf("%s: got Play == %q, want %q", c.Name, g, w)
+ }
+ }
+ if g, w := e.Output, c.Output; g != w {
+ t.Errorf("%s: got Output == %q, want %q", c.Name, g, w)
+ }
+ }
+}
diff --git a/src/pkg/go/doc/exports.go b/src/pkg/go/doc/exports.go
index 146be5d87..ff01285d4 100644
--- a/src/pkg/go/doc/exports.go
+++ b/src/pkg/go/doc/exports.go
@@ -107,7 +107,7 @@ func (r *reader) filterParamList(fields *ast.FieldList) {
// filterType strips any unexported struct fields or method types from typ
// in place. If fields (or methods) have been removed, the corresponding
-// struct or interface type has the Incomplete field set to true.
+// struct or interface type has the Incomplete field set to true.
//
func (r *reader) filterType(parent *namedType, typ ast.Expr) {
switch t := typ.(type) {
diff --git a/src/pkg/go/doc/reader.go b/src/pkg/go/doc/reader.go
index 5eaae37b7..dd6a57299 100644
--- a/src/pkg/go/doc/reader.go
+++ b/src/pkg/go/doc/reader.go
@@ -46,7 +46,7 @@ func (mset methodSet) set(f *ast.FuncDecl) {
// since it has documentation, assume f is simply another
// implementation and ignore it. This does not happen if the
// caller is using go/build.ScanDir to determine the list of
- // files implementing a package.
+ // files implementing a package.
return
}
// function doesn't exist or has no documentation; use f
@@ -149,6 +149,7 @@ type reader struct {
doc string // package documentation, if any
filenames []string
bugs []string
+ notes map[string][]string
// declarations
imports map[string]int
@@ -400,10 +401,23 @@ func (r *reader) readFunc(fun *ast.FuncDecl) {
}
var (
- bug_markers = regexp.MustCompile("^/[/*][ \t]*BUG\\(.*\\):[ \t]*") // BUG(uid):
- bug_content = regexp.MustCompile("[^ \n\r\t]+") // at least one non-whitespace char
+ noteMarker = regexp.MustCompile(`^/[/*][ \t]*([A-Z][A-Z]+)\(.+\):[ \t]*(.*)`) // MARKER(uid)
+ noteContent = regexp.MustCompile(`[^ \n\r\t]+`) // at least one non-whitespace char
)
+func readNote(c *ast.CommentGroup) (marker, annotation string) {
+ text := c.List[0].Text
+ if m := noteMarker.FindStringSubmatch(text); m != nil {
+ if btxt := m[2]; noteContent.MatchString(btxt) {
+ // non-empty MARKER comment; collect comment without the MARKER prefix
+ list := append([]*ast.Comment(nil), c.List...) // make a copy
+ list[0].Text = m[2]
+ return m[1], (&ast.CommentGroup{List: list}).Text()
+ }
+ }
+ return "", ""
+}
+
// readFile adds the AST for a source file to the reader.
//
func (r *reader) readFile(src *ast.File) {
@@ -469,16 +483,12 @@ func (r *reader) readFile(src *ast.File) {
}
}
- // collect BUG(...) comments
+ // collect MARKER(...): annotations
for _, c := range src.Comments {
- text := c.List[0].Text
- if m := bug_markers.FindStringIndex(text); m != nil {
- // found a BUG comment; maybe empty
- if btxt := text[m[1]:]; bug_content.MatchString(btxt) {
- // non-empty BUG comment; collect comment without BUG prefix
- list := append([]*ast.Comment(nil), c.List...) // make a copy
- list[0].Text = text[m[1]:]
- r.bugs = append(r.bugs, (&ast.CommentGroup{List: list}).Text())
+ if marker, text := readNote(c); marker != "" {
+ r.notes[marker] = append(r.notes[marker], text)
+ if marker == "BUG" {
+ r.bugs = append(r.bugs, text)
}
}
}
@@ -492,9 +502,10 @@ func (r *reader) readPackage(pkg *ast.Package, mode Mode) {
r.mode = mode
r.types = make(map[string]*namedType)
r.funcs = make(methodSet)
+ r.notes = make(map[string][]string)
// sort package files before reading them so that the
- // result result does not depend on map iteration order
+ // result does not depend on map iteration order
i := 0
for filename := range pkg.Files {
r.filenames[i] = filename
@@ -515,29 +526,6 @@ func (r *reader) readPackage(pkg *ast.Package, mode Mode) {
// ----------------------------------------------------------------------------
// Types
-var predeclaredTypes = map[string]bool{
- "bool": true,
- "byte": true,
- "complex64": true,
- "complex128": true,
- "error": true,
- "float32": true,
- "float64": true,
- "int": true,
- "int8": true,
- "int16": true,
- "int32": true,
- "int64": true,
- "rune": true,
- "string": true,
- "uint": true,
- "uint8": true,
- "uint16": true,
- "uint32": true,
- "uint64": true,
- "uintptr": true,
-}
-
func customizeRecv(f *Func, recvTypeName string, embeddedIsPtr bool, level int) *Func {
if f == nil || f.Decl == nil || f.Decl.Recv == nil || len(f.Decl.Recv.List) != 1 {
return f // shouldn't happen, but be safe
@@ -620,7 +608,7 @@ func (r *reader) computeMethodSets() {
// types that have no declaration. Instead, these functions and methods
// are shown at the package level. It also removes types with missing
// declarations or which are not visible.
-//
+//
func (r *reader) cleanupTypes() {
for _, t := range r.types {
visible := r.isVisible(t.name)
@@ -772,3 +760,54 @@ func sortedFuncs(m methodSet, allMethods bool) []*Func {
)
return list
}
+
+// ----------------------------------------------------------------------------
+// Predeclared identifiers
+
+var predeclaredTypes = map[string]bool{
+ "bool": true,
+ "byte": true,
+ "complex64": true,
+ "complex128": true,
+ "error": true,
+ "float32": true,
+ "float64": true,
+ "int": true,
+ "int8": true,
+ "int16": true,
+ "int32": true,
+ "int64": true,
+ "rune": true,
+ "string": true,
+ "uint": true,
+ "uint8": true,
+ "uint16": true,
+ "uint32": true,
+ "uint64": true,
+ "uintptr": true,
+}
+
+var predeclaredFuncs = map[string]bool{
+ "append": true,
+ "cap": true,
+ "close": true,
+ "complex": true,
+ "copy": true,
+ "delete": true,
+ "imag": true,
+ "len": true,
+ "make": true,
+ "new": true,
+ "panic": true,
+ "print": true,
+ "println": true,
+ "real": true,
+ "recover": true,
+}
+
+var predeclaredConstants = map[string]bool{
+ "false": true,
+ "iota": true,
+ "nil": true,
+ "true": true,
+}
diff --git a/src/pkg/go/doc/synopsis.go b/src/pkg/go/doc/synopsis.go
index 2192d78c0..2d1817439 100644
--- a/src/pkg/go/doc/synopsis.go
+++ b/src/pkg/go/doc/synopsis.go
@@ -4,7 +4,10 @@
package doc
-import "unicode"
+import (
+ "strings"
+ "unicode"
+)
// firstSentenceLen returns the length of the first sentence in s.
// The sentence ends after the first period followed by space and
@@ -24,17 +27,12 @@ func firstSentenceLen(s string) int {
return len(s)
}
-// Synopsis returns a cleaned version of the first sentence in s.
-// That sentence ends after the first period followed by space and
-// not preceded by exactly one uppercase letter. The result string
-// has no \n, \r, or \t characters and uses only single spaces between
-// words.
-//
-func Synopsis(s string) string {
- n := firstSentenceLen(s)
+// clean replaces each sequence of space, \n, \r, or \t characters
+// with a single space and removes any trailing and leading spaces.
+func clean(s string) string {
var b []byte
p := byte(' ')
- for i := 0; i < n; i++ {
+ for i := 0; i < len(s); i++ {
q := s[i]
if q == '\n' || q == '\r' || q == '\t' {
q = ' '
@@ -50,3 +48,26 @@ func Synopsis(s string) string {
}
return string(b)
}
+
+// Synopsis returns a cleaned version of the first sentence in s.
+// That sentence ends after the first period followed by space and
+// not preceded by exactly one uppercase letter. The result string
+// has no \n, \r, or \t characters and uses only single spaces between
+// words. If s starts with any of the IllegalPrefixes, the result
+// is the empty string.
+//
+func Synopsis(s string) string {
+ s = clean(s[0:firstSentenceLen(s)])
+ for _, prefix := range IllegalPrefixes {
+ if strings.HasPrefix(strings.ToLower(s), prefix) {
+ return ""
+ }
+ }
+ return s
+}
+
+var IllegalPrefixes = []string{
+ "copyright",
+ "all rights",
+ "author",
+}
diff --git a/src/pkg/go/doc/synopsis_test.go b/src/pkg/go/doc/synopsis_test.go
index dfc6598af..fd7081a07 100644
--- a/src/pkg/go/doc/synopsis_test.go
+++ b/src/pkg/go/doc/synopsis_test.go
@@ -28,6 +28,11 @@ var tests = []struct {
{"P. Q. ", 8, "P. Q."},
{"Package Καλημέρα κόσμε.", 36, "Package Καλημέρα κόσμε."},
{"Package こんにちは 世界\n", 31, "Package こんにちは 世界"},
+ {"Package foo does bar.", 21, "Package foo does bar."},
+ {"Copyright 2012 Google, Inc. Package foo does bar.", 27, ""},
+ {"All Rights reserved. Package foo does bar.", 20, ""},
+ {"All rights reserved. Package foo does bar.", 20, ""},
+ {"Authors: foo@bar.com. Package foo does bar.", 21, ""},
}
func TestSynopsis(t *testing.T) {
diff --git a/src/pkg/go/doc/testdata/a.0.golden b/src/pkg/go/doc/testdata/a.0.golden
index 24db02d34..ae3756c84 100644
--- a/src/pkg/go/doc/testdata/a.0.golden
+++ b/src/pkg/go/doc/testdata/a.0.golden
@@ -8,6 +8,17 @@ FILENAMES
testdata/a0.go
testdata/a1.go
+BUGS .Bugs is now deprecated, please use .Notes instead
+ // bug0
+ // bug1
+
BUGS
// bug0
// bug1
+
+SECBUGS
+ // sec hole 0 need to fix asap
+
+TODOS
+ // todo0
+ // todo1
diff --git a/src/pkg/go/doc/testdata/a.1.golden b/src/pkg/go/doc/testdata/a.1.golden
index 24db02d34..ae3756c84 100644
--- a/src/pkg/go/doc/testdata/a.1.golden
+++ b/src/pkg/go/doc/testdata/a.1.golden
@@ -8,6 +8,17 @@ FILENAMES
testdata/a0.go
testdata/a1.go
+BUGS .Bugs is now deprecated, please use .Notes instead
+ // bug0
+ // bug1
+
BUGS
// bug0
// bug1
+
+SECBUGS
+ // sec hole 0 need to fix asap
+
+TODOS
+ // todo0
+ // todo1
diff --git a/src/pkg/go/doc/testdata/a.2.golden b/src/pkg/go/doc/testdata/a.2.golden
index 24db02d34..ae3756c84 100644
--- a/src/pkg/go/doc/testdata/a.2.golden
+++ b/src/pkg/go/doc/testdata/a.2.golden
@@ -8,6 +8,17 @@ FILENAMES
testdata/a0.go
testdata/a1.go
+BUGS .Bugs is now deprecated, please use .Notes instead
+ // bug0
+ // bug1
+
BUGS
// bug0
// bug1
+
+SECBUGS
+ // sec hole 0 need to fix asap
+
+TODOS
+ // todo0
+ // todo1
diff --git a/src/pkg/go/doc/testdata/a0.go b/src/pkg/go/doc/testdata/a0.go
index dc552989e..71af470ee 100644
--- a/src/pkg/go/doc/testdata/a0.go
+++ b/src/pkg/go/doc/testdata/a0.go
@@ -6,3 +6,12 @@
package a
//BUG(uid): bug0
+
+//TODO(uid): todo0
+
+// A note with some spaces after it, should be ignored (watch out for
+// emacs modes that remove trailing whitespace).
+//NOTE(uid):
+
+// SECBUG(uid): sec hole 0
+// need to fix asap
diff --git a/src/pkg/go/doc/testdata/a1.go b/src/pkg/go/doc/testdata/a1.go
index 098776c1b..9fad1e09b 100644
--- a/src/pkg/go/doc/testdata/a1.go
+++ b/src/pkg/go/doc/testdata/a1.go
@@ -6,3 +6,7 @@
package a
//BUG(uid): bug1
+
+//TODO(uid): todo1
+
+//TODO(): ignored
diff --git a/src/pkg/go/doc/testdata/benchmark.go b/src/pkg/go/doc/testdata/benchmark.go
index 0aded5bb4..905e49644 100644
--- a/src/pkg/go/doc/testdata/benchmark.go
+++ b/src/pkg/go/doc/testdata/benchmark.go
@@ -13,7 +13,7 @@ import (
)
var matchBenchmarks = flag.String("test.bench", "", "regular expression to select benchmarks to run")
-var benchTime = flag.Float64("test.benchtime", 1, "approximate run time for each benchmark, in seconds")
+var benchTime = flag.Duration("test.benchtime", 1*time.Second, "approximate run time for each benchmark")
// An internal type but exported because it is cross-package; part of the implementation
// of go test.
@@ -151,7 +151,7 @@ func (b *B) launch() {
b.runN(n)
// Run the benchmark for at least the specified amount of time.
- d := time.Duration(*benchTime * float64(time.Second))
+ d := *benchTime
for !b.failed && b.duration < d && n < 1e9 {
last := n
// Predict iterations/sec.
diff --git a/src/pkg/go/doc/testdata/e.go b/src/pkg/go/doc/testdata/e.go
index 19dd138cf..ec432e3e5 100644
--- a/src/pkg/go/doc/testdata/e.go
+++ b/src/pkg/go/doc/testdata/e.go
@@ -106,7 +106,7 @@ type U4 struct {
*u5
}
-// U4.M should appear as method of U4.
+// U4.M should appear as method of U4.
func (*U4) M() {}
type u5 struct {
diff --git a/src/pkg/go/doc/testdata/error2.1.golden b/src/pkg/go/doc/testdata/error2.1.golden
index 776bd1b3e..dbcc1b03e 100644
--- a/src/pkg/go/doc/testdata/error2.1.golden
+++ b/src/pkg/go/doc/testdata/error2.1.golden
@@ -10,7 +10,7 @@ FILENAMES
TYPES
//
type I0 interface {
- // When embedded, the the locally declared error interface
+ // When embedded, the locally-declared error interface
// is only visible if all declarations are shown.
error
}
diff --git a/src/pkg/go/doc/testdata/error2.go b/src/pkg/go/doc/testdata/error2.go
index 6cc36feef..6ee96c245 100644
--- a/src/pkg/go/doc/testdata/error2.go
+++ b/src/pkg/go/doc/testdata/error2.go
@@ -5,7 +5,7 @@
package error2
type I0 interface {
- // When embedded, the the locally declared error interface
+ // When embedded, the locally-declared error interface
// is only visible if all declarations are shown.
error
}
diff --git a/src/pkg/go/doc/testdata/template.txt b/src/pkg/go/doc/testdata/template.txt
index 32e331cdd..d3882b6b9 100644
--- a/src/pkg/go/doc/testdata/template.txt
+++ b/src/pkg/go/doc/testdata/template.txt
@@ -60,6 +60,9 @@ TYPES
{{end}}{{end}}{{end}}{{/*
*/}}{{with .Bugs}}
-BUGS
+BUGS .Bugs is now deprecated, please use .Notes instead
{{range .}} {{synopsis .}}
-{{end}}{{end}} \ No newline at end of file
+{{end}}{{end}}{{with .Notes}}{{range $marker, $content := .}}
+{{$marker}}S
+{{range $content}} {{synopsis .}}
+{{end}}{{end}}{{end}} \ No newline at end of file
diff --git a/src/pkg/go/doc/testdata/testing.1.golden b/src/pkg/go/doc/testdata/testing.1.golden
index d26a4685c..ffdb5c3b5 100644
--- a/src/pkg/go/doc/testdata/testing.1.golden
+++ b/src/pkg/go/doc/testdata/testing.1.golden
@@ -45,7 +45,7 @@ VARIABLES
)
//
- var benchTime = flag.Float64("test.benchtime", 1, "approximate run time for each benchmark, in seconds")
+ var benchTime = flag.Duration("test.benchtime", 1*time.Second, "approximate run time for each benchmark")
//
var matchBenchmarks = flag.String("test.bench", "", "regular expression to select benchmarks to run")
diff --git a/src/pkg/go/doc/testdata/testing.go b/src/pkg/go/doc/testdata/testing.go
index 71c1d1eaf..c2499ad77 100644
--- a/src/pkg/go/doc/testdata/testing.go
+++ b/src/pkg/go/doc/testdata/testing.go
@@ -197,7 +197,7 @@ func (c *common) Fatalf(format string, args ...interface{}) {
c.FailNow()
}
-// Parallel signals that this test is to be run in parallel with (and only with)
+// Parallel signals that this test is to be run in parallel with (and only with)
// other parallel tests in this CPU group.
func (t *T) Parallel() {
t.signal <- (*T)(nil) // Release main testing loop
@@ -215,7 +215,7 @@ func tRunner(t *T, test *InternalTest) {
t.start = time.Now()
// When this goroutine is done, either because test.F(t)
- // returned normally or because a test failure triggered
+ // returned normally or because a test failure triggered
// a call to runtime.Goexit, record the duration and send
// a signal saying that the test is done.
defer func() {