summaryrefslogtreecommitdiffstats
path: root/src/go/doc
diff options
context:
space:
mode:
Diffstat (limited to 'src/go/doc')
-rw-r--r--src/go/doc/Makefile7
-rw-r--r--src/go/doc/comment.go513
-rw-r--r--src/go/doc/comment_test.go247
-rw-r--r--src/go/doc/doc.go222
-rw-r--r--src/go/doc/doc_test.go164
-rw-r--r--src/go/doc/example.go550
-rw-r--r--src/go/doc/example_test.go743
-rw-r--r--src/go/doc/exports.go309
-rw-r--r--src/go/doc/filter.go105
-rw-r--r--src/go/doc/headscan.go116
-rw-r--r--src/go/doc/reader.go917
-rw-r--r--src/go/doc/synopsis.go83
-rw-r--r--src/go/doc/synopsis_test.go52
-rw-r--r--src/go/doc/testdata/a.0.golden52
-rw-r--r--src/go/doc/testdata/a.1.golden52
-rw-r--r--src/go/doc/testdata/a.2.golden52
-rw-r--r--src/go/doc/testdata/a0.go40
-rw-r--r--src/go/doc/testdata/a1.go12
-rw-r--r--src/go/doc/testdata/b.0.golden71
-rw-r--r--src/go/doc/testdata/b.1.golden83
-rw-r--r--src/go/doc/testdata/b.2.golden71
-rw-r--r--src/go/doc/testdata/b.go58
-rw-r--r--src/go/doc/testdata/benchmark.go293
-rw-r--r--src/go/doc/testdata/blank.0.golden62
-rw-r--r--src/go/doc/testdata/blank.1.golden83
-rw-r--r--src/go/doc/testdata/blank.2.golden62
-rw-r--r--src/go/doc/testdata/blank.go75
-rw-r--r--src/go/doc/testdata/bugpara.0.golden20
-rw-r--r--src/go/doc/testdata/bugpara.1.golden20
-rw-r--r--src/go/doc/testdata/bugpara.2.golden20
-rw-r--r--src/go/doc/testdata/bugpara.go9
-rw-r--r--src/go/doc/testdata/c.0.golden48
-rw-r--r--src/go/doc/testdata/c.1.golden48
-rw-r--r--src/go/doc/testdata/c.2.golden48
-rw-r--r--src/go/doc/testdata/c.go62
-rw-r--r--src/go/doc/testdata/d.0.golden104
-rw-r--r--src/go/doc/testdata/d.1.golden104
-rw-r--r--src/go/doc/testdata/d.2.golden104
-rw-r--r--src/go/doc/testdata/d1.go57
-rw-r--r--src/go/doc/testdata/d2.go45
-rw-r--r--src/go/doc/testdata/e.0.golden109
-rw-r--r--src/go/doc/testdata/e.1.golden144
-rw-r--r--src/go/doc/testdata/e.2.golden130
-rw-r--r--src/go/doc/testdata/e.go147
-rw-r--r--src/go/doc/testdata/error1.0.golden30
-rw-r--r--src/go/doc/testdata/error1.1.golden32
-rw-r--r--src/go/doc/testdata/error1.2.golden30
-rw-r--r--src/go/doc/testdata/error1.go24
-rw-r--r--src/go/doc/testdata/error2.0.golden27
-rw-r--r--src/go/doc/testdata/error2.1.golden37
-rw-r--r--src/go/doc/testdata/error2.2.golden27
-rw-r--r--src/go/doc/testdata/error2.go29
-rw-r--r--src/go/doc/testdata/example.go81
-rw-r--r--src/go/doc/testdata/f.0.golden13
-rw-r--r--src/go/doc/testdata/f.1.golden16
-rw-r--r--src/go/doc/testdata/f.2.golden13
-rw-r--r--src/go/doc/testdata/f.go14
-rw-r--r--src/go/doc/testdata/g.0.golden32
-rw-r--r--src/go/doc/testdata/g.1.golden34
-rw-r--r--src/go/doc/testdata/g.2.golden32
-rw-r--r--src/go/doc/testdata/g.go25
-rw-r--r--src/go/doc/testdata/issue12839.0.golden51
-rw-r--r--src/go/doc/testdata/issue12839.1.golden54
-rw-r--r--src/go/doc/testdata/issue12839.2.golden51
-rw-r--r--src/go/doc/testdata/issue12839.go69
-rw-r--r--src/go/doc/testdata/issue13742.0.golden25
-rw-r--r--src/go/doc/testdata/issue13742.1.golden25
-rw-r--r--src/go/doc/testdata/issue13742.2.golden25
-rw-r--r--src/go/doc/testdata/issue13742.go18
-rw-r--r--src/go/doc/testdata/issue16153.0.golden32
-rw-r--r--src/go/doc/testdata/issue16153.1.golden34
-rw-r--r--src/go/doc/testdata/issue16153.2.golden32
-rw-r--r--src/go/doc/testdata/issue16153.go27
-rw-r--r--src/go/doc/testdata/issue17788.0.golden8
-rw-r--r--src/go/doc/testdata/issue17788.1.golden8
-rw-r--r--src/go/doc/testdata/issue17788.2.golden8
-rw-r--r--src/go/doc/testdata/issue17788.go8
-rw-r--r--src/go/doc/testdata/issue22856.0.golden45
-rw-r--r--src/go/doc/testdata/issue22856.1.golden45
-rw-r--r--src/go/doc/testdata/issue22856.2.golden45
-rw-r--r--src/go/doc/testdata/issue22856.go27
-rw-r--r--src/go/doc/testdata/predeclared.0.golden8
-rw-r--r--src/go/doc/testdata/predeclared.1.golden22
-rw-r--r--src/go/doc/testdata/predeclared.2.golden8
-rw-r--r--src/go/doc/testdata/predeclared.go22
-rw-r--r--src/go/doc/testdata/template.txt68
-rw-r--r--src/go/doc/testdata/testing.0.golden156
-rw-r--r--src/go/doc/testdata/testing.1.golden298
-rw-r--r--src/go/doc/testdata/testing.2.golden156
-rw-r--r--src/go/doc/testdata/testing.go404
90 files changed, 8588 insertions, 0 deletions
diff --git a/src/go/doc/Makefile b/src/go/doc/Makefile
new file mode 100644
index 0000000..ca4948f
--- /dev/null
+++ b/src/go/doc/Makefile
@@ -0,0 +1,7 @@
+# Copyright 2009 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.
+
+# Script to test heading detection heuristic
+headscan: headscan.go
+ go build headscan.go
diff --git a/src/go/doc/comment.go b/src/go/doc/comment.go
new file mode 100644
index 0000000..92131a3
--- /dev/null
+++ b/src/go/doc/comment.go
@@ -0,0 +1,513 @@
+// Copyright 2009 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.
+
+// Godoc comment extraction and comment -> HTML formatting.
+
+package doc
+
+import (
+ "bytes"
+ "internal/lazyregexp"
+ "io"
+ "strings"
+ "text/template" // for HTMLEscape
+ "unicode"
+ "unicode/utf8"
+)
+
+const (
+ ldquo = "“"
+ rdquo = "”"
+ ulquo = "“"
+ urquo = "”"
+)
+
+var (
+ htmlQuoteReplacer = strings.NewReplacer(ulquo, ldquo, urquo, rdquo)
+ unicodeQuoteReplacer = strings.NewReplacer("``", ulquo, "''", urquo)
+)
+
+// Escape comment text for HTML. If nice is set,
+// also turn `` into “ and '' into ”.
+func commentEscape(w io.Writer, text string, nice bool) {
+ if nice {
+ // In the first pass, we convert `` and '' into their unicode equivalents.
+ // This prevents them from being escaped in HTMLEscape.
+ text = convertQuotes(text)
+ var buf bytes.Buffer
+ template.HTMLEscape(&buf, []byte(text))
+ // Now we convert the unicode quotes to their HTML escaped entities to maintain old behavior.
+ // We need to use a temp buffer to read the string back and do the conversion,
+ // otherwise HTMLEscape will escape & to &
+ htmlQuoteReplacer.WriteString(w, buf.String())
+ return
+ }
+ template.HTMLEscape(w, []byte(text))
+}
+
+func convertQuotes(text string) string {
+ return unicodeQuoteReplacer.Replace(text)
+}
+
+const (
+ // Regexp for Go identifiers
+ identRx = `[\pL_][\pL_0-9]*`
+
+ // Regexp for URLs
+ // Match parens, and check later for balance - see #5043, #22285
+ // Match .,:;?! within path, but not at end - see #18139, #16565
+ // This excludes some rare yet valid urls ending in common punctuation
+ // in order to allow sentences ending in URLs.
+
+ // protocol (required) e.g. http
+ protoPart = `(https?|ftp|file|gopher|mailto|nntp)`
+ // host (required) e.g. www.example.com or [::1]:8080
+ hostPart = `([a-zA-Z0-9_@\-.\[\]:]+)`
+ // path+query+fragment (optional) e.g. /path/index.html?q=foo#bar
+ pathPart = `([.,:;?!]*[a-zA-Z0-9$'()*+&#=@~_/\-\[\]%])*`
+
+ urlRx = protoPart + `://` + hostPart + pathPart
+)
+
+var matchRx = lazyregexp.New(`(` + urlRx + `)|(` + identRx + `)`)
+
+var (
+ html_a = []byte(`<a href="`)
+ html_aq = []byte(`">`)
+ html_enda = []byte("</a>")
+ html_i = []byte("<i>")
+ html_endi = []byte("</i>")
+ html_p = []byte("<p>\n")
+ html_endp = []byte("</p>\n")
+ html_pre = []byte("<pre>")
+ html_endpre = []byte("</pre>\n")
+ html_h = []byte(`<h3 id="`)
+ html_hq = []byte(`">`)
+ html_endh = []byte("</h3>\n")
+)
+
+// Emphasize and escape a line of text for HTML. URLs are converted into links;
+// if the URL also appears in the words map, the link is taken from the map (if
+// the corresponding map value is the empty string, the URL is not converted
+// into a link). Go identifiers that appear in the words map are italicized; if
+// the corresponding map value is not the empty string, it is considered a URL
+// and the word is converted into a link. If nice is set, the remaining text's
+// appearance is improved where it makes sense (e.g., `` is turned into &ldquo;
+// and '' into &rdquo;).
+func emphasize(w io.Writer, line string, words map[string]string, nice bool) {
+ for {
+ m := matchRx.FindStringSubmatchIndex(line)
+ if m == nil {
+ break
+ }
+ // m >= 6 (two parenthesized sub-regexps in matchRx, 1st one is urlRx)
+
+ // write text before match
+ commentEscape(w, line[0:m[0]], nice)
+
+ // adjust match for URLs
+ match := line[m[0]:m[1]]
+ if strings.Contains(match, "://") {
+ m0, m1 := m[0], m[1]
+ for _, s := range []string{"()", "{}", "[]"} {
+ open, close := s[:1], s[1:] // E.g., "(" and ")"
+ // require opening parentheses before closing parentheses (#22285)
+ if i := strings.Index(match, close); i >= 0 && i < strings.Index(match, open) {
+ m1 = m0 + i
+ match = line[m0:m1]
+ }
+ // require balanced pairs of parentheses (#5043)
+ for i := 0; strings.Count(match, open) != strings.Count(match, close) && i < 10; i++ {
+ m1 = strings.LastIndexAny(line[:m1], s)
+ match = line[m0:m1]
+ }
+ }
+ if m1 != m[1] {
+ // redo matching with shortened line for correct indices
+ m = matchRx.FindStringSubmatchIndex(line[:m[0]+len(match)])
+ }
+ }
+
+ // analyze match
+ url := ""
+ italics := false
+ if words != nil {
+ url, italics = words[match]
+ }
+ if m[2] >= 0 {
+ // match against first parenthesized sub-regexp; must be match against urlRx
+ if !italics {
+ // no alternative URL in words list, use match instead
+ url = match
+ }
+ italics = false // don't italicize URLs
+ }
+
+ // write match
+ if len(url) > 0 {
+ w.Write(html_a)
+ template.HTMLEscape(w, []byte(url))
+ w.Write(html_aq)
+ }
+ if italics {
+ w.Write(html_i)
+ }
+ commentEscape(w, match, nice)
+ if italics {
+ w.Write(html_endi)
+ }
+ if len(url) > 0 {
+ w.Write(html_enda)
+ }
+
+ // advance
+ line = line[m[1]:]
+ }
+ commentEscape(w, line, nice)
+}
+
+func indentLen(s string) int {
+ i := 0
+ for i < len(s) && (s[i] == ' ' || s[i] == '\t') {
+ i++
+ }
+ return i
+}
+
+func isBlank(s string) bool {
+ return len(s) == 0 || (len(s) == 1 && s[0] == '\n')
+}
+
+func commonPrefix(a, b string) string {
+ i := 0
+ for i < len(a) && i < len(b) && a[i] == b[i] {
+ i++
+ }
+ return a[0:i]
+}
+
+func unindent(block []string) {
+ if len(block) == 0 {
+ return
+ }
+
+ // compute maximum common white prefix
+ prefix := block[0][0:indentLen(block[0])]
+ for _, line := range block {
+ if !isBlank(line) {
+ prefix = commonPrefix(prefix, line[0:indentLen(line)])
+ }
+ }
+ n := len(prefix)
+
+ // remove
+ for i, line := range block {
+ if !isBlank(line) {
+ block[i] = line[n:]
+ }
+ }
+}
+
+// heading returns the trimmed line if it passes as a section heading;
+// otherwise it returns the empty string.
+func heading(line string) string {
+ line = strings.TrimSpace(line)
+ if len(line) == 0 {
+ return ""
+ }
+
+ // a heading must start with an uppercase letter
+ r, _ := utf8.DecodeRuneInString(line)
+ if !unicode.IsLetter(r) || !unicode.IsUpper(r) {
+ return ""
+ }
+
+ // it must end in a letter or digit:
+ r, _ = utf8.DecodeLastRuneInString(line)
+ if !unicode.IsLetter(r) && !unicode.IsDigit(r) {
+ return ""
+ }
+
+ // exclude lines with illegal characters. we allow "(),"
+ if strings.ContainsAny(line, ";:!?+*/=[]{}_^°&§~%#@<\">\\") {
+ return ""
+ }
+
+ // allow "'" for possessive "'s" only
+ for b := line; ; {
+ i := strings.IndexRune(b, '\'')
+ if i < 0 {
+ break
+ }
+ if i+1 >= len(b) || b[i+1] != 's' || (i+2 < len(b) && b[i+2] != ' ') {
+ return "" // not followed by "s "
+ }
+ b = b[i+2:]
+ }
+
+ // allow "." when followed by non-space
+ for b := line; ; {
+ i := strings.IndexRune(b, '.')
+ if i < 0 {
+ break
+ }
+ if i+1 >= len(b) || b[i+1] == ' ' {
+ return "" // not followed by non-space
+ }
+ b = b[i+1:]
+ }
+
+ return line
+}
+
+type op int
+
+const (
+ opPara op = iota
+ opHead
+ opPre
+)
+
+type block struct {
+ op op
+ lines []string
+}
+
+var nonAlphaNumRx = lazyregexp.New(`[^a-zA-Z0-9]`)
+
+func anchorID(line string) string {
+ // 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.
+// The comment was prepared by DocReader,
+// so it is known not to have leading, trailing blank lines
+// nor to have trailing spaces at the end of lines.
+// The comment markers have already been removed.
+//
+// Each span of unindented non-blank lines is converted into
+// a single paragraph. There is one exception to the rule: a span that
+// consists of a single line, is followed by another paragraph span,
+// begins with a capital letter, and contains no punctuation
+// other than parentheses and commas is formatted as a heading.
+//
+// A span of indented lines is converted into a <pre> block,
+// with the common indent prefix removed.
+//
+// URLs in the comment text are converted into links; if the URL also appears
+// in the words map, the link is taken from the map (if the corresponding map
+// value is the empty string, the URL is not converted into a link).
+//
+// A pair of (consecutive) backticks (`) is converted to a unicode left quote (“), and a pair of (consecutive)
+// single quotes (') is converted to a unicode right quote (”).
+//
+// Go identifiers that appear in the words map are italicized; if the corresponding
+// map value is not the empty string, it is considered a URL and the word is converted
+// into a link.
+func ToHTML(w io.Writer, text string, words map[string]string) {
+ for _, b := range blocks(text) {
+ switch b.op {
+ case opPara:
+ w.Write(html_p)
+ for _, line := range b.lines {
+ emphasize(w, line, words, true)
+ }
+ w.Write(html_endp)
+ case opHead:
+ w.Write(html_h)
+ id := ""
+ for _, line := range b.lines {
+ if id == "" {
+ id = anchorID(line)
+ w.Write([]byte(id))
+ w.Write(html_hq)
+ }
+ commentEscape(w, line, true)
+ }
+ if id == "" {
+ w.Write(html_hq)
+ }
+ w.Write(html_endh)
+ case opPre:
+ w.Write(html_pre)
+ for _, line := range b.lines {
+ emphasize(w, line, nil, false)
+ }
+ w.Write(html_endpre)
+ }
+ }
+}
+
+func blocks(text string) []block {
+ var (
+ out []block
+ para []string
+
+ lastWasBlank = false
+ lastWasHeading = false
+ )
+
+ close := func() {
+ if para != nil {
+ out = append(out, block{opPara, para})
+ para = nil
+ }
+ }
+
+ lines := strings.SplitAfter(text, "\n")
+ unindent(lines)
+ for i := 0; i < len(lines); {
+ line := lines[i]
+ if isBlank(line) {
+ // close paragraph
+ close()
+ i++
+ lastWasBlank = true
+ continue
+ }
+ if indentLen(line) > 0 {
+ // close paragraph
+ close()
+
+ // count indented or blank lines
+ j := i + 1
+ for j < len(lines) && (isBlank(lines[j]) || indentLen(lines[j]) > 0) {
+ j++
+ }
+ // but not trailing blank lines
+ for j > i && isBlank(lines[j-1]) {
+ j--
+ }
+ pre := lines[i:j]
+ i = j
+
+ unindent(pre)
+
+ // put those lines in a pre block
+ out = append(out, block{opPre, pre})
+ lastWasHeading = false
+ continue
+ }
+
+ if lastWasBlank && !lastWasHeading && i+2 < len(lines) &&
+ isBlank(lines[i+1]) && !isBlank(lines[i+2]) && indentLen(lines[i+2]) == 0 {
+ // current line is non-blank, surrounded by blank lines
+ // and the next non-blank line is not indented: this
+ // might be a heading.
+ if head := heading(line); head != "" {
+ close()
+ out = append(out, block{opHead, []string{head}})
+ i += 2
+ lastWasHeading = true
+ continue
+ }
+ }
+
+ // open paragraph
+ lastWasBlank = false
+ lastWasHeading = false
+ para = append(para, lines[i])
+ i++
+ }
+ close()
+
+ return out
+}
+
+// ToText prepares comment text for presentation in textual output.
+// It wraps paragraphs of text to width or fewer Unicode code points
+// and then prefixes each line with the indent. In preformatted sections
+// (such as program text), it prefixes each non-blank line with preIndent.
+//
+// A pair of (consecutive) backticks (`) is converted to a unicode left quote (“), and a pair of (consecutive)
+// single quotes (') is converted to a unicode right quote (”).
+func ToText(w io.Writer, text string, indent, preIndent string, width int) {
+ l := lineWrapper{
+ out: w,
+ width: width,
+ indent: indent,
+ }
+ for _, b := range blocks(text) {
+ switch b.op {
+ case opPara:
+ // l.write will add leading newline if required
+ for _, line := range b.lines {
+ line = convertQuotes(line)
+ l.write(line)
+ }
+ l.flush()
+ case opHead:
+ w.Write(nl)
+ for _, line := range b.lines {
+ line = convertQuotes(line)
+ l.write(line + "\n")
+ }
+ l.flush()
+ case opPre:
+ w.Write(nl)
+ for _, line := range b.lines {
+ if isBlank(line) {
+ w.Write([]byte("\n"))
+ } else {
+ w.Write([]byte(preIndent))
+ w.Write([]byte(line))
+ }
+ }
+ }
+ }
+}
+
+type lineWrapper struct {
+ out io.Writer
+ printed bool
+ width int
+ indent string
+ n int
+ pendSpace int
+}
+
+var nl = []byte("\n")
+var space = []byte(" ")
+var prefix = []byte("// ")
+
+func (l *lineWrapper) write(text string) {
+ if l.n == 0 && l.printed {
+ l.out.Write(nl) // blank line before new paragraph
+ }
+ l.printed = true
+
+ needsPrefix := false
+ isComment := strings.HasPrefix(text, "//")
+ for _, f := range strings.Fields(text) {
+ w := utf8.RuneCountInString(f)
+ // wrap if line is too long
+ if l.n > 0 && l.n+l.pendSpace+w > l.width {
+ l.out.Write(nl)
+ l.n = 0
+ l.pendSpace = 0
+ needsPrefix = isComment && !strings.HasPrefix(f, "//")
+ }
+ if l.n == 0 {
+ l.out.Write([]byte(l.indent))
+ }
+ if needsPrefix {
+ l.out.Write(prefix)
+ needsPrefix = false
+ }
+ l.out.Write(space[:l.pendSpace])
+ l.out.Write([]byte(f))
+ l.n += l.pendSpace + w
+ l.pendSpace = 1
+ }
+}
+
+func (l *lineWrapper) flush() {
+ if l.n == 0 {
+ return
+ }
+ l.out.Write(nl)
+ l.pendSpace = 0
+ l.n = 0
+}
diff --git a/src/go/doc/comment_test.go b/src/go/doc/comment_test.go
new file mode 100644
index 0000000..6d1b209
--- /dev/null
+++ b/src/go/doc/comment_test.go
@@ -0,0 +1,247 @@
+// Copyright 2011 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
+
+import (
+ "bytes"
+ "reflect"
+ "strings"
+ "testing"
+)
+
+var headingTests = []struct {
+ line string
+ ok bool
+}{
+ {"Section", true},
+ {"A typical usage", true},
+ {"ΔΛΞ is Greek", true},
+ {"Foo 42", true},
+ {"", false},
+ {"section", false},
+ {"A typical usage:", false},
+ {"This code:", false},
+ {"δ is Greek", false},
+ {"Foo §", false},
+ {"Fermat's Last Sentence", true},
+ {"Fermat's", true},
+ {"'sX", false},
+ {"Ted 'Too' Bar", false},
+ {"Use n+m", false},
+ {"Scanning:", false},
+ {"N:M", false},
+}
+
+func TestIsHeading(t *testing.T) {
+ for _, tt := range headingTests {
+ if h := heading(tt.line); (len(h) > 0) != tt.ok {
+ t.Errorf("isHeading(%q) = %v, want %v", tt.line, h, tt.ok)
+ }
+ }
+}
+
+var blocksTests = []struct {
+ in string
+ out []block
+ text string
+}{
+ {
+ in: `Para 1.
+Para 1 line 2.
+
+Para 2.
+
+Section
+
+Para 3.
+
+ pre
+ pre1
+
+Para 4.
+
+ pre
+ pre1
+
+ pre2
+
+Para 5.
+
+
+ pre
+
+
+ pre1
+ pre2
+
+Para 6.
+ pre
+ pre2
+`,
+ out: []block{
+ {opPara, []string{"Para 1.\n", "Para 1 line 2.\n"}},
+ {opPara, []string{"Para 2.\n"}},
+ {opHead, []string{"Section"}},
+ {opPara, []string{"Para 3.\n"}},
+ {opPre, []string{"pre\n", "pre1\n"}},
+ {opPara, []string{"Para 4.\n"}},
+ {opPre, []string{"pre\n", "pre1\n", "\n", "pre2\n"}},
+ {opPara, []string{"Para 5.\n"}},
+ {opPre, []string{"pre\n", "\n", "\n", "pre1\n", "pre2\n"}},
+ {opPara, []string{"Para 6.\n"}},
+ {opPre, []string{"pre\n", "pre2\n"}},
+ },
+ text: `. Para 1. Para 1 line 2.
+
+. Para 2.
+
+
+. Section
+
+. Para 3.
+
+$ pre
+$ pre1
+
+. Para 4.
+
+$ pre
+$ pre1
+
+$ pre2
+
+. Para 5.
+
+$ pre
+
+
+$ pre1
+$ pre2
+
+. Para 6.
+
+$ pre
+$ pre2
+`,
+ },
+ {
+ in: "Para.\n\tshould not be ``escaped''",
+ out: []block{
+ {opPara, []string{"Para.\n"}},
+ {opPre, []string{"should not be ``escaped''"}},
+ },
+ text: ". Para.\n\n$ should not be ``escaped''",
+ },
+ {
+ in: "// A very long line of 46 char for line wrapping.",
+ out: []block{
+ {opPara, []string{"// A very long line of 46 char for line wrapping."}},
+ },
+ text: `. // A very long line of 46 char for line
+. // wrapping.
+`,
+ },
+ {
+ in: `/* A very long line of 46 char for line wrapping.
+A very long line of 46 char for line wrapping. */`,
+ out: []block{
+ {opPara, []string{"/* A very long line of 46 char for line wrapping.\n", "A very long line of 46 char for line wrapping. */"}},
+ },
+ text: `. /* A very long line of 46 char for line
+. wrapping. A very long line of 46 char
+. for line wrapping. */
+`,
+ },
+ {
+ in: `A line of 36 char for line wrapping.
+//Another line starting with //`,
+ out: []block{
+ {opPara, []string{"A line of 36 char for line wrapping.\n",
+ "//Another line starting with //"}},
+ },
+ text: `. A line of 36 char for line wrapping.
+. //Another line starting with //
+`,
+ },
+}
+
+func TestBlocks(t *testing.T) {
+ for i, tt := range blocksTests {
+ b := blocks(tt.in)
+ if !reflect.DeepEqual(b, tt.out) {
+ t.Errorf("#%d: mismatch\nhave: %v\nwant: %v", i, b, tt.out)
+ }
+ }
+}
+
+func TestToText(t *testing.T) {
+ var buf bytes.Buffer
+ for i, tt := range blocksTests {
+ ToText(&buf, tt.in, ". ", "$\t", 40)
+ if have := buf.String(); have != tt.text {
+ t.Errorf("#%d: mismatch\nhave: %s\nwant: %s\nhave vs want:\n%q\n%q", i, have, tt.text, have, tt.text)
+ }
+ buf.Reset()
+ }
+}
+
+var emphasizeTests = []struct {
+ in, out string
+}{
+ {"", ""},
+ {"http://[::1]:8080/foo.txt", `<a href="http://[::1]:8080/foo.txt">http://[::1]:8080/foo.txt</a>`},
+ {"before (https://www.google.com) after", `before (<a href="https://www.google.com">https://www.google.com</a>) after`},
+ {"before https://www.google.com:30/x/y/z:b::c. After", `before <a href="https://www.google.com:30/x/y/z:b::c">https://www.google.com:30/x/y/z:b::c</a>. After`},
+ {"http://www.google.com/path/:;!-/?query=%34b#093124", `<a href="http://www.google.com/path/:;!-/?query=%34b#093124">http://www.google.com/path/:;!-/?query=%34b#093124</a>`},
+ {"http://www.google.com/path/:;!-/?query=%34bar#093124", `<a href="http://www.google.com/path/:;!-/?query=%34bar#093124">http://www.google.com/path/:;!-/?query=%34bar#093124</a>`},
+ {"http://www.google.com/index.html! After", `<a href="http://www.google.com/index.html">http://www.google.com/index.html</a>! After`},
+ {"http://www.google.com/", `<a href="http://www.google.com/">http://www.google.com/</a>`},
+ {"https://www.google.com/", `<a href="https://www.google.com/">https://www.google.com/</a>`},
+ {"http://www.google.com/path.", `<a href="http://www.google.com/path">http://www.google.com/path</a>.`},
+ {"http://en.wikipedia.org/wiki/Camellia_(cipher)", `<a href="http://en.wikipedia.org/wiki/Camellia_(cipher)">http://en.wikipedia.org/wiki/Camellia_(cipher)</a>`},
+ {"(http://www.google.com/)", `(<a href="http://www.google.com/">http://www.google.com/</a>)`},
+ {"http://gmail.com)", `<a href="http://gmail.com">http://gmail.com</a>)`},
+ {"((http://gmail.com))", `((<a href="http://gmail.com">http://gmail.com</a>))`},
+ {"http://gmail.com ((http://gmail.com)) ()", `<a href="http://gmail.com">http://gmail.com</a> ((<a href="http://gmail.com">http://gmail.com</a>)) ()`},
+ {"Foo bar http://example.com/ quux!", `Foo bar <a href="http://example.com/">http://example.com/</a> quux!`},
+ {"Hello http://example.com/%2f/ /world.", `Hello <a href="http://example.com/%2f/">http://example.com/%2f/</a> /world.`},
+ {"Lorem http: ipsum //host/path", "Lorem http: ipsum //host/path"},
+ {"javascript://is/not/linked", "javascript://is/not/linked"},
+ {"http://foo", `<a href="http://foo">http://foo</a>`},
+ {"art by [[https://www.example.com/person/][Person Name]]", `art by [[<a href="https://www.example.com/person/">https://www.example.com/person/</a>][Person Name]]`},
+ {"please visit (http://golang.org/)", `please visit (<a href="http://golang.org/">http://golang.org/</a>)`},
+ {"please visit http://golang.org/hello())", `please visit <a href="http://golang.org/hello()">http://golang.org/hello()</a>)`},
+ {"http://git.qemu.org/?p=qemu.git;a=blob;f=qapi-schema.json;hb=HEAD", `<a href="http://git.qemu.org/?p=qemu.git;a=blob;f=qapi-schema.json;hb=HEAD">http://git.qemu.org/?p=qemu.git;a=blob;f=qapi-schema.json;hb=HEAD</a>`},
+ {"https://foo.bar/bal/x(])", `<a href="https://foo.bar/bal/x(">https://foo.bar/bal/x(</a>])`}, // inner ] causes (]) to be cut off from URL
+ {"foo [ http://bar(])", `foo [ <a href="http://bar(">http://bar(</a>])`}, // outer [ causes ]) to be cut off from URL
+}
+
+func TestEmphasize(t *testing.T) {
+ for i, tt := range emphasizeTests {
+ var buf bytes.Buffer
+ emphasize(&buf, tt.in, nil, true)
+ out := buf.String()
+ if out != tt.out {
+ t.Errorf("#%d: mismatch\nhave: %v\nwant: %v", i, out, tt.out)
+ }
+ }
+}
+
+func TestCommentEscape(t *testing.T) {
+ commentTests := []struct {
+ in, out string
+ }{
+ {"typically invoked as ``go tool asm'',", "typically invoked as " + ldquo + "go tool asm" + rdquo + ","},
+ {"For more detail, run ``go help test'' and ``go help testflag''", "For more detail, run " + ldquo + "go help test" + rdquo + " and " + ldquo + "go help testflag" + rdquo},
+ }
+ for i, tt := range commentTests {
+ var buf strings.Builder
+ commentEscape(&buf, tt.in, true)
+ out := buf.String()
+ if out != tt.out {
+ t.Errorf("#%d: mismatch\nhave: %q\nwant: %q", i, out, tt.out)
+ }
+ }
+}
diff --git a/src/go/doc/doc.go b/src/go/doc/doc.go
new file mode 100644
index 0000000..79d3899
--- /dev/null
+++ b/src/go/doc/doc.go
@@ -0,0 +1,222 @@
+// Copyright 2009 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 extracts source code documentation from a Go AST.
+package doc
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+ "strings"
+)
+
+// Package is the documentation for an entire package.
+type Package struct {
+ Doc string
+ Name string
+ ImportPath string
+ Imports []string
+ Filenames []string
+ Notes map[string][]*Note
+
+ // Deprecated: For backward compatibility Bugs is still populated,
+ // but all new code should use Notes instead.
+ Bugs []string
+
+ // declarations
+ Consts []*Value
+ Types []*Type
+ Vars []*Value
+ Funcs []*Func
+
+ // Examples is a sorted list of examples associated with
+ // the package. Examples are extracted from _test.go files
+ // provided to NewFromFiles.
+ Examples []*Example
+}
+
+// Value is the documentation for a (possibly grouped) var or const declaration.
+type Value struct {
+ Doc string
+ Names []string // var or const names in declaration order
+ Decl *ast.GenDecl
+
+ order int
+}
+
+// Type is the documentation for a type declaration.
+type Type struct {
+ Doc string
+ Name string
+ Decl *ast.GenDecl
+
+ // associated declarations
+ Consts []*Value // sorted list of constants of (mostly) this type
+ Vars []*Value // sorted list of variables of (mostly) this type
+ Funcs []*Func // sorted list of functions returning this type
+ Methods []*Func // sorted list of methods (including embedded ones) of this type
+
+ // Examples is a sorted list of examples associated with
+ // this type. Examples are extracted from _test.go files
+ // provided to NewFromFiles.
+ Examples []*Example
+}
+
+// Func is the documentation for a func declaration.
+type Func struct {
+ Doc string
+ Name string
+ Decl *ast.FuncDecl
+
+ // methods
+ // (for functions, these fields have the respective zero value)
+ Recv string // actual receiver "T" or "*T"
+ Orig string // original receiver "T" or "*T"
+ Level int // embedding level; 0 means not embedded
+
+ // Examples is a sorted list of examples associated with this
+ // function or method. Examples are extracted from _test.go files
+ // provided to NewFromFiles.
+ Examples []*Example
+}
+
+// A Note represents a marked comment starting with "MARKER(uid): note body".
+// Any note with a marker of 2 or more upper case [A-Z] letters and a uid of
+// at least one character is recognized. The ":" following the uid is optional.
+// Notes are collected in the Package.Notes map indexed by the notes marker.
+type Note struct {
+ Pos, End token.Pos // position range of the comment containing the marker
+ UID string // uid found with the marker
+ Body string // note body text
+}
+
+// Mode values control the operation of New and NewFromFiles.
+type Mode int
+
+const (
+ // AllDecls says to extract documentation for all package-level
+ // declarations, not just exported ones.
+ AllDecls Mode = 1 << iota
+
+ // AllMethods says to show all embedded methods, not just the ones of
+ // invisible (unexported) anonymous fields.
+ AllMethods
+
+ // PreserveAST says to leave the AST unmodified. Originally, pieces of
+ // the AST such as function bodies were nil-ed out to save memory in
+ // godoc, but not all programs want that behavior.
+ PreserveAST
+)
+
+// New computes the package documentation for the given package AST.
+// New takes ownership of the AST pkg and may edit or overwrite it.
+// To have the Examples fields populated, use NewFromFiles and include
+// the package's _test.go files.
+//
+func New(pkg *ast.Package, importPath string, mode Mode) *Package {
+ var r reader
+ r.readPackage(pkg, mode)
+ r.computeMethodSets()
+ r.cleanupTypes()
+ return &Package{
+ Doc: r.doc,
+ Name: pkg.Name,
+ ImportPath: importPath,
+ Imports: sortedKeys(r.imports),
+ Filenames: r.filenames,
+ Notes: r.notes,
+ Bugs: noteBodies(r.notes["BUG"]),
+ Consts: sortedValues(r.values, token.CONST),
+ Types: sortedTypes(r.types, mode&AllMethods != 0),
+ Vars: sortedValues(r.values, token.VAR),
+ Funcs: sortedFuncs(r.funcs, true),
+ }
+}
+
+// NewFromFiles computes documentation for a package.
+//
+// The package is specified by a list of *ast.Files and corresponding
+// file set, which must not be nil.
+// NewFromFiles uses all provided files when computing documentation,
+// so it is the caller's responsibility to provide only the files that
+// match the desired build context. "go/build".Context.MatchFile can
+// be used for determining whether a file matches a build context with
+// the desired GOOS and GOARCH values, and other build constraints.
+// The import path of the package is specified by importPath.
+//
+// Examples found in _test.go files are associated with the corresponding
+// type, function, method, or the package, based on their name.
+// If the example has a suffix in its name, it is set in the
+// Example.Suffix field. Examples with malformed names are skipped.
+//
+// Optionally, a single extra argument of type Mode can be provided to
+// control low-level aspects of the documentation extraction behavior.
+//
+// NewFromFiles takes ownership of the AST files and may edit them,
+// unless the PreserveAST Mode bit is on.
+//
+func NewFromFiles(fset *token.FileSet, files []*ast.File, importPath string, opts ...interface{}) (*Package, error) {
+ // Check for invalid API usage.
+ if fset == nil {
+ panic(fmt.Errorf("doc.NewFromFiles: no token.FileSet provided (fset == nil)"))
+ }
+ var mode Mode
+ switch len(opts) { // There can only be 0 or 1 options, so a simple switch works for now.
+ case 0:
+ // Nothing to do.
+ case 1:
+ m, ok := opts[0].(Mode)
+ if !ok {
+ panic(fmt.Errorf("doc.NewFromFiles: option argument type must be doc.Mode"))
+ }
+ mode = m
+ default:
+ panic(fmt.Errorf("doc.NewFromFiles: there must not be more than 1 option argument"))
+ }
+
+ // Collect .go and _test.go files.
+ var (
+ goFiles = make(map[string]*ast.File)
+ testGoFiles []*ast.File
+ )
+ for i := range files {
+ f := fset.File(files[i].Pos())
+ if f == nil {
+ return nil, fmt.Errorf("file files[%d] is not found in the provided file set", i)
+ }
+ switch name := f.Name(); {
+ case strings.HasSuffix(name, ".go") && !strings.HasSuffix(name, "_test.go"):
+ goFiles[name] = files[i]
+ case strings.HasSuffix(name, "_test.go"):
+ testGoFiles = append(testGoFiles, files[i])
+ default:
+ return nil, fmt.Errorf("file files[%d] filename %q does not have a .go extension", i, name)
+ }
+ }
+
+ // TODO(dmitshur,gri): A relatively high level call to ast.NewPackage with a simpleImporter
+ // ast.Importer implementation is made below. It might be possible to short-circuit and simplify.
+
+ // Compute package documentation.
+ pkg, _ := ast.NewPackage(fset, goFiles, simpleImporter, nil) // Ignore errors that can happen due to unresolved identifiers.
+ p := New(pkg, importPath, mode)
+ classifyExamples(p, Examples(testGoFiles...))
+ return p, nil
+}
+
+// simpleImporter returns a (dummy) package object named by the last path
+// component of the provided package path (as is the convention for packages).
+// This is sufficient to resolve package identifiers without doing an actual
+// import. It never returns an error.
+func simpleImporter(imports map[string]*ast.Object, path string) (*ast.Object, error) {
+ pkg := imports[path]
+ if pkg == nil {
+ // note that strings.LastIndex returns -1 if there is no "/"
+ pkg = ast.NewObj(ast.Pkg, path[strings.LastIndex(path, "/")+1:])
+ pkg.Data = ast.NewScope(nil) // required by ast.NewPackage for dot-import
+ imports[path] = pkg
+ }
+ return pkg, nil
+}
diff --git a/src/go/doc/doc_test.go b/src/go/doc/doc_test.go
new file mode 100644
index 0000000..cbdca62
--- /dev/null
+++ b/src/go/doc/doc_test.go
@@ -0,0 +1,164 @@
+// Copyright 2012 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
+
+import (
+ "bytes"
+ "flag"
+ "fmt"
+ "go/ast"
+ "go/parser"
+ "go/printer"
+ "go/token"
+ "io/fs"
+ "os"
+ "path/filepath"
+ "regexp"
+ "strings"
+ "testing"
+ "text/template"
+)
+
+var update = flag.Bool("update", false, "update golden (.out) files")
+var files = flag.String("files", "", "consider only Go test files matching this regular expression")
+
+const dataDir = "testdata"
+
+var templateTxt = readTemplate("template.txt")
+
+func readTemplate(filename string) *template.Template {
+ t := template.New(filename)
+ t.Funcs(template.FuncMap{
+ "node": nodeFmt,
+ "synopsis": synopsisFmt,
+ "indent": indentFmt,
+ })
+ return template.Must(t.ParseFiles(filepath.Join(dataDir, filename)))
+}
+
+func nodeFmt(node interface{}, fset *token.FileSet) string {
+ var buf bytes.Buffer
+ printer.Fprint(&buf, fset, node)
+ return strings.ReplaceAll(strings.TrimSpace(buf.String()), "\n", "\n\t")
+}
+
+func synopsisFmt(s string) string {
+ const n = 64
+ if len(s) > n {
+ // cut off excess text and go back to a word boundary
+ s = s[0:n]
+ if i := strings.LastIndexAny(s, "\t\n "); i >= 0 {
+ s = s[0:i]
+ }
+ s = strings.TrimSpace(s) + " ..."
+ }
+ return "// " + strings.ReplaceAll(s, "\n", " ")
+}
+
+func indentFmt(indent, s string) string {
+ end := ""
+ if strings.HasSuffix(s, "\n") {
+ end = "\n"
+ s = s[:len(s)-1]
+ }
+ return indent + strings.ReplaceAll(s, "\n", "\n"+indent) + end
+}
+
+func isGoFile(fi fs.FileInfo) bool {
+ name := fi.Name()
+ return !fi.IsDir() &&
+ len(name) > 0 && name[0] != '.' && // ignore .files
+ filepath.Ext(name) == ".go"
+}
+
+type bundle struct {
+ *Package
+ FSet *token.FileSet
+}
+
+func test(t *testing.T, mode Mode) {
+ // determine file filter
+ filter := isGoFile
+ if *files != "" {
+ rx, err := regexp.Compile(*files)
+ if err != nil {
+ t.Fatal(err)
+ }
+ filter = func(fi fs.FileInfo) bool {
+ return isGoFile(fi) && rx.MatchString(fi.Name())
+ }
+ }
+
+ // get packages
+ fset := token.NewFileSet()
+ pkgs, err := parser.ParseDir(fset, dataDir, filter, parser.ParseComments)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // test packages
+ for _, pkg := range pkgs {
+ importPath := dataDir + "/" + pkg.Name
+ var files []*ast.File
+ for _, f := range pkg.Files {
+ files = append(files, f)
+ }
+ doc, err := NewFromFiles(fset, files, importPath, mode)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+
+ // golden files always use / in filenames - canonicalize them
+ for i, filename := range doc.Filenames {
+ doc.Filenames[i] = filepath.ToSlash(filename)
+ }
+
+ // print documentation
+ var buf bytes.Buffer
+ if err := templateTxt.Execute(&buf, bundle{doc, fset}); err != nil {
+ t.Error(err)
+ continue
+ }
+ got := buf.Bytes()
+
+ // update golden file if necessary
+ golden := filepath.Join(dataDir, fmt.Sprintf("%s.%d.golden", pkg.Name, mode))
+ if *update {
+ err := os.WriteFile(golden, got, 0644)
+ if err != nil {
+ t.Error(err)
+ }
+ continue
+ }
+
+ // get golden file
+ want, err := os.ReadFile(golden)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+
+ // compare
+ if !bytes.Equal(got, want) {
+ t.Errorf("package %s\n\tgot:\n%s\n\twant:\n%s", pkg.Name, got, want)
+ }
+ }
+}
+
+func Test(t *testing.T) {
+ test(t, 0)
+ test(t, AllDecls)
+ test(t, AllMethods)
+}
+
+func TestAnchorID(t *testing.T) {
+ const in = "Important Things 2 Know & Stuff"
+ const want = "hdr-Important_Things_2_Know___Stuff"
+ got := anchorID(in)
+ if got != want {
+ t.Errorf("anchorID(%q) = %q; want %q", in, got, want)
+ }
+}
diff --git a/src/go/doc/example.go b/src/go/doc/example.go
new file mode 100644
index 0000000..274000c
--- /dev/null
+++ b/src/go/doc/example.go
@@ -0,0 +1,550 @@
+// Copyright 2011 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.
+
+// Extract example functions from file ASTs.
+
+package doc
+
+import (
+ "go/ast"
+ "go/token"
+ "internal/lazyregexp"
+ "path"
+ "sort"
+ "strconv"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+)
+
+// An Example represents an example function found in a test source file.
+type Example struct {
+ Name string // name of the item being exemplified (including optional suffix)
+ Suffix string // example suffix, without leading '_' (only populated by NewFromFiles)
+ 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
+ Unordered bool
+ EmptyOutput bool // expect empty output
+ Order int // original source code order
+}
+
+// Examples returns the examples found in testFiles, sorted by Name field.
+// The Order fields record the order in which the examples were encountered.
+// The Suffix field is not populated when Examples is called directly, it is
+// only populated by NewFromFiles for examples it finds in _test.go files.
+//
+// Playable Examples must be in a package whose name ends in "_test".
+// An Example is "playable" (the Play field is non-nil) in either of these
+// circumstances:
+// - The example function is self-contained: the function references only
+// identifiers from other packages (or predeclared identifiers, such as
+// "int") and the test file does not include a dot import.
+// - The entire test file is the example: the file contains exactly one
+// example function, zero test or benchmark functions, and at least one
+// top-level function, type, variable, or constant declaration other
+// than the example function.
+func Examples(testFiles ...*ast.File) []*Example {
+ var list []*Example
+ for _, file := range testFiles {
+ hasTests := false // file contains tests or benchmarks
+ numDecl := 0 // number of non-import declarations in the file
+ var flist []*Example
+ for _, decl := range file.Decls {
+ if g, ok := decl.(*ast.GenDecl); ok && g.Tok != token.IMPORT {
+ numDecl++
+ continue
+ }
+ f, ok := decl.(*ast.FuncDecl)
+ if !ok || f.Recv != nil {
+ continue
+ }
+ numDecl++
+ name := f.Name.Name
+ if isTest(name, "Test") || isTest(name, "Benchmark") {
+ hasTests = true
+ continue
+ }
+ if !isTest(name, "Example") {
+ continue
+ }
+ if params := f.Type.Params; len(params.List) != 0 {
+ continue // function has params; not a valid example
+ }
+ if f.Body == nil { // ast.File.Body nil dereference (see issue 28044)
+ continue
+ }
+ var doc string
+ if f.Doc != nil {
+ doc = f.Doc.Text()
+ }
+ output, unordered, hasOutput := exampleOutput(f.Body, file.Comments)
+ flist = append(flist, &Example{
+ Name: name[len("Example"):],
+ Doc: doc,
+ Code: f.Body,
+ Play: playExample(file, f),
+ Comments: file.Comments,
+ Output: output,
+ Unordered: unordered,
+ EmptyOutput: output == "" && hasOutput,
+ Order: len(flist),
+ })
+ }
+ if !hasTests && numDecl > 1 && len(flist) == 1 {
+ // If this file only has one example function, some
+ // 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...)
+ }
+ // sort by name
+ sort.Slice(list, func(i, j int) bool {
+ return list[i].Name < list[j].Name
+ })
+ return list
+}
+
+var outputPrefix = lazyregexp.New(`(?i)^[[:space:]]*(unordered )?output:`)
+
+// Extracts the expected output and whether there was a valid output comment
+func exampleOutput(b *ast.BlockStmt, comments []*ast.CommentGroup) (output string, unordered, ok bool) {
+ if _, last := lastComment(b, comments); last != nil {
+ // test that it begins with the correct prefix
+ text := last.Text()
+ if loc := outputPrefix.FindStringSubmatchIndex(text); loc != nil {
+ if loc[2] != -1 {
+ unordered = true
+ }
+ 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, unordered, true
+ }
+ }
+ return "", false, false // no suitable comment found
+}
+
+// isTest tells whether name looks like a test, example, or benchmark.
+// It is a Test (say) if there is a character after Test that is not a
+// lower-case letter. (We don't want Testiness.)
+func isTest(name, prefix string) bool {
+ if !strings.HasPrefix(name, prefix) {
+ return false
+ }
+ if len(name) == len(prefix) { // "Test" is ok
+ return true
+ }
+ rune, _ := utf8.DecodeRuneInString(name[len(prefix):])
+ return !unicode.IsLower(rune)
+}
+
+// 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, f *ast.FuncDecl) *ast.File {
+ body := f.Body
+
+ if !strings.HasSuffix(file.Name.Name, "_test") {
+ // We don't support examples that are part of the
+ // greater package (yet).
+ return nil
+ }
+
+ // Collect top-level declarations in the file.
+ topDecls := make(map[*ast.Object]ast.Decl)
+ typMethods := make(map[string][]ast.Decl)
+
+ for _, decl := range file.Decls {
+ switch d := decl.(type) {
+ case *ast.FuncDecl:
+ if d.Recv == nil {
+ topDecls[d.Name.Obj] = d
+ } else {
+ if len(d.Recv.List) == 1 {
+ t := d.Recv.List[0].Type
+ tname, _ := baseTypeName(t)
+ typMethods[tname] = append(typMethods[tname], d)
+ }
+ }
+ case *ast.GenDecl:
+ for _, spec := range d.Specs {
+ switch s := spec.(type) {
+ case *ast.TypeSpec:
+ topDecls[s.Name.Obj] = d
+ case *ast.ValueSpec:
+ for _, name := range s.Names {
+ topDecls[name.Obj] = d
+ }
+ }
+ }
+ }
+ }
+
+ // Find unresolved identifiers and uses of top-level declarations.
+ unresolved := make(map[string]bool)
+ var depDecls []ast.Decl
+ hasDepDecls := make(map[ast.Decl]bool)
+
+ var inspectFunc func(ast.Node) bool
+ inspectFunc = func(n ast.Node) bool {
+ switch e := n.(type) {
+ case *ast.Ident:
+ if e.Obj == nil && e.Name != "_" {
+ unresolved[e.Name] = true
+ } else if d := topDecls[e.Obj]; d != nil {
+ if !hasDepDecls[d] {
+ hasDepDecls[d] = true
+ depDecls = append(depDecls, d)
+ }
+ }
+ return true
+ case *ast.SelectorExpr:
+ // 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".)
+ ast.Inspect(e.X, inspectFunc)
+ return false
+ case *ast.KeyValueExpr:
+ // For key value expressions, only inspect the value
+ // as the key should be resolved by the type of the
+ // composite literal.
+ ast.Inspect(e.Value, inspectFunc)
+ return false
+ }
+ return true
+ }
+ ast.Inspect(body, inspectFunc)
+ for i := 0; i < len(depDecls); i++ {
+ switch d := depDecls[i].(type) {
+ case *ast.FuncDecl:
+ // Inspect types of parameters and results. See #28492.
+ if d.Type.Params != nil {
+ for _, p := range d.Type.Params.List {
+ ast.Inspect(p.Type, inspectFunc)
+ }
+ }
+ if d.Type.Results != nil {
+ for _, r := range d.Type.Results.List {
+ ast.Inspect(r.Type, inspectFunc)
+ }
+ }
+
+ // Functions might not have a body. See #42706.
+ if d.Body != nil {
+ ast.Inspect(d.Body, inspectFunc)
+ }
+ case *ast.GenDecl:
+ for _, spec := range d.Specs {
+ switch s := spec.(type) {
+ case *ast.TypeSpec:
+ ast.Inspect(s.Type, inspectFunc)
+
+ depDecls = append(depDecls, typMethods[s.Name.Name]...)
+ case *ast.ValueSpec:
+ if s.Type != nil {
+ ast.Inspect(s.Type, inspectFunc)
+ }
+ for _, val := range s.Values {
+ ast.Inspect(val, inspectFunc)
+ }
+ }
+ }
+ }
+ }
+
+ // 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
+ }
+ if p == "syscall/js" {
+ // We don't support examples that import syscall/js,
+ // because the package syscall/js is not available in the playground.
+ return nil
+ }
+ 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 the "Output:" or "Unordered output:" comment and adjust body
+ // end position.
+ body, comments = stripOutputComment(body, comments)
+
+ // Include documentation belonging to dependent declarations.
+ for _, d := range depDecls {
+ switch d := d.(type) {
+ case *ast.GenDecl:
+ if d.Doc != nil {
+ comments = append(comments, d.Doc)
+ }
+ case *ast.FuncDecl:
+ if d.Doc != nil {
+ comments = append(comments, d.Doc)
+ }
+ }
+ }
+
+ // 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: f.Type,
+ Body: body,
+ }
+
+ decls := make([]ast.Decl, 0, 2+len(depDecls))
+ decls = append(decls, importDecl)
+ decls = append(decls, depDecls...)
+ decls = append(decls, funcDecl)
+
+ sort.Slice(decls, func(i, j int) bool {
+ return decls[i].Pos() < decls[j].Pos()
+ })
+
+ sort.Slice(comments, func(i, j int) bool {
+ return comments[i].Pos() < comments[j].Pos()
+ })
+
+ // Synthesize file.
+ return &ast.File{
+ Name: ast.NewIdent("main"),
+ Decls: decls,
+ 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 the "Output:" or "Unordered output:"
+// comment 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 there is no "Output:" or "Unordered output:" comment.
+ 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) {
+ if b == nil {
+ return
+ }
+ 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
+}
+
+// classifyExamples classifies examples and assigns them to the Examples field
+// of the relevant Func, Type, or Package that the example is associated with.
+//
+// The classification process is ambiguous in some cases:
+//
+// - ExampleFoo_Bar matches a type named Foo_Bar
+// or a method named Foo.Bar.
+// - ExampleFoo_bar matches a type named Foo_bar
+// or Foo (with a "bar" suffix).
+//
+// Examples with malformed names are not associated with anything.
+//
+func classifyExamples(p *Package, examples []*Example) {
+ if len(examples) == 0 {
+ return
+ }
+
+ // Mapping of names for funcs, types, and methods to the example listing.
+ ids := make(map[string]*[]*Example)
+ ids[""] = &p.Examples // package-level examples have an empty name
+ for _, f := range p.Funcs {
+ if !token.IsExported(f.Name) {
+ continue
+ }
+ ids[f.Name] = &f.Examples
+ }
+ for _, t := range p.Types {
+ if !token.IsExported(t.Name) {
+ continue
+ }
+ ids[t.Name] = &t.Examples
+ for _, f := range t.Funcs {
+ if !token.IsExported(f.Name) {
+ continue
+ }
+ ids[f.Name] = &f.Examples
+ }
+ for _, m := range t.Methods {
+ if !token.IsExported(m.Name) {
+ continue
+ }
+ ids[strings.TrimPrefix(m.Recv, "*")+"_"+m.Name] = &m.Examples
+ }
+ }
+
+ // Group each example with the associated func, type, or method.
+ for _, ex := range examples {
+ // Consider all possible split points for the suffix
+ // by starting at the end of string (no suffix case),
+ // then trying all positions that contain a '_' character.
+ //
+ // An association is made on the first successful match.
+ // Examples with malformed names that match nothing are skipped.
+ for i := len(ex.Name); i >= 0; i = strings.LastIndexByte(ex.Name[:i], '_') {
+ prefix, suffix, ok := splitExampleName(ex.Name, i)
+ if !ok {
+ continue
+ }
+ exs, ok := ids[prefix]
+ if !ok {
+ continue
+ }
+ ex.Suffix = suffix
+ *exs = append(*exs, ex)
+ break
+ }
+ }
+
+ // Sort list of example according to the user-specified suffix name.
+ for _, exs := range ids {
+ sort.Slice((*exs), func(i, j int) bool {
+ return (*exs)[i].Suffix < (*exs)[j].Suffix
+ })
+ }
+}
+
+// splitExampleName attempts to split example name s at index i,
+// and reports if that produces a valid split. The suffix may be
+// absent. Otherwise, it must start with a lower-case letter and
+// be preceded by '_'.
+//
+// One of i == len(s) or s[i] == '_' must be true.
+func splitExampleName(s string, i int) (prefix, suffix string, ok bool) {
+ if i == len(s) {
+ return s, "", true
+ }
+ if i == len(s)-1 {
+ return "", "", false
+ }
+ prefix, suffix = s[:i], s[i+1:]
+ return prefix, suffix, isExampleSuffix(suffix)
+}
+
+func isExampleSuffix(s string) bool {
+ r, size := utf8.DecodeRuneInString(s)
+ return size > 0 && unicode.IsLower(r)
+}
diff --git a/src/go/doc/example_test.go b/src/go/doc/example_test.go
new file mode 100644
index 0000000..cf1b702
--- /dev/null
+++ b/src/go/doc/example_test.go
@@ -0,0 +1,743 @@
+// 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"
+ "fmt"
+ "go/ast"
+ "go/doc"
+ "go/format"
+ "go/parser"
+ "go/token"
+ "reflect"
+ "strings"
+ "testing"
+)
+
+const exampleTestFile = `
+package foo_test
+
+import (
+ "flag"
+ "fmt"
+ "log"
+ "sort"
+ "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)
+}
+
+func ExampleKeyValue() {
+ v := struct {
+ a string
+ b int
+ }{
+ a: "A",
+ b: 1,
+ }
+ fmt.Print(v)
+ // Output: a: "A", b: 1
+}
+
+func ExampleKeyValueImport() {
+ f := flag.Flag{
+ Name: "play",
+ }
+ fmt.Print(f)
+ // Output: Name: "play"
+}
+
+var keyValueTopDecl = struct {
+ a string
+ b int
+}{
+ a: "B",
+ b: 2,
+}
+
+func ExampleKeyValueTopDecl() {
+ fmt.Print(keyValueTopDecl)
+ // Output: a: "B", b: 2
+}
+
+// Person represents a person by name and age.
+type Person struct {
+ Name string
+ Age int
+}
+
+// String returns a string representation of the Person.
+func (p Person) String() string {
+ return fmt.Sprintf("%s: %d", p.Name, p.Age)
+}
+
+// ByAge implements sort.Interface for []Person based on
+// the Age field.
+type ByAge []Person
+
+// Len returns the number of elements in ByAge.
+func (a (ByAge)) Len() int { return len(a) }
+
+// Swap swaps the elements in ByAge.
+func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
+
+// people is the array of Person
+var people = []Person{
+ {"Bob", 31},
+ {"John", 42},
+ {"Michael", 17},
+ {"Jenny", 26},
+}
+
+func ExampleSort() {
+ fmt.Println(people)
+ sort.Sort(ByAge(people))
+ fmt.Println(people)
+ // Output:
+ // [Bob: 31 John: 42 Michael: 17 Jenny: 26]
+ // [Michael: 17 Jenny: 26 Bob: 31 John: 42]
+}
+`
+
+var exampleTestCases = []struct {
+ Name, Play, Output string
+}{
+ {
+ Name: "Hello",
+ Play: exampleHelloPlay,
+ Output: "Hello, world!\n",
+ },
+ {
+ Name: "Import",
+ Play: exampleImportPlay,
+ },
+ {
+ Name: "KeyValue",
+ Play: exampleKeyValuePlay,
+ Output: "a: \"A\", b: 1\n",
+ },
+ {
+ Name: "KeyValueImport",
+ Play: exampleKeyValueImportPlay,
+ Output: "Name: \"play\"\n",
+ },
+ {
+ Name: "KeyValueTopDecl",
+ Play: exampleKeyValueTopDeclPlay,
+ Output: "a: \"B\", b: 2\n",
+ },
+ {
+ Name: "Sort",
+ Play: exampleSortPlay,
+ Output: "[Bob: 31 John: 42 Michael: 17 Jenny: 26]\n[Michael: 17 Jenny: 26 Bob: 31 John: 42]\n",
+ },
+}
+
+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)
+}
+`
+
+const exampleKeyValuePlay = `package main
+
+import (
+ "fmt"
+)
+
+func main() {
+ v := struct {
+ a string
+ b int
+ }{
+ a: "A",
+ b: 1,
+ }
+ fmt.Print(v)
+}
+`
+
+const exampleKeyValueImportPlay = `package main
+
+import (
+ "flag"
+ "fmt"
+)
+
+func main() {
+ f := flag.Flag{
+ Name: "play",
+ }
+ fmt.Print(f)
+}
+`
+
+const exampleKeyValueTopDeclPlay = `package main
+
+import (
+ "fmt"
+)
+
+var keyValueTopDecl = struct {
+ a string
+ b int
+}{
+ a: "B",
+ b: 2,
+}
+
+func main() {
+ fmt.Print(keyValueTopDecl)
+}
+`
+
+const exampleSortPlay = `package main
+
+import (
+ "fmt"
+ "sort"
+)
+
+// Person represents a person by name and age.
+type Person struct {
+ Name string
+ Age int
+}
+
+// String returns a string representation of the Person.
+func (p Person) String() string {
+ return fmt.Sprintf("%s: %d", p.Name, p.Age)
+}
+
+// ByAge implements sort.Interface for []Person based on
+// the Age field.
+type ByAge []Person
+
+// Len returns the number of elements in ByAge.
+func (a ByAge) Len() int { return len(a) }
+
+// Swap swaps the elements in ByAge.
+func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
+
+// people is the array of Person
+var people = []Person{
+ {"Bob", 31},
+ {"John", 42},
+ {"Michael", 17},
+ {"Jenny", 26},
+}
+
+func main() {
+ fmt.Println(people)
+ sort.Sort(ByAge(people))
+ fmt.Println(people)
+}
+`
+
+func TestExamples(t *testing.T) {
+ fset := token.NewFileSet()
+ file, err := parser.ParseFile(fset, "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 != "" {
+ g := formatFile(t, fset, e.Play)
+ 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)
+ }
+ }
+}
+
+const exampleWholeFile = `package foo_test
+
+type X int
+
+func (X) Foo() {
+}
+
+func (X) TestBlah() {
+}
+
+func (X) BenchmarkFoo() {
+}
+
+func Example() {
+ fmt.Println("Hello, world!")
+ // Output: Hello, world!
+}
+`
+
+const exampleWholeFileOutput = `package main
+
+type X int
+
+func (X) Foo() {
+}
+
+func (X) TestBlah() {
+}
+
+func (X) BenchmarkFoo() {
+}
+
+func main() {
+ fmt.Println("Hello, world!")
+}
+`
+
+const exampleWholeFileFunction = `package foo_test
+
+func Foo(x int) {
+}
+
+func Example() {
+ fmt.Println("Hello, world!")
+ // Output: Hello, world!
+}
+`
+
+const exampleWholeFileFunctionOutput = `package main
+
+func Foo(x int) {
+}
+
+func main() {
+ fmt.Println("Hello, world!")
+}
+`
+
+const exampleWholeFileExternalFunction = `package foo_test
+
+func foo(int)
+
+func Example() {
+ foo(42)
+ // Output:
+}
+`
+
+const exampleWholeFileExternalFunctionOutput = `package main
+
+func foo(int)
+
+func main() {
+ foo(42)
+}
+`
+
+var exampleWholeFileTestCases = []struct {
+ Title, Source, Play, Output string
+}{
+ {
+ "Methods",
+ exampleWholeFile,
+ exampleWholeFileOutput,
+ "Hello, world!\n",
+ },
+ {
+ "Function",
+ exampleWholeFileFunction,
+ exampleWholeFileFunctionOutput,
+ "Hello, world!\n",
+ },
+ {
+ "ExternalFunction",
+ exampleWholeFileExternalFunction,
+ exampleWholeFileExternalFunctionOutput,
+ "",
+ },
+}
+
+func TestExamplesWholeFile(t *testing.T) {
+ for _, c := range exampleWholeFileTestCases {
+ fset := token.NewFileSet()
+ file, err := parser.ParseFile(fset, "test.go", strings.NewReader(c.Source), parser.ParseComments)
+ if err != nil {
+ t.Fatal(err)
+ }
+ es := doc.Examples(file)
+ if len(es) != 1 {
+ t.Fatalf("%s: wrong number of examples; got %d want 1", c.Title, len(es))
+ }
+ e := es[0]
+ if e.Name != "" {
+ t.Errorf("%s: got Name == %q, want %q", c.Title, e.Name, "")
+ }
+ if g, w := formatFile(t, fset, e.Play), c.Play; g != w {
+ t.Errorf("%s: got Play == %q, want %q", c.Title, g, w)
+ }
+ if g, w := e.Output, c.Output; g != w {
+ t.Errorf("%s: got Output == %q, want %q", c.Title, g, w)
+ }
+ }
+}
+
+const exampleInspectSignature = `package foo_test
+
+import (
+ "bytes"
+ "io"
+)
+
+func getReader() io.Reader { return nil }
+
+func do(b bytes.Reader) {}
+
+func Example() {
+ getReader()
+ do()
+ // Output:
+}
+
+func ExampleIgnored() {
+}
+`
+
+const exampleInspectSignatureOutput = `package main
+
+import (
+ "bytes"
+ "io"
+)
+
+func getReader() io.Reader { return nil }
+
+func do(b bytes.Reader) {}
+
+func main() {
+ getReader()
+ do()
+}
+`
+
+func TestExampleInspectSignature(t *testing.T) {
+ // Verify that "bytes" and "io" are imported. See issue #28492.
+ fset := token.NewFileSet()
+ file, err := parser.ParseFile(fset, "test.go", strings.NewReader(exampleInspectSignature), parser.ParseComments)
+ if err != nil {
+ t.Fatal(err)
+ }
+ es := doc.Examples(file)
+ if len(es) != 2 {
+ t.Fatalf("wrong number of examples; got %d want 2", len(es))
+ }
+ // We are interested in the first example only.
+ e := es[0]
+ if e.Name != "" {
+ t.Errorf("got Name == %q, want %q", e.Name, "")
+ }
+ if g, w := formatFile(t, fset, e.Play), exampleInspectSignatureOutput; g != w {
+ t.Errorf("got Play == %q, want %q", g, w)
+ }
+ if g, w := e.Output, ""; g != w {
+ t.Errorf("got Output == %q, want %q", g, w)
+ }
+}
+
+const exampleEmpty = `
+package p
+func Example() {}
+func Example_a()
+`
+
+const exampleEmptyOutput = `package main
+
+func main() {}
+func main()
+`
+
+func TestExampleEmpty(t *testing.T) {
+ fset := token.NewFileSet()
+ file, err := parser.ParseFile(fset, "test.go", strings.NewReader(exampleEmpty), parser.ParseComments)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ es := doc.Examples(file)
+ if len(es) != 1 {
+ t.Fatalf("wrong number of examples; got %d want 1", len(es))
+ }
+ e := es[0]
+ if e.Name != "" {
+ t.Errorf("got Name == %q, want %q", e.Name, "")
+ }
+ if g, w := formatFile(t, fset, e.Play), exampleEmptyOutput; g != w {
+ t.Errorf("got Play == %q, want %q", g, w)
+ }
+ if g, w := e.Output, ""; g != w {
+ t.Errorf("got Output == %q, want %q", g, w)
+ }
+}
+
+func formatFile(t *testing.T, fset *token.FileSet, n *ast.File) string {
+ if n == nil {
+ return "<nil>"
+ }
+ var buf bytes.Buffer
+ if err := format.Node(&buf, fset, n); err != nil {
+ t.Fatal(err)
+ }
+ return buf.String()
+}
+
+// This example illustrates how to use NewFromFiles
+// to compute package documentation with examples.
+func ExampleNewFromFiles() {
+ // src and test are two source files that make up
+ // a package whose documentation will be computed.
+ const src = `
+// This is the package comment.
+package p
+
+import "fmt"
+
+// This comment is associated with the Greet function.
+func Greet(who string) {
+ fmt.Printf("Hello, %s!\n", who)
+}
+`
+ const test = `
+package p_test
+
+// This comment is associated with the ExampleGreet_world example.
+func ExampleGreet_world() {
+ Greet("world")
+}
+`
+
+ // Create the AST by parsing src and test.
+ fset := token.NewFileSet()
+ files := []*ast.File{
+ mustParse(fset, "src.go", src),
+ mustParse(fset, "src_test.go", test),
+ }
+
+ // Compute package documentation with examples.
+ p, err := doc.NewFromFiles(fset, files, "example.com/p")
+ if err != nil {
+ panic(err)
+ }
+
+ fmt.Printf("package %s - %s", p.Name, p.Doc)
+ fmt.Printf("func %s - %s", p.Funcs[0].Name, p.Funcs[0].Doc)
+ fmt.Printf(" ⤷ example with suffix %q - %s", p.Funcs[0].Examples[0].Suffix, p.Funcs[0].Examples[0].Doc)
+
+ // Output:
+ // package p - This is the package comment.
+ // func Greet - This comment is associated with the Greet function.
+ // ⤷ example with suffix "world" - This comment is associated with the ExampleGreet_world example.
+}
+
+func TestClassifyExamples(t *testing.T) {
+ const src = `
+package p
+
+const Const1 = 0
+var Var1 = 0
+
+type (
+ Type1 int
+ Type1_Foo int
+ Type1_foo int
+ type2 int
+
+ Embed struct { Type1 }
+ Uembed struct { type2 }
+)
+
+func Func1() {}
+func Func1_Foo() {}
+func Func1_foo() {}
+func func2() {}
+
+func (Type1) Func1() {}
+func (Type1) Func1_Foo() {}
+func (Type1) Func1_foo() {}
+func (Type1) func2() {}
+
+func (type2) Func1() {}
+
+type (
+ Conflict int
+ Conflict_Conflict int
+ Conflict_conflict int
+)
+
+func (Conflict) Conflict() {}
+`
+ const test = `
+package p_test
+
+func ExampleConst1() {} // invalid - no support for consts and vars
+func ExampleVar1() {} // invalid - no support for consts and vars
+
+func Example() {}
+func Example_() {} // invalid - suffix must start with a lower-case letter
+func Example_suffix() {}
+func Example_suffix_xX_X_x() {}
+func Example_世界() {} // invalid - suffix must start with a lower-case letter
+func Example_123() {} // invalid - suffix must start with a lower-case letter
+func Example_BadSuffix() {} // invalid - suffix must start with a lower-case letter
+
+func ExampleType1() {}
+func ExampleType1_() {} // invalid - suffix must start with a lower-case letter
+func ExampleType1_suffix() {}
+func ExampleType1_BadSuffix() {} // invalid - suffix must start with a lower-case letter
+func ExampleType1_Foo() {}
+func ExampleType1_Foo_suffix() {}
+func ExampleType1_Foo_BadSuffix() {} // invalid - suffix must start with a lower-case letter
+func ExampleType1_foo() {}
+func ExampleType1_foo_suffix() {}
+func ExampleType1_foo_Suffix() {} // matches Type1, instead of Type1_foo
+func Exampletype2() {} // invalid - cannot match unexported
+
+func ExampleFunc1() {}
+func ExampleFunc1_() {} // invalid - suffix must start with a lower-case letter
+func ExampleFunc1_suffix() {}
+func ExampleFunc1_BadSuffix() {} // invalid - suffix must start with a lower-case letter
+func ExampleFunc1_Foo() {}
+func ExampleFunc1_Foo_suffix() {}
+func ExampleFunc1_Foo_BadSuffix() {} // invalid - suffix must start with a lower-case letter
+func ExampleFunc1_foo() {}
+func ExampleFunc1_foo_suffix() {}
+func ExampleFunc1_foo_Suffix() {} // matches Func1, instead of Func1_foo
+func Examplefunc1() {} // invalid - cannot match unexported
+
+func ExampleType1_Func1() {}
+func ExampleType1_Func1_() {} // invalid - suffix must start with a lower-case letter
+func ExampleType1_Func1_suffix() {}
+func ExampleType1_Func1_BadSuffix() {} // invalid - suffix must start with a lower-case letter
+func ExampleType1_Func1_Foo() {}
+func ExampleType1_Func1_Foo_suffix() {}
+func ExampleType1_Func1_Foo_BadSuffix() {} // invalid - suffix must start with a lower-case letter
+func ExampleType1_Func1_foo() {}
+func ExampleType1_Func1_foo_suffix() {}
+func ExampleType1_Func1_foo_Suffix() {} // matches Type1.Func1, instead of Type1.Func1_foo
+func ExampleType1_func2() {} // matches Type1, instead of Type1.func2
+
+func ExampleEmbed_Func1() {} // invalid - no support for forwarded methods from embedding exported type
+func ExampleUembed_Func1() {} // methods from embedding unexported types are OK
+func ExampleUembed_Func1_suffix() {}
+
+func ExampleConflict_Conflict() {} // ambiguous with either Conflict or Conflict_Conflict type
+func ExampleConflict_conflict() {} // ambiguous with either Conflict or Conflict_conflict type
+func ExampleConflict_Conflict_suffix() {} // ambiguous with either Conflict or Conflict_Conflict type
+func ExampleConflict_conflict_suffix() {} // ambiguous with either Conflict or Conflict_conflict type
+`
+
+ // Parse literal source code as a *doc.Package.
+ fset := token.NewFileSet()
+ files := []*ast.File{
+ mustParse(fset, "src.go", src),
+ mustParse(fset, "src_test.go", test),
+ }
+ p, err := doc.NewFromFiles(fset, files, "example.com/p")
+ if err != nil {
+ t.Fatalf("doc.NewFromFiles: %v", err)
+ }
+
+ // Collect the association of examples to top-level identifiers.
+ got := map[string][]string{}
+ got[""] = exampleNames(p.Examples)
+ for _, f := range p.Funcs {
+ got[f.Name] = exampleNames(f.Examples)
+ }
+ for _, t := range p.Types {
+ got[t.Name] = exampleNames(t.Examples)
+ for _, f := range t.Funcs {
+ got[f.Name] = exampleNames(f.Examples)
+ }
+ for _, m := range t.Methods {
+ got[t.Name+"."+m.Name] = exampleNames(m.Examples)
+ }
+ }
+
+ want := map[string][]string{
+ "": {"", "suffix", "suffix_xX_X_x"}, // Package-level examples.
+
+ "Type1": {"", "foo_Suffix", "func2", "suffix"},
+ "Type1_Foo": {"", "suffix"},
+ "Type1_foo": {"", "suffix"},
+
+ "Func1": {"", "foo_Suffix", "suffix"},
+ "Func1_Foo": {"", "suffix"},
+ "Func1_foo": {"", "suffix"},
+
+ "Type1.Func1": {"", "foo_Suffix", "suffix"},
+ "Type1.Func1_Foo": {"", "suffix"},
+ "Type1.Func1_foo": {"", "suffix"},
+
+ "Uembed.Func1": {"", "suffix"},
+
+ // These are implementation dependent due to the ambiguous parsing.
+ "Conflict_Conflict": {"", "suffix"},
+ "Conflict_conflict": {"", "suffix"},
+ }
+
+ for id := range got {
+ if !reflect.DeepEqual(got[id], want[id]) {
+ t.Errorf("classification mismatch for %q:\ngot %q\nwant %q", id, got[id], want[id])
+ }
+ }
+}
+
+func exampleNames(exs []*doc.Example) (out []string) {
+ for _, ex := range exs {
+ out = append(out, ex.Suffix)
+ }
+ return out
+}
+
+func mustParse(fset *token.FileSet, filename, src string) *ast.File {
+ f, err := parser.ParseFile(fset, filename, src, parser.ParseComments)
+ if err != nil {
+ panic(err)
+ }
+ return f
+}
diff --git a/src/go/doc/exports.go b/src/go/doc/exports.go
new file mode 100644
index 0000000..819c030
--- /dev/null
+++ b/src/go/doc/exports.go
@@ -0,0 +1,309 @@
+// Copyright 2011 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.
+
+// This file implements export filtering of an AST.
+
+package doc
+
+import (
+ "go/ast"
+ "go/token"
+)
+
+// filterIdentList removes unexported names from list in place
+// and returns the resulting list.
+//
+func filterIdentList(list []*ast.Ident) []*ast.Ident {
+ j := 0
+ for _, x := range list {
+ if token.IsExported(x.Name) {
+ list[j] = x
+ j++
+ }
+ }
+ return list[0:j]
+}
+
+var underscore = ast.NewIdent("_")
+
+func filterCompositeLit(lit *ast.CompositeLit, filter Filter, export bool) {
+ n := len(lit.Elts)
+ lit.Elts = filterExprList(lit.Elts, filter, export)
+ if len(lit.Elts) < n {
+ lit.Incomplete = true
+ }
+}
+
+func filterExprList(list []ast.Expr, filter Filter, export bool) []ast.Expr {
+ j := 0
+ for _, exp := range list {
+ switch x := exp.(type) {
+ case *ast.CompositeLit:
+ filterCompositeLit(x, filter, export)
+ case *ast.KeyValueExpr:
+ if x, ok := x.Key.(*ast.Ident); ok && !filter(x.Name) {
+ continue
+ }
+ if x, ok := x.Value.(*ast.CompositeLit); ok {
+ filterCompositeLit(x, filter, export)
+ }
+ }
+ list[j] = exp
+ j++
+ }
+ return list[0:j]
+}
+
+// updateIdentList replaces all unexported identifiers with underscore
+// and reports whether at least one exported name exists.
+func updateIdentList(list []*ast.Ident) (hasExported bool) {
+ for i, x := range list {
+ if token.IsExported(x.Name) {
+ hasExported = true
+ } else {
+ list[i] = underscore
+ }
+ }
+ return hasExported
+}
+
+// hasExportedName reports whether list contains any exported names.
+//
+func hasExportedName(list []*ast.Ident) bool {
+ for _, x := range list {
+ if x.IsExported() {
+ return true
+ }
+ }
+ return false
+}
+
+// removeErrorField removes anonymous fields named "error" from an interface.
+// This is called when "error" has been determined to be a local name,
+// not the predeclared type.
+//
+func removeErrorField(ityp *ast.InterfaceType) {
+ list := ityp.Methods.List // we know that ityp.Methods != nil
+ j := 0
+ for _, field := range list {
+ keepField := true
+ if n := len(field.Names); n == 0 {
+ // anonymous field
+ if fname, _ := baseTypeName(field.Type); fname == "error" {
+ keepField = false
+ }
+ }
+ if keepField {
+ list[j] = field
+ j++
+ }
+ }
+ if j < len(list) {
+ ityp.Incomplete = true
+ }
+ ityp.Methods.List = list[0:j]
+}
+
+// filterFieldList removes unexported fields (field names) from the field list
+// in place and reports whether fields were removed. Anonymous fields are
+// recorded with the parent type. filterType is called with the types of
+// all remaining fields.
+//
+func (r *reader) filterFieldList(parent *namedType, fields *ast.FieldList, ityp *ast.InterfaceType) (removedFields bool) {
+ if fields == nil {
+ return
+ }
+ list := fields.List
+ j := 0
+ for _, field := range list {
+ keepField := false
+ if n := len(field.Names); n == 0 {
+ // anonymous field
+ fname := r.recordAnonymousField(parent, field.Type)
+ if token.IsExported(fname) {
+ keepField = true
+ } else if ityp != nil && fname == "error" {
+ // possibly the predeclared error interface; keep
+ // it for now but remember this interface so that
+ // it can be fixed if error is also defined locally
+ keepField = true
+ r.remember(ityp)
+ }
+ } else {
+ field.Names = filterIdentList(field.Names)
+ if len(field.Names) < n {
+ removedFields = true
+ }
+ if len(field.Names) > 0 {
+ keepField = true
+ }
+ }
+ if keepField {
+ r.filterType(nil, field.Type)
+ list[j] = field
+ j++
+ }
+ }
+ if j < len(list) {
+ removedFields = true
+ }
+ fields.List = list[0:j]
+ return
+}
+
+// filterParamList applies filterType to each parameter type in fields.
+//
+func (r *reader) filterParamList(fields *ast.FieldList) {
+ if fields != nil {
+ for _, f := range fields.List {
+ r.filterType(nil, f.Type)
+ }
+ }
+}
+
+// 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.
+//
+func (r *reader) filterType(parent *namedType, typ ast.Expr) {
+ switch t := typ.(type) {
+ case *ast.Ident:
+ // nothing to do
+ case *ast.ParenExpr:
+ r.filterType(nil, t.X)
+ case *ast.ArrayType:
+ r.filterType(nil, t.Elt)
+ case *ast.StructType:
+ if r.filterFieldList(parent, t.Fields, nil) {
+ t.Incomplete = true
+ }
+ case *ast.FuncType:
+ r.filterParamList(t.Params)
+ r.filterParamList(t.Results)
+ case *ast.InterfaceType:
+ if r.filterFieldList(parent, t.Methods, t) {
+ t.Incomplete = true
+ }
+ case *ast.MapType:
+ r.filterType(nil, t.Key)
+ r.filterType(nil, t.Value)
+ case *ast.ChanType:
+ r.filterType(nil, t.Value)
+ }
+}
+
+func (r *reader) filterSpec(spec ast.Spec) bool {
+ switch s := spec.(type) {
+ case *ast.ImportSpec:
+ // always keep imports so we can collect them
+ return true
+ case *ast.ValueSpec:
+ s.Values = filterExprList(s.Values, token.IsExported, true)
+ if len(s.Values) > 0 || s.Type == nil && len(s.Values) == 0 {
+ // If there are values declared on RHS, just replace the unexported
+ // identifiers on the LHS with underscore, so that it matches
+ // the sequence of expression on the RHS.
+ //
+ // Similarly, if there are no type and values, then this expression
+ // must be following an iota expression, where order matters.
+ if updateIdentList(s.Names) {
+ r.filterType(nil, s.Type)
+ return true
+ }
+ } else {
+ s.Names = filterIdentList(s.Names)
+ if len(s.Names) > 0 {
+ r.filterType(nil, s.Type)
+ return true
+ }
+ }
+ case *ast.TypeSpec:
+ if name := s.Name.Name; token.IsExported(name) {
+ r.filterType(r.lookupType(s.Name.Name), s.Type)
+ return true
+ } else if name == "error" {
+ // special case: remember that error is declared locally
+ r.errorDecl = true
+ }
+ }
+ return false
+}
+
+// copyConstType returns a copy of typ with position pos.
+// typ must be a valid constant type.
+// In practice, only (possibly qualified) identifiers are possible.
+//
+func copyConstType(typ ast.Expr, pos token.Pos) ast.Expr {
+ switch typ := typ.(type) {
+ case *ast.Ident:
+ return &ast.Ident{Name: typ.Name, NamePos: pos}
+ case *ast.SelectorExpr:
+ if id, ok := typ.X.(*ast.Ident); ok {
+ // presumably a qualified identifier
+ return &ast.SelectorExpr{
+ Sel: ast.NewIdent(typ.Sel.Name),
+ X: &ast.Ident{Name: id.Name, NamePos: pos},
+ }
+ }
+ }
+ return nil // shouldn't happen, but be conservative and don't panic
+}
+
+func (r *reader) filterSpecList(list []ast.Spec, tok token.Token) []ast.Spec {
+ if tok == token.CONST {
+ // Propagate any type information that would get lost otherwise
+ // when unexported constants are filtered.
+ var prevType ast.Expr
+ for _, spec := range list {
+ spec := spec.(*ast.ValueSpec)
+ if spec.Type == nil && len(spec.Values) == 0 && prevType != nil {
+ // provide current spec with an explicit type
+ spec.Type = copyConstType(prevType, spec.Pos())
+ }
+ if hasExportedName(spec.Names) {
+ // exported names are preserved so there's no need to propagate the type
+ prevType = nil
+ } else {
+ prevType = spec.Type
+ }
+ }
+ }
+
+ j := 0
+ for _, s := range list {
+ if r.filterSpec(s) {
+ list[j] = s
+ j++
+ }
+ }
+ return list[0:j]
+}
+
+func (r *reader) filterDecl(decl ast.Decl) bool {
+ switch d := decl.(type) {
+ case *ast.GenDecl:
+ d.Specs = r.filterSpecList(d.Specs, d.Tok)
+ return len(d.Specs) > 0
+ case *ast.FuncDecl:
+ // ok to filter these methods early because any
+ // conflicting method will be filtered here, too -
+ // thus, removing these methods early will not lead
+ // to the false removal of possible conflicts
+ return token.IsExported(d.Name.Name)
+ }
+ return false
+}
+
+// fileExports removes unexported declarations from src in place.
+//
+func (r *reader) fileExports(src *ast.File) {
+ j := 0
+ for _, d := range src.Decls {
+ if r.filterDecl(d) {
+ src.Decls[j] = d
+ j++
+ }
+ }
+ src.Decls = src.Decls[0:j]
+}
diff --git a/src/go/doc/filter.go b/src/go/doc/filter.go
new file mode 100644
index 0000000..a6f243f
--- /dev/null
+++ b/src/go/doc/filter.go
@@ -0,0 +1,105 @@
+// Copyright 2009 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
+
+import "go/ast"
+
+type Filter func(string) bool
+
+func matchFields(fields *ast.FieldList, f Filter) bool {
+ if fields != nil {
+ for _, field := range fields.List {
+ for _, name := range field.Names {
+ if f(name.Name) {
+ return true
+ }
+ }
+ }
+ }
+ return false
+}
+
+func matchDecl(d *ast.GenDecl, f Filter) bool {
+ for _, d := range d.Specs {
+ switch v := d.(type) {
+ case *ast.ValueSpec:
+ for _, name := range v.Names {
+ if f(name.Name) {
+ return true
+ }
+ }
+ case *ast.TypeSpec:
+ if f(v.Name.Name) {
+ return true
+ }
+ switch t := v.Type.(type) {
+ case *ast.StructType:
+ if matchFields(t.Fields, f) {
+ return true
+ }
+ case *ast.InterfaceType:
+ if matchFields(t.Methods, f) {
+ return true
+ }
+ }
+ }
+ }
+ return false
+}
+
+func filterValues(a []*Value, f Filter) []*Value {
+ w := 0
+ for _, vd := range a {
+ if matchDecl(vd.Decl, f) {
+ a[w] = vd
+ w++
+ }
+ }
+ return a[0:w]
+}
+
+func filterFuncs(a []*Func, f Filter) []*Func {
+ w := 0
+ for _, fd := range a {
+ if f(fd.Name) {
+ a[w] = fd
+ w++
+ }
+ }
+ return a[0:w]
+}
+
+func filterTypes(a []*Type, f Filter) []*Type {
+ w := 0
+ for _, td := range a {
+ n := 0 // number of matches
+ if matchDecl(td.Decl, f) {
+ n = 1
+ } else {
+ // type name doesn't match, but we may have matching consts, vars, factories or methods
+ td.Consts = filterValues(td.Consts, f)
+ td.Vars = filterValues(td.Vars, f)
+ td.Funcs = filterFuncs(td.Funcs, f)
+ td.Methods = filterFuncs(td.Methods, f)
+ n += len(td.Consts) + len(td.Vars) + len(td.Funcs) + len(td.Methods)
+ }
+ if n > 0 {
+ a[w] = td
+ w++
+ }
+ }
+ return a[0:w]
+}
+
+// Filter eliminates documentation for names that don't pass through the filter f.
+// TODO(gri): Recognize "Type.Method" as a name.
+//
+func (p *Package) Filter(f Filter) {
+ p.Consts = filterValues(p.Consts, f)
+ p.Vars = filterValues(p.Vars, f)
+ p.Types = filterTypes(p.Types, f)
+ p.Funcs = filterFuncs(p.Funcs, f)
+ p.Doc = "" // don't show top-level package doc
+}
diff --git a/src/go/doc/headscan.go b/src/go/doc/headscan.go
new file mode 100644
index 0000000..28cb84f
--- /dev/null
+++ b/src/go/doc/headscan.go
@@ -0,0 +1,116 @@
+// Copyright 2011 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.
+
+//go:build ignore
+// +build ignore
+
+/*
+ The headscan command extracts comment headings from package files;
+ it is used to detect false positives which may require an adjustment
+ to the comment formatting heuristics in comment.go.
+
+ Usage: headscan [-root root_directory]
+
+ By default, the $GOROOT/src directory is scanned.
+*/
+package main
+
+import (
+ "bytes"
+ "flag"
+ "fmt"
+ "go/doc"
+ "go/parser"
+ "go/token"
+ "internal/lazyregexp"
+ "io/fs"
+ "os"
+ "path/filepath"
+ "runtime"
+ "strings"
+)
+
+var (
+ root = flag.String("root", filepath.Join(runtime.GOROOT(), "src"), "root of filesystem tree to scan")
+ verbose = flag.Bool("v", false, "verbose mode")
+)
+
+// ToHTML in comment.go assigns a (possibly blank) ID to each heading
+var html_h = lazyregexp.New(`<h3 id="[^"]*">`)
+
+const html_endh = "</h3>\n"
+
+func isGoFile(fi fs.FileInfo) bool {
+ return strings.HasSuffix(fi.Name(), ".go") &&
+ !strings.HasSuffix(fi.Name(), "_test.go")
+}
+
+func appendHeadings(list []string, comment string) []string {
+ var buf bytes.Buffer
+ doc.ToHTML(&buf, comment, nil)
+ for s := buf.String(); ; {
+ loc := html_h.FindStringIndex(s)
+ if len(loc) == 0 {
+ break
+ }
+ i := loc[1]
+ j := strings.Index(s, html_endh)
+ if j < 0 {
+ list = append(list, s[i:]) // incorrect HTML
+ break
+ }
+ list = append(list, s[i:j])
+ s = s[j+len(html_endh):]
+ }
+ return list
+}
+
+func main() {
+ flag.Parse()
+ fset := token.NewFileSet()
+ nheadings := 0
+ err := filepath.WalkDir(*root, func(path string, info fs.DirEntry, err error) error {
+ if !info.IsDir() {
+ return nil
+ }
+ pkgs, err := parser.ParseDir(fset, path, isGoFile, parser.ParseComments)
+ if err != nil {
+ if *verbose {
+ fmt.Fprintln(os.Stderr, err)
+ }
+ return nil
+ }
+ for _, pkg := range pkgs {
+ d := doc.New(pkg, path, doc.Mode(0))
+ list := appendHeadings(nil, d.Doc)
+ for _, d := range d.Consts {
+ list = appendHeadings(list, d.Doc)
+ }
+ for _, d := range d.Types {
+ list = appendHeadings(list, d.Doc)
+ }
+ for _, d := range d.Vars {
+ list = appendHeadings(list, d.Doc)
+ }
+ for _, d := range d.Funcs {
+ list = appendHeadings(list, d.Doc)
+ }
+ if len(list) > 0 {
+ // directories may contain multiple packages;
+ // print path and package name
+ fmt.Printf("%s (package %s)\n", path, pkg.Name)
+ for _, h := range list {
+ fmt.Printf("\t%s\n", h)
+ }
+ nheadings += len(list)
+ }
+ }
+ return nil
+ })
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+ fmt.Println(nheadings, "headings found")
+}
diff --git a/src/go/doc/reader.go b/src/go/doc/reader.go
new file mode 100644
index 0000000..c277b35
--- /dev/null
+++ b/src/go/doc/reader.go
@@ -0,0 +1,917 @@
+// Copyright 2009 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
+
+import (
+ "go/ast"
+ "go/token"
+ "internal/lazyregexp"
+ "sort"
+ "strconv"
+)
+
+// ----------------------------------------------------------------------------
+// function/method sets
+//
+// Internally, we treat functions like methods and collect them in method sets.
+
+// A methodSet describes a set of methods. Entries where Decl == nil are conflict
+// entries (more than one method with the same name at the same embedding level).
+//
+type methodSet map[string]*Func
+
+// recvString returns a string representation of recv of the
+// form "T", "*T", or "BADRECV" (if not a proper receiver type).
+//
+func recvString(recv ast.Expr) string {
+ switch t := recv.(type) {
+ case *ast.Ident:
+ return t.Name
+ case *ast.StarExpr:
+ return "*" + recvString(t.X)
+ }
+ return "BADRECV"
+}
+
+// set creates the corresponding Func for f and adds it to mset.
+// If there are multiple f's with the same name, set keeps the first
+// one with documentation; conflicts are ignored. The boolean
+// specifies whether to leave the AST untouched.
+//
+func (mset methodSet) set(f *ast.FuncDecl, preserveAST bool) {
+ name := f.Name.Name
+ if g := mset[name]; g != nil && g.Doc != "" {
+ // A function with the same name has already been registered;
+ // 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.
+ return
+ }
+ // function doesn't exist or has no documentation; use f
+ recv := ""
+ if f.Recv != nil {
+ var typ ast.Expr
+ // be careful in case of incorrect ASTs
+ if list := f.Recv.List; len(list) == 1 {
+ typ = list[0].Type
+ }
+ recv = recvString(typ)
+ }
+ mset[name] = &Func{
+ Doc: f.Doc.Text(),
+ Name: name,
+ Decl: f,
+ Recv: recv,
+ Orig: recv,
+ }
+ if !preserveAST {
+ f.Doc = nil // doc consumed - remove from AST
+ }
+}
+
+// add adds method m to the method set; m is ignored if the method set
+// already contains a method with the same name at the same or a higher
+// level than m.
+//
+func (mset methodSet) add(m *Func) {
+ old := mset[m.Name]
+ if old == nil || m.Level < old.Level {
+ mset[m.Name] = m
+ return
+ }
+ if m.Level == old.Level {
+ // conflict - mark it using a method with nil Decl
+ mset[m.Name] = &Func{
+ Name: m.Name,
+ Level: m.Level,
+ }
+ }
+}
+
+// ----------------------------------------------------------------------------
+// Named types
+
+// baseTypeName returns the name of the base type of x (or "")
+// and whether the type is imported or not.
+//
+func baseTypeName(x ast.Expr) (name string, imported bool) {
+ switch t := x.(type) {
+ case *ast.Ident:
+ return t.Name, false
+ case *ast.SelectorExpr:
+ if _, ok := t.X.(*ast.Ident); ok {
+ // only possible for qualified type names;
+ // assume type is imported
+ return t.Sel.Name, true
+ }
+ case *ast.ParenExpr:
+ return baseTypeName(t.X)
+ case *ast.StarExpr:
+ return baseTypeName(t.X)
+ }
+ return
+}
+
+// An embeddedSet describes a set of embedded types.
+type embeddedSet map[*namedType]bool
+
+// A namedType represents a named unqualified (package local, or possibly
+// predeclared) type. The namedType for a type name is always found via
+// reader.lookupType.
+//
+type namedType struct {
+ doc string // doc comment for type
+ name string // type name
+ decl *ast.GenDecl // nil if declaration hasn't been seen yet
+
+ isEmbedded bool // true if this type is embedded
+ isStruct bool // true if this type is a struct
+ embedded embeddedSet // true if the embedded type is a pointer
+
+ // associated declarations
+ values []*Value // consts and vars
+ funcs methodSet
+ methods methodSet
+}
+
+// ----------------------------------------------------------------------------
+// AST reader
+
+// reader accumulates documentation for a single package.
+// It modifies the AST: Comments (declaration documentation)
+// that have been collected by the reader are set to nil
+// in the respective AST nodes so that they are not printed
+// twice (once when printing the documentation and once when
+// printing the corresponding AST node).
+//
+type reader struct {
+ mode Mode
+
+ // package properties
+ doc string // package documentation, if any
+ filenames []string
+ notes map[string][]*Note
+
+ // declarations
+ imports map[string]int
+ hasDotImp bool // if set, package contains a dot import
+ values []*Value // consts and vars
+ order int // sort order of const and var declarations (when we can't use a name)
+ types map[string]*namedType
+ funcs methodSet
+
+ // support for package-local error type declarations
+ errorDecl bool // if set, type "error" was declared locally
+ fixlist []*ast.InterfaceType // list of interfaces containing anonymous field "error"
+}
+
+func (r *reader) isVisible(name string) bool {
+ return r.mode&AllDecls != 0 || token.IsExported(name)
+}
+
+// lookupType returns the base type with the given name.
+// If the base type has not been encountered yet, a new
+// type with the given name but no associated declaration
+// is added to the type map.
+//
+func (r *reader) lookupType(name string) *namedType {
+ if name == "" || name == "_" {
+ return nil // no type docs for anonymous types
+ }
+ if typ, found := r.types[name]; found {
+ return typ
+ }
+ // type not found - add one without declaration
+ typ := &namedType{
+ name: name,
+ embedded: make(embeddedSet),
+ funcs: make(methodSet),
+ methods: make(methodSet),
+ }
+ r.types[name] = typ
+ return typ
+}
+
+// recordAnonymousField registers fieldType as the type of an
+// anonymous field in the parent type. If the field is imported
+// (qualified name) or the parent is nil, the field is ignored.
+// The function returns the field name.
+//
+func (r *reader) recordAnonymousField(parent *namedType, fieldType ast.Expr) (fname string) {
+ fname, imp := baseTypeName(fieldType)
+ if parent == nil || imp {
+ return
+ }
+ if ftype := r.lookupType(fname); ftype != nil {
+ ftype.isEmbedded = true
+ _, ptr := fieldType.(*ast.StarExpr)
+ parent.embedded[ftype] = ptr
+ }
+ return
+}
+
+func (r *reader) readDoc(comment *ast.CommentGroup) {
+ // By convention there should be only one package comment
+ // but collect all of them if there are more than one.
+ text := comment.Text()
+ if r.doc == "" {
+ r.doc = text
+ return
+ }
+ r.doc += "\n" + text
+}
+
+func (r *reader) remember(typ *ast.InterfaceType) {
+ r.fixlist = append(r.fixlist, typ)
+}
+
+func specNames(specs []ast.Spec) []string {
+ names := make([]string, 0, len(specs)) // reasonable estimate
+ for _, s := range specs {
+ // s guaranteed to be an *ast.ValueSpec by readValue
+ for _, ident := range s.(*ast.ValueSpec).Names {
+ names = append(names, ident.Name)
+ }
+ }
+ return names
+}
+
+// readValue processes a const or var declaration.
+//
+func (r *reader) readValue(decl *ast.GenDecl) {
+ // determine if decl should be associated with a type
+ // Heuristic: For each typed entry, determine the type name, if any.
+ // If there is exactly one type name that is sufficiently
+ // frequent, associate the decl with the respective type.
+ domName := ""
+ domFreq := 0
+ prev := ""
+ n := 0
+ for _, spec := range decl.Specs {
+ s, ok := spec.(*ast.ValueSpec)
+ if !ok {
+ continue // should not happen, but be conservative
+ }
+ name := ""
+ switch {
+ case s.Type != nil:
+ // a type is present; determine its name
+ if n, imp := baseTypeName(s.Type); !imp {
+ name = n
+ }
+ case decl.Tok == token.CONST && len(s.Values) == 0:
+ // no type or value is present but we have a constant declaration;
+ // use the previous type name (possibly the empty string)
+ name = prev
+ }
+ if name != "" {
+ // entry has a named type
+ if domName != "" && domName != name {
+ // more than one type name - do not associate
+ // with any type
+ domName = ""
+ break
+ }
+ domName = name
+ domFreq++
+ }
+ prev = name
+ n++
+ }
+
+ // nothing to do w/o a legal declaration
+ if n == 0 {
+ return
+ }
+
+ // determine values list with which to associate the Value for this decl
+ values := &r.values
+ const threshold = 0.75
+ if domName != "" && r.isVisible(domName) && domFreq >= int(float64(len(decl.Specs))*threshold) {
+ // typed entries are sufficiently frequent
+ if typ := r.lookupType(domName); typ != nil {
+ values = &typ.values // associate with that type
+ }
+ }
+
+ *values = append(*values, &Value{
+ Doc: decl.Doc.Text(),
+ Names: specNames(decl.Specs),
+ Decl: decl,
+ order: r.order,
+ })
+ if r.mode&PreserveAST == 0 {
+ decl.Doc = nil // doc consumed - remove from AST
+ }
+ // Note: It's important that the order used here is global because the cleanupTypes
+ // methods may move values associated with types back into the global list. If the
+ // order is list-specific, sorting is not deterministic because the same order value
+ // may appear multiple times (was bug, found when fixing #16153).
+ r.order++
+}
+
+// fields returns a struct's fields or an interface's methods.
+//
+func fields(typ ast.Expr) (list []*ast.Field, isStruct bool) {
+ var fields *ast.FieldList
+ switch t := typ.(type) {
+ case *ast.StructType:
+ fields = t.Fields
+ isStruct = true
+ case *ast.InterfaceType:
+ fields = t.Methods
+ }
+ if fields != nil {
+ list = fields.List
+ }
+ return
+}
+
+// readType processes a type declaration.
+//
+func (r *reader) readType(decl *ast.GenDecl, spec *ast.TypeSpec) {
+ typ := r.lookupType(spec.Name.Name)
+ if typ == nil {
+ return // no name or blank name - ignore the type
+ }
+
+ // A type should be added at most once, so typ.decl
+ // should be nil - if it is not, simply overwrite it.
+ typ.decl = decl
+
+ // compute documentation
+ doc := spec.Doc
+ if doc == nil {
+ // no doc associated with the spec, use the declaration doc, if any
+ doc = decl.Doc
+ }
+ if r.mode&PreserveAST == 0 {
+ spec.Doc = nil // doc consumed - remove from AST
+ decl.Doc = nil // doc consumed - remove from AST
+ }
+ typ.doc = doc.Text()
+
+ // record anonymous fields (they may contribute methods)
+ // (some fields may have been recorded already when filtering
+ // exports, but that's ok)
+ var list []*ast.Field
+ list, typ.isStruct = fields(spec.Type)
+ for _, field := range list {
+ if len(field.Names) == 0 {
+ r.recordAnonymousField(typ, field.Type)
+ }
+ }
+}
+
+// isPredeclared reports whether n denotes a predeclared type.
+//
+func (r *reader) isPredeclared(n string) bool {
+ return predeclaredTypes[n] && r.types[n] == nil
+}
+
+// readFunc processes a func or method declaration.
+//
+func (r *reader) readFunc(fun *ast.FuncDecl) {
+ // strip function body if requested.
+ if r.mode&PreserveAST == 0 {
+ fun.Body = nil
+ }
+
+ // associate methods with the receiver type, if any
+ if fun.Recv != nil {
+ // method
+ if len(fun.Recv.List) == 0 {
+ // should not happen (incorrect AST); (See issue 17788)
+ // don't show this method
+ return
+ }
+ recvTypeName, imp := baseTypeName(fun.Recv.List[0].Type)
+ if imp {
+ // should not happen (incorrect AST);
+ // don't show this method
+ return
+ }
+ if typ := r.lookupType(recvTypeName); typ != nil {
+ typ.methods.set(fun, r.mode&PreserveAST != 0)
+ }
+ // otherwise ignore the method
+ // TODO(gri): There may be exported methods of non-exported types
+ // that can be called because of exported values (consts, vars, or
+ // function results) of that type. Could determine if that is the
+ // case and then show those methods in an appropriate section.
+ return
+ }
+
+ // Associate factory functions with the first visible result type, as long as
+ // others are predeclared types.
+ if fun.Type.Results.NumFields() >= 1 {
+ var typ *namedType // type to associate the function with
+ numResultTypes := 0
+ for _, res := range fun.Type.Results.List {
+ factoryType := res.Type
+ if t, ok := factoryType.(*ast.ArrayType); ok {
+ // We consider functions that return slices or arrays of type
+ // T (or pointers to T) as factory functions of T.
+ factoryType = t.Elt
+ }
+ if n, imp := baseTypeName(factoryType); !imp && r.isVisible(n) && !r.isPredeclared(n) {
+ if t := r.lookupType(n); t != nil {
+ typ = t
+ numResultTypes++
+ if numResultTypes > 1 {
+ break
+ }
+ }
+ }
+ }
+ // If there is exactly one result type,
+ // associate the function with that type.
+ if numResultTypes == 1 {
+ typ.funcs.set(fun, r.mode&PreserveAST != 0)
+ return
+ }
+ }
+
+ // just an ordinary function
+ r.funcs.set(fun, r.mode&PreserveAST != 0)
+}
+
+var (
+ noteMarker = `([A-Z][A-Z]+)\(([^)]+)\):?` // MARKER(uid), MARKER at least 2 chars, uid at least 1 char
+ noteMarkerRx = lazyregexp.New(`^[ \t]*` + noteMarker) // MARKER(uid) at text start
+ noteCommentRx = lazyregexp.New(`^/[/*][ \t]*` + noteMarker) // MARKER(uid) at comment start
+)
+
+// readNote collects a single note from a sequence of comments.
+//
+func (r *reader) readNote(list []*ast.Comment) {
+ text := (&ast.CommentGroup{List: list}).Text()
+ if m := noteMarkerRx.FindStringSubmatchIndex(text); m != nil {
+ // The note body starts after the marker.
+ // We remove any formatting so that we don't
+ // get spurious line breaks/indentation when
+ // showing the TODO body.
+ body := clean(text[m[1]:], keepNL)
+ if body != "" {
+ marker := text[m[2]:m[3]]
+ r.notes[marker] = append(r.notes[marker], &Note{
+ Pos: list[0].Pos(),
+ End: list[len(list)-1].End(),
+ UID: text[m[4]:m[5]],
+ Body: body,
+ })
+ }
+ }
+}
+
+// readNotes extracts notes from comments.
+// A note must start at the beginning of a comment with "MARKER(uid):"
+// and is followed by the note body (e.g., "// BUG(gri): fix this").
+// The note ends at the end of the comment group or at the start of
+// another note in the same comment group, whichever comes first.
+//
+func (r *reader) readNotes(comments []*ast.CommentGroup) {
+ for _, group := range comments {
+ i := -1 // comment index of most recent note start, valid if >= 0
+ list := group.List
+ for j, c := range list {
+ if noteCommentRx.MatchString(c.Text) {
+ if i >= 0 {
+ r.readNote(list[i:j])
+ }
+ i = j
+ }
+ }
+ if i >= 0 {
+ r.readNote(list[i:])
+ }
+ }
+}
+
+// readFile adds the AST for a source file to the reader.
+//
+func (r *reader) readFile(src *ast.File) {
+ // add package documentation
+ if src.Doc != nil {
+ r.readDoc(src.Doc)
+ if r.mode&PreserveAST == 0 {
+ src.Doc = nil // doc consumed - remove from AST
+ }
+ }
+
+ // add all declarations but for functions which are processed in a separate pass
+ for _, decl := range src.Decls {
+ switch d := decl.(type) {
+ case *ast.GenDecl:
+ switch d.Tok {
+ case token.IMPORT:
+ // imports are handled individually
+ for _, spec := range d.Specs {
+ if s, ok := spec.(*ast.ImportSpec); ok {
+ if import_, err := strconv.Unquote(s.Path.Value); err == nil {
+ r.imports[import_] = 1
+ if s.Name != nil && s.Name.Name == "." {
+ r.hasDotImp = true
+ }
+ }
+ }
+ }
+ case token.CONST, token.VAR:
+ // constants and variables are always handled as a group
+ r.readValue(d)
+ case token.TYPE:
+ // types are handled individually
+ if len(d.Specs) == 1 && !d.Lparen.IsValid() {
+ // common case: single declaration w/o parentheses
+ // (if a single declaration is parenthesized,
+ // create a new fake declaration below, so that
+ // go/doc type declarations always appear w/o
+ // parentheses)
+ if s, ok := d.Specs[0].(*ast.TypeSpec); ok {
+ r.readType(d, s)
+ }
+ break
+ }
+ for _, spec := range d.Specs {
+ if s, ok := spec.(*ast.TypeSpec); ok {
+ // use an individual (possibly fake) declaration
+ // for each type; this also ensures that each type
+ // gets to (re-)use the declaration documentation
+ // if there's none associated with the spec itself
+ fake := &ast.GenDecl{
+ Doc: d.Doc,
+ // don't use the existing TokPos because it
+ // will lead to the wrong selection range for
+ // the fake declaration if there are more
+ // than one type in the group (this affects
+ // src/cmd/godoc/godoc.go's posLink_urlFunc)
+ TokPos: s.Pos(),
+ Tok: token.TYPE,
+ Specs: []ast.Spec{s},
+ }
+ r.readType(fake, s)
+ }
+ }
+ }
+ }
+ }
+
+ // collect MARKER(...): annotations
+ r.readNotes(src.Comments)
+ if r.mode&PreserveAST == 0 {
+ src.Comments = nil // consumed unassociated comments - remove from AST
+ }
+}
+
+func (r *reader) readPackage(pkg *ast.Package, mode Mode) {
+ // initialize reader
+ r.filenames = make([]string, len(pkg.Files))
+ r.imports = make(map[string]int)
+ r.mode = mode
+ r.types = make(map[string]*namedType)
+ r.funcs = make(methodSet)
+ r.notes = make(map[string][]*Note)
+
+ // sort package files before reading them so that the
+ // result does not depend on map iteration order
+ i := 0
+ for filename := range pkg.Files {
+ r.filenames[i] = filename
+ i++
+ }
+ sort.Strings(r.filenames)
+
+ // process files in sorted order
+ for _, filename := range r.filenames {
+ f := pkg.Files[filename]
+ if mode&AllDecls == 0 {
+ r.fileExports(f)
+ }
+ r.readFile(f)
+ }
+
+ // process functions now that we have better type information
+ for _, f := range pkg.Files {
+ for _, decl := range f.Decls {
+ if d, ok := decl.(*ast.FuncDecl); ok {
+ r.readFunc(d)
+ }
+ }
+ }
+}
+
+// ----------------------------------------------------------------------------
+// Types
+
+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
+ }
+
+ // copy existing receiver field and set new type
+ newField := *f.Decl.Recv.List[0]
+ origPos := newField.Type.Pos()
+ _, origRecvIsPtr := newField.Type.(*ast.StarExpr)
+ newIdent := &ast.Ident{NamePos: origPos, Name: recvTypeName}
+ var typ ast.Expr = newIdent
+ if !embeddedIsPtr && origRecvIsPtr {
+ newIdent.NamePos++ // '*' is one character
+ typ = &ast.StarExpr{Star: origPos, X: newIdent}
+ }
+ newField.Type = typ
+
+ // copy existing receiver field list and set new receiver field
+ newFieldList := *f.Decl.Recv
+ newFieldList.List = []*ast.Field{&newField}
+
+ // copy existing function declaration and set new receiver field list
+ newFuncDecl := *f.Decl
+ newFuncDecl.Recv = &newFieldList
+
+ // copy existing function documentation and set new declaration
+ newF := *f
+ newF.Decl = &newFuncDecl
+ newF.Recv = recvString(typ)
+ // the Orig field never changes
+ newF.Level = level
+
+ return &newF
+}
+
+// collectEmbeddedMethods collects the embedded methods of typ in mset.
+//
+func (r *reader) collectEmbeddedMethods(mset methodSet, typ *namedType, recvTypeName string, embeddedIsPtr bool, level int, visited embeddedSet) {
+ visited[typ] = true
+ for embedded, isPtr := range typ.embedded {
+ // Once an embedded type is embedded as a pointer type
+ // all embedded types in those types are treated like
+ // pointer types for the purpose of the receiver type
+ // computation; i.e., embeddedIsPtr is sticky for this
+ // embedding hierarchy.
+ thisEmbeddedIsPtr := embeddedIsPtr || isPtr
+ for _, m := range embedded.methods {
+ // only top-level methods are embedded
+ if m.Level == 0 {
+ mset.add(customizeRecv(m, recvTypeName, thisEmbeddedIsPtr, level))
+ }
+ }
+ if !visited[embedded] {
+ r.collectEmbeddedMethods(mset, embedded, recvTypeName, thisEmbeddedIsPtr, level+1, visited)
+ }
+ }
+ delete(visited, typ)
+}
+
+// computeMethodSets determines the actual method sets for each type encountered.
+//
+func (r *reader) computeMethodSets() {
+ for _, t := range r.types {
+ // collect embedded methods for t
+ if t.isStruct {
+ // struct
+ r.collectEmbeddedMethods(t.methods, t, t.name, false, 1, make(embeddedSet))
+ } else {
+ // interface
+ // TODO(gri) fix this
+ }
+ }
+
+ // if error was declared locally, don't treat it as exported field anymore
+ if r.errorDecl {
+ for _, ityp := range r.fixlist {
+ removeErrorField(ityp)
+ }
+ }
+}
+
+// cleanupTypes removes the association of functions and methods with
+// 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)
+ predeclared := predeclaredTypes[t.name]
+
+ if t.decl == nil && (predeclared || visible && (t.isEmbedded || r.hasDotImp)) {
+ // t.name is a predeclared type (and was not redeclared in this package),
+ // or it was embedded somewhere but its declaration is missing (because
+ // the AST is incomplete), or we have a dot-import (and all bets are off):
+ // move any associated values, funcs, and methods back to the top-level so
+ // that they are not lost.
+ // 1) move values
+ r.values = append(r.values, t.values...)
+ // 2) move factory functions
+ for name, f := range t.funcs {
+ // in a correct AST, package-level function names
+ // are all different - no need to check for conflicts
+ r.funcs[name] = f
+ }
+ // 3) move methods
+ if !predeclared {
+ for name, m := range t.methods {
+ // don't overwrite functions with the same name - drop them
+ if _, found := r.funcs[name]; !found {
+ r.funcs[name] = m
+ }
+ }
+ }
+ }
+ // remove types w/o declaration or which are not visible
+ if t.decl == nil || !visible {
+ delete(r.types, t.name)
+ }
+ }
+}
+
+// ----------------------------------------------------------------------------
+// Sorting
+
+type data struct {
+ n int
+ swap func(i, j int)
+ less func(i, j int) bool
+}
+
+func (d *data) Len() int { return d.n }
+func (d *data) Swap(i, j int) { d.swap(i, j) }
+func (d *data) Less(i, j int) bool { return d.less(i, j) }
+
+// sortBy is a helper function for sorting
+func sortBy(less func(i, j int) bool, swap func(i, j int), n int) {
+ sort.Sort(&data{n, swap, less})
+}
+
+func sortedKeys(m map[string]int) []string {
+ list := make([]string, len(m))
+ i := 0
+ for key := range m {
+ list[i] = key
+ i++
+ }
+ sort.Strings(list)
+ return list
+}
+
+// sortingName returns the name to use when sorting d into place.
+//
+func sortingName(d *ast.GenDecl) string {
+ if len(d.Specs) == 1 {
+ if s, ok := d.Specs[0].(*ast.ValueSpec); ok {
+ return s.Names[0].Name
+ }
+ }
+ return ""
+}
+
+func sortedValues(m []*Value, tok token.Token) []*Value {
+ list := make([]*Value, len(m)) // big enough in any case
+ i := 0
+ for _, val := range m {
+ if val.Decl.Tok == tok {
+ list[i] = val
+ i++
+ }
+ }
+ list = list[0:i]
+
+ sortBy(
+ func(i, j int) bool {
+ if ni, nj := sortingName(list[i].Decl), sortingName(list[j].Decl); ni != nj {
+ return ni < nj
+ }
+ return list[i].order < list[j].order
+ },
+ func(i, j int) { list[i], list[j] = list[j], list[i] },
+ len(list),
+ )
+
+ return list
+}
+
+func sortedTypes(m map[string]*namedType, allMethods bool) []*Type {
+ list := make([]*Type, len(m))
+ i := 0
+ for _, t := range m {
+ list[i] = &Type{
+ Doc: t.doc,
+ Name: t.name,
+ Decl: t.decl,
+ Consts: sortedValues(t.values, token.CONST),
+ Vars: sortedValues(t.values, token.VAR),
+ Funcs: sortedFuncs(t.funcs, true),
+ Methods: sortedFuncs(t.methods, allMethods),
+ }
+ i++
+ }
+
+ sortBy(
+ func(i, j int) bool { return list[i].Name < list[j].Name },
+ func(i, j int) { list[i], list[j] = list[j], list[i] },
+ len(list),
+ )
+
+ return list
+}
+
+func removeStar(s string) string {
+ if len(s) > 0 && s[0] == '*' {
+ return s[1:]
+ }
+ return s
+}
+
+func sortedFuncs(m methodSet, allMethods bool) []*Func {
+ list := make([]*Func, len(m))
+ i := 0
+ for _, m := range m {
+ // determine which methods to include
+ switch {
+ case m.Decl == nil:
+ // exclude conflict entry
+ case allMethods, m.Level == 0, !token.IsExported(removeStar(m.Orig)):
+ // forced inclusion, method not embedded, or method
+ // embedded but original receiver type not exported
+ list[i] = m
+ i++
+ }
+ }
+ list = list[0:i]
+ sortBy(
+ func(i, j int) bool { return list[i].Name < list[j].Name },
+ func(i, j int) { list[i], list[j] = list[j], list[i] },
+ len(list),
+ )
+ return list
+}
+
+// noteBodies returns a list of note body strings given a list of notes.
+// This is only used to populate the deprecated Package.Bugs field.
+//
+func noteBodies(notes []*Note) []string {
+ var list []string
+ for _, n := range notes {
+ list = append(list, n.Body)
+ }
+ return list
+}
+
+// ----------------------------------------------------------------------------
+// Predeclared identifiers
+
+// IsPredeclared reports whether s is a predeclared identifier.
+func IsPredeclared(s string) bool {
+ return predeclaredTypes[s] || predeclaredFuncs[s] || predeclaredConstants[s]
+}
+
+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/go/doc/synopsis.go b/src/go/doc/synopsis.go
new file mode 100644
index 0000000..3fa1616
--- /dev/null
+++ b/src/go/doc/synopsis.go
@@ -0,0 +1,83 @@
+// Copyright 2012 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
+
+import (
+ "strings"
+ "unicode"
+)
+
+// firstSentenceLen returns the length of the first sentence in s.
+// The sentence ends after the first period followed by space and
+// not preceded by exactly one uppercase letter.
+//
+func firstSentenceLen(s string) int {
+ var ppp, pp, p rune
+ for i, q := range s {
+ if q == '\n' || q == '\r' || q == '\t' {
+ q = ' '
+ }
+ if q == ' ' && p == '.' && (!unicode.IsUpper(pp) || unicode.IsUpper(ppp)) {
+ return i
+ }
+ if p == '。' || p == '.' {
+ return i
+ }
+ ppp, pp, p = pp, p, q
+ }
+ return len(s)
+}
+
+const (
+ keepNL = 1 << iota
+)
+
+// clean replaces each sequence of space, \n, \r, or \t characters
+// with a single space and removes any trailing and leading spaces.
+// If the keepNL flag is set, newline characters are passed through
+// instead of being change to spaces.
+func clean(s string, flags int) string {
+ var b []byte
+ p := byte(' ')
+ for i := 0; i < len(s); i++ {
+ q := s[i]
+ if (flags&keepNL) == 0 && q == '\n' || q == '\r' || q == '\t' {
+ q = ' '
+ }
+ if q != ' ' || p != ' ' {
+ b = append(b, q)
+ p = q
+ }
+ }
+ // remove trailing blank, if any
+ if n := len(b); n > 0 && p == ' ' {
+ b = b[0 : n-1]
+ }
+ 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)], 0)
+ for _, prefix := range IllegalPrefixes {
+ if strings.HasPrefix(strings.ToLower(s), prefix) {
+ return ""
+ }
+ }
+ s = convertQuotes(s)
+ return s
+}
+
+var IllegalPrefixes = []string{
+ "copyright",
+ "all rights",
+ "author",
+}
diff --git a/src/go/doc/synopsis_test.go b/src/go/doc/synopsis_test.go
new file mode 100644
index 0000000..3f443dc
--- /dev/null
+++ b/src/go/doc/synopsis_test.go
@@ -0,0 +1,52 @@
+// Copyright 2012 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
+
+import "testing"
+
+var tests = []struct {
+ txt string
+ fsl int
+ syn string
+}{
+ {"", 0, ""},
+ {"foo", 3, "foo"},
+ {"foo.", 4, "foo."},
+ {"foo.bar", 7, "foo.bar"},
+ {" foo. ", 6, "foo."},
+ {" foo\t bar.\n", 12, "foo bar."},
+ {" foo\t bar.\n", 12, "foo bar."},
+ {"a b\n\nc\r\rd\t\t", 12, "a b c d"},
+ {"a b\n\nc\r\rd\t\t . BLA", 15, "a b c d ."},
+ {"Package poems by T.S.Eliot. To rhyme...", 27, "Package poems by T.S.Eliot."},
+ {"Package poems by T. S. Eliot. To rhyme...", 29, "Package poems by T. S. Eliot."},
+ {"foo implements the foo ABI. The foo ABI is...", 27, "foo implements the foo ABI."},
+ {"Package\nfoo. ..", 12, "Package foo."},
+ {"P . Q.", 3, "P ."},
+ {"P. Q. ", 8, "P. Q."},
+ {"Package Καλημέρα κόσμε.", 36, "Package Καλημέρα κόσμε."},
+ {"Package こんにちは 世界\n", 31, "Package こんにちは 世界"},
+ {"Package こんにちは。世界", 26, "Package こんにちは。"},
+ {"Package 안녕.世界", 17, "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, ""},
+ {"typically invoked as ``go tool asm'',", 37, "typically invoked as " + ulquo + "go tool asm" + urquo + ","},
+}
+
+func TestSynopsis(t *testing.T) {
+ for _, e := range tests {
+ fsl := firstSentenceLen(e.txt)
+ if fsl != e.fsl {
+ t.Errorf("got fsl = %d; want %d for %q\n", fsl, e.fsl, e.txt)
+ }
+ syn := Synopsis(e.txt)
+ if syn != e.syn {
+ t.Errorf("got syn = %q; want %q for %q\n", syn, e.syn, e.txt)
+ }
+ }
+}
diff --git a/src/go/doc/testdata/a.0.golden b/src/go/doc/testdata/a.0.golden
new file mode 100644
index 0000000..7e680b8
--- /dev/null
+++ b/src/go/doc/testdata/a.0.golden
@@ -0,0 +1,52 @@
+// comment 0 comment 1
+PACKAGE a
+
+IMPORTPATH
+ testdata/a
+
+FILENAMES
+ testdata/a0.go
+ testdata/a1.go
+
+BUGS .Bugs is now deprecated, please use .Notes instead
+ bug0
+
+ bug1
+
+
+BUGS
+BUG(uid) bug0
+
+BUG(uid) bug1
+
+
+NOTES
+NOTE(uid)
+
+NOTE(foo) 1 of 4 - this is the first line of note 1
+ - note 1 continues on this 2nd line
+ - note 1 continues on this 3rd line
+
+NOTE(foo) 2 of 4
+
+NOTE(bar) 3 of 4
+
+NOTE(bar) 4 of 4
+ - this is the last line of note 4
+
+NOTE(bam) This note which contains a (parenthesized) subphrase
+ must appear in its entirety.
+
+NOTE(xxx) The ':' after the marker and uid is optional.
+
+
+SECBUGS
+SECBUG(uid) sec hole 0
+ need to fix asap
+
+
+TODOS
+TODO(uid) todo0
+
+TODO(uid) todo1
+
diff --git a/src/go/doc/testdata/a.1.golden b/src/go/doc/testdata/a.1.golden
new file mode 100644
index 0000000..7e680b8
--- /dev/null
+++ b/src/go/doc/testdata/a.1.golden
@@ -0,0 +1,52 @@
+// comment 0 comment 1
+PACKAGE a
+
+IMPORTPATH
+ testdata/a
+
+FILENAMES
+ testdata/a0.go
+ testdata/a1.go
+
+BUGS .Bugs is now deprecated, please use .Notes instead
+ bug0
+
+ bug1
+
+
+BUGS
+BUG(uid) bug0
+
+BUG(uid) bug1
+
+
+NOTES
+NOTE(uid)
+
+NOTE(foo) 1 of 4 - this is the first line of note 1
+ - note 1 continues on this 2nd line
+ - note 1 continues on this 3rd line
+
+NOTE(foo) 2 of 4
+
+NOTE(bar) 3 of 4
+
+NOTE(bar) 4 of 4
+ - this is the last line of note 4
+
+NOTE(bam) This note which contains a (parenthesized) subphrase
+ must appear in its entirety.
+
+NOTE(xxx) The ':' after the marker and uid is optional.
+
+
+SECBUGS
+SECBUG(uid) sec hole 0
+ need to fix asap
+
+
+TODOS
+TODO(uid) todo0
+
+TODO(uid) todo1
+
diff --git a/src/go/doc/testdata/a.2.golden b/src/go/doc/testdata/a.2.golden
new file mode 100644
index 0000000..7e680b8
--- /dev/null
+++ b/src/go/doc/testdata/a.2.golden
@@ -0,0 +1,52 @@
+// comment 0 comment 1
+PACKAGE a
+
+IMPORTPATH
+ testdata/a
+
+FILENAMES
+ testdata/a0.go
+ testdata/a1.go
+
+BUGS .Bugs is now deprecated, please use .Notes instead
+ bug0
+
+ bug1
+
+
+BUGS
+BUG(uid) bug0
+
+BUG(uid) bug1
+
+
+NOTES
+NOTE(uid)
+
+NOTE(foo) 1 of 4 - this is the first line of note 1
+ - note 1 continues on this 2nd line
+ - note 1 continues on this 3rd line
+
+NOTE(foo) 2 of 4
+
+NOTE(bar) 3 of 4
+
+NOTE(bar) 4 of 4
+ - this is the last line of note 4
+
+NOTE(bam) This note which contains a (parenthesized) subphrase
+ must appear in its entirety.
+
+NOTE(xxx) The ':' after the marker and uid is optional.
+
+
+SECBUGS
+SECBUG(uid) sec hole 0
+ need to fix asap
+
+
+TODOS
+TODO(uid) todo0
+
+TODO(uid) todo1
+
diff --git a/src/go/doc/testdata/a0.go b/src/go/doc/testdata/a0.go
new file mode 100644
index 0000000..2420c8a
--- /dev/null
+++ b/src/go/doc/testdata/a0.go
@@ -0,0 +1,40 @@
+// Copyright 2012 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.
+
+// comment 0
+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
+
+// Multiple notes may be in the same comment group and should be
+// recognized individually. Notes may start in the middle of a
+// comment group as long as they start at the beginning of an
+// individual comment.
+//
+// NOTE(foo): 1 of 4 - this is the first line of note 1
+// - note 1 continues on this 2nd line
+// - note 1 continues on this 3rd line
+// NOTE(foo): 2 of 4
+// NOTE(bar): 3 of 4
+/* NOTE(bar): 4 of 4 */
+// - this is the last line of note 4
+//
+//
+
+// NOTE(bam): This note which contains a (parenthesized) subphrase
+// must appear in its entirety.
+
+// NOTE(xxx) The ':' after the marker and uid is optional.
+
+// NOTE(): NO uid - should not show up.
+// NOTE() NO uid - should not show up.
diff --git a/src/go/doc/testdata/a1.go b/src/go/doc/testdata/a1.go
new file mode 100644
index 0000000..9fad1e0
--- /dev/null
+++ b/src/go/doc/testdata/a1.go
@@ -0,0 +1,12 @@
+// Copyright 2012 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.
+
+// comment 1
+package a
+
+//BUG(uid): bug1
+
+//TODO(uid): todo1
+
+//TODO(): ignored
diff --git a/src/go/doc/testdata/b.0.golden b/src/go/doc/testdata/b.0.golden
new file mode 100644
index 0000000..9d93392
--- /dev/null
+++ b/src/go/doc/testdata/b.0.golden
@@ -0,0 +1,71 @@
+//
+PACKAGE b
+
+IMPORTPATH
+ testdata/b
+
+IMPORTS
+ a
+
+FILENAMES
+ testdata/b.go
+
+CONSTANTS
+ //
+ const (
+ C1 notExported = iota
+ C2
+
+ C4
+ C5
+ )
+
+ //
+ const C notExported = 0
+
+ //
+ const Pi = 3.14 // Pi
+
+
+VARIABLES
+ //
+ var (
+ U1, U2, U4, U5 notExported
+
+ U7 notExported = 7
+ )
+
+ //
+ var MaxInt int // MaxInt
+
+ //
+ var V notExported
+
+ //
+ var V1, V2, V4, V5 notExported
+
+
+FUNCTIONS
+ //
+ func F(x int) int
+
+ //
+ func F1() notExported
+
+ // Always under the package functions list.
+ func NotAFactory() int
+
+ // Associated with uint type if AllDecls is set.
+ func UintFactory() uint
+
+
+TYPES
+ //
+ type T struct{} // T
+
+ //
+ var V T // v
+
+ //
+ func (x *T) M()
+
diff --git a/src/go/doc/testdata/b.1.golden b/src/go/doc/testdata/b.1.golden
new file mode 100644
index 0000000..66c47b5
--- /dev/null
+++ b/src/go/doc/testdata/b.1.golden
@@ -0,0 +1,83 @@
+//
+PACKAGE b
+
+IMPORTPATH
+ testdata/b
+
+IMPORTS
+ a
+
+FILENAMES
+ testdata/b.go
+
+CONSTANTS
+ //
+ const Pi = 3.14 // Pi
+
+
+VARIABLES
+ //
+ var MaxInt int // MaxInt
+
+
+FUNCTIONS
+ //
+ func F(x int) int
+
+ // Always under the package functions list.
+ func NotAFactory() int
+
+
+TYPES
+ //
+ type T struct{} // T
+
+ //
+ var V T // v
+
+ //
+ func (x *T) M()
+
+ //
+ type notExported int
+
+ //
+ const (
+ C1 notExported = iota
+ C2
+ c3
+ C4
+ C5
+ )
+
+ //
+ const C notExported = 0
+
+ //
+ var (
+ U1, U2, u3, U4, U5 notExported
+ u6 notExported
+ U7 notExported = 7
+ )
+
+ //
+ var V notExported
+
+ //
+ var V1, V2, v3, V4, V5 notExported
+
+ //
+ func F1() notExported
+
+ //
+ func f2() notExported
+
+ // Should only appear if AllDecls is set.
+ type uint struct{} // overrides a predeclared type uint
+
+ // Associated with uint type if AllDecls is set.
+ func UintFactory() uint
+
+ // Associated with uint type if AllDecls is set.
+ func uintFactory() uint
+
diff --git a/src/go/doc/testdata/b.2.golden b/src/go/doc/testdata/b.2.golden
new file mode 100644
index 0000000..9d93392
--- /dev/null
+++ b/src/go/doc/testdata/b.2.golden
@@ -0,0 +1,71 @@
+//
+PACKAGE b
+
+IMPORTPATH
+ testdata/b
+
+IMPORTS
+ a
+
+FILENAMES
+ testdata/b.go
+
+CONSTANTS
+ //
+ const (
+ C1 notExported = iota
+ C2
+
+ C4
+ C5
+ )
+
+ //
+ const C notExported = 0
+
+ //
+ const Pi = 3.14 // Pi
+
+
+VARIABLES
+ //
+ var (
+ U1, U2, U4, U5 notExported
+
+ U7 notExported = 7
+ )
+
+ //
+ var MaxInt int // MaxInt
+
+ //
+ var V notExported
+
+ //
+ var V1, V2, V4, V5 notExported
+
+
+FUNCTIONS
+ //
+ func F(x int) int
+
+ //
+ func F1() notExported
+
+ // Always under the package functions list.
+ func NotAFactory() int
+
+ // Associated with uint type if AllDecls is set.
+ func UintFactory() uint
+
+
+TYPES
+ //
+ type T struct{} // T
+
+ //
+ var V T // v
+
+ //
+ func (x *T) M()
+
diff --git a/src/go/doc/testdata/b.go b/src/go/doc/testdata/b.go
new file mode 100644
index 0000000..e50663b
--- /dev/null
+++ b/src/go/doc/testdata/b.go
@@ -0,0 +1,58 @@
+// Copyright 2012 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 b
+
+import "a"
+
+// ----------------------------------------------------------------------------
+// Basic declarations
+
+const Pi = 3.14 // Pi
+var MaxInt int // MaxInt
+type T struct{} // T
+var V T // v
+func F(x int) int {} // F
+func (x *T) M() {} // M
+
+// Corner cases: association with (presumed) predeclared types
+
+// Always under the package functions list.
+func NotAFactory() int {}
+
+// Associated with uint type if AllDecls is set.
+func UintFactory() uint {}
+
+// Associated with uint type if AllDecls is set.
+func uintFactory() uint {}
+
+// Should only appear if AllDecls is set.
+type uint struct{} // overrides a predeclared type uint
+
+// ----------------------------------------------------------------------------
+// Exported declarations associated with non-exported types must always be shown.
+
+type notExported int
+
+const C notExported = 0
+
+const (
+ C1 notExported = iota
+ C2
+ c3
+ C4
+ C5
+)
+
+var V notExported
+var V1, V2, v3, V4, V5 notExported
+
+var (
+ U1, U2, u3, U4, U5 notExported
+ u6 notExported
+ U7 notExported = 7
+)
+
+func F1() notExported {}
+func f2() notExported {}
diff --git a/src/go/doc/testdata/benchmark.go b/src/go/doc/testdata/benchmark.go
new file mode 100644
index 0000000..1d581f0
--- /dev/null
+++ b/src/go/doc/testdata/benchmark.go
@@ -0,0 +1,293 @@
+// Copyright 2009 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 testing
+
+import (
+ "flag"
+ "fmt"
+ "os"
+ "runtime"
+ "time"
+)
+
+var matchBenchmarks = flag.String("test.bench", "", "regular expression to select benchmarks to run")
+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.
+type InternalBenchmark struct {
+ Name string
+ F func(b *B)
+}
+
+// B is a type passed to Benchmark functions to manage benchmark
+// timing and to specify the number of iterations to run.
+type B struct {
+ common
+ N int
+ benchmark InternalBenchmark
+ bytes int64
+ timerOn bool
+ result BenchmarkResult
+}
+
+// StartTimer starts timing a test. This function is called automatically
+// before a benchmark starts, but it can also used to resume timing after
+// a call to StopTimer.
+func (b *B) StartTimer() {
+ if !b.timerOn {
+ b.start = time.Now()
+ b.timerOn = true
+ }
+}
+
+// StopTimer stops timing a test. This can be used to pause the timer
+// while performing complex initialization that you don't
+// want to measure.
+func (b *B) StopTimer() {
+ if b.timerOn {
+ b.duration += time.Now().Sub(b.start)
+ b.timerOn = false
+ }
+}
+
+// ResetTimer sets the elapsed benchmark time to zero.
+// It does not affect whether the timer is running.
+func (b *B) ResetTimer() {
+ if b.timerOn {
+ b.start = time.Now()
+ }
+ b.duration = 0
+}
+
+// SetBytes records the number of bytes processed in a single operation.
+// If this is called, the benchmark will report ns/op and MB/s.
+func (b *B) SetBytes(n int64) { b.bytes = n }
+
+func (b *B) nsPerOp() int64 {
+ if b.N <= 0 {
+ return 0
+ }
+ return b.duration.Nanoseconds() / int64(b.N)
+}
+
+// runN runs a single benchmark for the specified number of iterations.
+func (b *B) runN(n int) {
+ // Try to get a comparable environment for each run
+ // by clearing garbage from previous runs.
+ runtime.GC()
+ b.N = n
+ b.ResetTimer()
+ b.StartTimer()
+ b.benchmark.F(b)
+ b.StopTimer()
+}
+
+func min(x, y int) int {
+ if x > y {
+ return y
+ }
+ return x
+}
+
+func max(x, y int) int {
+ if x < y {
+ return y
+ }
+ return x
+}
+
+// roundDown10 rounds a number down to the nearest power of 10.
+func roundDown10(n int) int {
+ var tens = 0
+ // tens = floor(log_10(n))
+ for n > 10 {
+ n = n / 10
+ tens++
+ }
+ // result = 10^tens
+ result := 1
+ for i := 0; i < tens; i++ {
+ result *= 10
+ }
+ return result
+}
+
+// roundUp rounds x up to a number of the form [1eX, 2eX, 5eX].
+func roundUp(n int) int {
+ base := roundDown10(n)
+ if n < (2 * base) {
+ return 2 * base
+ }
+ if n < (5 * base) {
+ return 5 * base
+ }
+ return 10 * base
+}
+
+// run times the benchmark function in a separate goroutine.
+func (b *B) run() BenchmarkResult {
+ go b.launch()
+ <-b.signal
+ return b.result
+}
+
+// launch launches the benchmark function. It gradually increases the number
+// of benchmark iterations until the benchmark runs for a second in order
+// to get a reasonable measurement. It prints timing information in this form
+// testing.BenchmarkHello 100000 19 ns/op
+// launch is run by the fun function as a separate goroutine.
+func (b *B) launch() {
+ // Run the benchmark for a single iteration in case it's expensive.
+ n := 1
+
+ // Signal that we're done whether we return normally
+ // or by FailNow's runtime.Goexit.
+ defer func() {
+ b.signal <- b
+ }()
+
+ b.runN(n)
+ // Run the benchmark for at least the specified amount of time.
+ d := *benchTime
+ for !b.failed && b.duration < d && n < 1e9 {
+ last := n
+ // Predict iterations/sec.
+ if b.nsPerOp() == 0 {
+ n = 1e9
+ } else {
+ n = int(d.Nanoseconds() / b.nsPerOp())
+ }
+ // Run more iterations than we think we'll need for a second (1.5x).
+ // Don't grow too fast in case we had timing errors previously.
+ // Be sure to run at least one more than last time.
+ n = max(min(n+n/2, 100*last), last+1)
+ // Round up to something easy to read.
+ n = roundUp(n)
+ b.runN(n)
+ }
+ b.result = BenchmarkResult{b.N, b.duration, b.bytes}
+}
+
+// The results of a benchmark run.
+type BenchmarkResult struct {
+ N int // The number of iterations.
+ T time.Duration // The total time taken.
+ Bytes int64 // Bytes processed in one iteration.
+}
+
+func (r BenchmarkResult) NsPerOp() int64 {
+ if r.N <= 0 {
+ return 0
+ }
+ return r.T.Nanoseconds() / int64(r.N)
+}
+
+func (r BenchmarkResult) mbPerSec() float64 {
+ if r.Bytes <= 0 || r.T <= 0 || r.N <= 0 {
+ return 0
+ }
+ return (float64(r.Bytes) * float64(r.N) / 1e6) / r.T.Seconds()
+}
+
+func (r BenchmarkResult) String() string {
+ mbs := r.mbPerSec()
+ mb := ""
+ if mbs != 0 {
+ mb = fmt.Sprintf("\t%7.2f MB/s", mbs)
+ }
+ nsop := r.NsPerOp()
+ ns := fmt.Sprintf("%10d ns/op", nsop)
+ if r.N > 0 && nsop < 100 {
+ // The format specifiers here make sure that
+ // the ones digits line up for all three possible formats.
+ if nsop < 10 {
+ ns = fmt.Sprintf("%13.2f ns/op", float64(r.T.Nanoseconds())/float64(r.N))
+ } else {
+ ns = fmt.Sprintf("%12.1f ns/op", float64(r.T.Nanoseconds())/float64(r.N))
+ }
+ }
+ return fmt.Sprintf("%8d\t%s%s", r.N, ns, mb)
+}
+
+// An internal function but exported because it is cross-package; part of the implementation
+// of go test.
+func RunBenchmarks(matchString func(pat, str string) (bool, error), benchmarks []InternalBenchmark) {
+ // If no flag was specified, don't run benchmarks.
+ if len(*matchBenchmarks) == 0 {
+ return
+ }
+ for _, Benchmark := range benchmarks {
+ matched, err := matchString(*matchBenchmarks, Benchmark.Name)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "testing: invalid regexp for -test.bench: %s\n", err)
+ os.Exit(1)
+ }
+ if !matched {
+ continue
+ }
+ for _, procs := range cpuList {
+ runtime.GOMAXPROCS(procs)
+ b := &B{
+ common: common{
+ signal: make(chan interface{}),
+ },
+ benchmark: Benchmark,
+ }
+ benchName := Benchmark.Name
+ if procs != 1 {
+ benchName = fmt.Sprintf("%s-%d", Benchmark.Name, procs)
+ }
+ fmt.Printf("%s\t", benchName)
+ r := b.run()
+ if b.failed {
+ // The output could be very long here, but probably isn't.
+ // We print it all, regardless, because we don't want to trim the reason
+ // the benchmark failed.
+ fmt.Printf("--- FAIL: %s\n%s", benchName, b.output)
+ continue
+ }
+ fmt.Printf("%v\n", r)
+ // Unlike with tests, we ignore the -chatty flag and always print output for
+ // benchmarks since the output generation time will skew the results.
+ if len(b.output) > 0 {
+ b.trimOutput()
+ fmt.Printf("--- BENCH: %s\n%s", benchName, b.output)
+ }
+ if p := runtime.GOMAXPROCS(-1); p != procs {
+ fmt.Fprintf(os.Stderr, "testing: %s left GOMAXPROCS set to %d\n", benchName, p)
+ }
+ }
+ }
+}
+
+// trimOutput shortens the output from a benchmark, which can be very long.
+func (b *B) trimOutput() {
+ // The output is likely to appear multiple times because the benchmark
+ // is run multiple times, but at least it will be seen. This is not a big deal
+ // because benchmarks rarely print, but just in case, we trim it if it's too long.
+ const maxNewlines = 10
+ for nlCount, j := 0, 0; j < len(b.output); j++ {
+ if b.output[j] == '\n' {
+ nlCount++
+ if nlCount >= maxNewlines {
+ b.output = append(b.output[:j], "\n\t... [output truncated]\n"...)
+ break
+ }
+ }
+ }
+}
+
+// Benchmark benchmarks a single function. Useful for creating
+// custom benchmarks that do not use go test.
+func Benchmark(f func(b *B)) BenchmarkResult {
+ b := &B{
+ common: common{
+ signal: make(chan interface{}),
+ },
+ benchmark: InternalBenchmark{"", f},
+ }
+ return b.run()
+}
diff --git a/src/go/doc/testdata/blank.0.golden b/src/go/doc/testdata/blank.0.golden
new file mode 100644
index 0000000..70f2929
--- /dev/null
+++ b/src/go/doc/testdata/blank.0.golden
@@ -0,0 +1,62 @@
+// Package blank is a go/doc test for the handling of _. See issue ...
+PACKAGE blank
+
+IMPORTPATH
+ testdata/blank
+
+IMPORTS
+ os
+
+FILENAMES
+ testdata/blank.go
+
+CONSTANTS
+ // T constants counting from unexported constants.
+ const (
+ C1 T
+ C2
+
+ C3
+
+ C4 int
+ )
+
+ // Constants with a single type that is not propagated.
+ const (
+ Default = 0644
+ Useless = 0312
+ WideOpen = 0777
+ )
+
+ // Constants with an imported type that is propagated.
+ const (
+ M1 os.FileMode
+ M2
+ M3
+ )
+
+ // Package constants.
+ const (
+ I1 int
+ I2
+ )
+
+
+TYPES
+ // S has a padding field.
+ type S struct {
+ H uint32
+
+ A uint8
+ // contains filtered or unexported fields
+ }
+
+ //
+ type T int
+
+ // T constants counting from a blank constant.
+ const (
+ T1 T
+ T2
+ )
+
diff --git a/src/go/doc/testdata/blank.1.golden b/src/go/doc/testdata/blank.1.golden
new file mode 100644
index 0000000..8098cb6
--- /dev/null
+++ b/src/go/doc/testdata/blank.1.golden
@@ -0,0 +1,83 @@
+// Package blank is a go/doc test for the handling of _. See issue ...
+PACKAGE blank
+
+IMPORTPATH
+ testdata/blank
+
+IMPORTS
+ os
+
+FILENAMES
+ testdata/blank.go
+
+CONSTANTS
+ // T constants counting from unexported constants.
+ const (
+ tweedledee T = iota
+ tweedledum
+ C1
+ C2
+ alice
+ C3
+ redQueen int = iota
+ C4
+ )
+
+ // Constants with a single type that is not propagated.
+ const (
+ zero os.FileMode = 0
+ Default = 0644
+ Useless = 0312
+ WideOpen = 0777
+ )
+
+ // Constants with an imported type that is propagated.
+ const (
+ zero os.FileMode = 0
+ M1
+ M2
+ M3
+ )
+
+ // Package constants.
+ const (
+ _ int = iota
+ I1
+ I2
+ )
+
+ // Unexported constants counting from blank iota. See issue 9615.
+ const (
+ _ = iota
+ one = iota + 1
+ )
+
+
+VARIABLES
+ //
+ var _ = T(55)
+
+
+FUNCTIONS
+ //
+ func _()
+
+
+TYPES
+ // S has a padding field.
+ type S struct {
+ H uint32
+ _ uint8
+ A uint8
+ }
+
+ //
+ type T int
+
+ // T constants counting from a blank constant.
+ const (
+ _ T = iota
+ T1
+ T2
+ )
+
diff --git a/src/go/doc/testdata/blank.2.golden b/src/go/doc/testdata/blank.2.golden
new file mode 100644
index 0000000..70f2929
--- /dev/null
+++ b/src/go/doc/testdata/blank.2.golden
@@ -0,0 +1,62 @@
+// Package blank is a go/doc test for the handling of _. See issue ...
+PACKAGE blank
+
+IMPORTPATH
+ testdata/blank
+
+IMPORTS
+ os
+
+FILENAMES
+ testdata/blank.go
+
+CONSTANTS
+ // T constants counting from unexported constants.
+ const (
+ C1 T
+ C2
+
+ C3
+
+ C4 int
+ )
+
+ // Constants with a single type that is not propagated.
+ const (
+ Default = 0644
+ Useless = 0312
+ WideOpen = 0777
+ )
+
+ // Constants with an imported type that is propagated.
+ const (
+ M1 os.FileMode
+ M2
+ M3
+ )
+
+ // Package constants.
+ const (
+ I1 int
+ I2
+ )
+
+
+TYPES
+ // S has a padding field.
+ type S struct {
+ H uint32
+
+ A uint8
+ // contains filtered or unexported fields
+ }
+
+ //
+ type T int
+
+ // T constants counting from a blank constant.
+ const (
+ T1 T
+ T2
+ )
+
diff --git a/src/go/doc/testdata/blank.go b/src/go/doc/testdata/blank.go
new file mode 100644
index 0000000..5ea6186
--- /dev/null
+++ b/src/go/doc/testdata/blank.go
@@ -0,0 +1,75 @@
+// Copyright 2014 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 blank is a go/doc test for the handling of _.
+// See issue 5397.
+package blank
+
+import "os"
+
+type T int
+
+// T constants counting from a blank constant.
+const (
+ _ T = iota
+ T1
+ T2
+)
+
+// T constants counting from unexported constants.
+const (
+ tweedledee T = iota
+ tweedledum
+ C1
+ C2
+ alice
+ C3
+ redQueen int = iota
+ C4
+)
+
+// Constants with a single type that is not propagated.
+const (
+ zero os.FileMode = 0
+ Default = 0644
+ Useless = 0312
+ WideOpen = 0777
+)
+
+// Constants with an imported type that is propagated.
+const (
+ zero os.FileMode = 0
+ M1
+ M2
+ M3
+)
+
+// Package constants.
+const (
+ _ int = iota
+ I1
+ I2
+)
+
+// Unexported constants counting from blank iota.
+// See issue 9615.
+const (
+ _ = iota
+ one = iota + 1
+)
+
+// Blanks not in doc output:
+
+// S has a padding field.
+type S struct {
+ H uint32
+ _ uint8
+ A uint8
+}
+
+func _() {}
+
+type _ T
+
+var _ = T(55)
diff --git a/src/go/doc/testdata/bugpara.0.golden b/src/go/doc/testdata/bugpara.0.golden
new file mode 100644
index 0000000..5804859
--- /dev/null
+++ b/src/go/doc/testdata/bugpara.0.golden
@@ -0,0 +1,20 @@
+//
+PACKAGE bugpara
+
+IMPORTPATH
+ testdata/bugpara
+
+FILENAMES
+ testdata/bugpara.go
+
+BUGS .Bugs is now deprecated, please use .Notes instead
+ Sometimes bugs have multiple paragraphs.
+
+ Like this one.
+
+
+BUGS
+BUG(rsc) Sometimes bugs have multiple paragraphs.
+
+ Like this one.
+
diff --git a/src/go/doc/testdata/bugpara.1.golden b/src/go/doc/testdata/bugpara.1.golden
new file mode 100644
index 0000000..5804859
--- /dev/null
+++ b/src/go/doc/testdata/bugpara.1.golden
@@ -0,0 +1,20 @@
+//
+PACKAGE bugpara
+
+IMPORTPATH
+ testdata/bugpara
+
+FILENAMES
+ testdata/bugpara.go
+
+BUGS .Bugs is now deprecated, please use .Notes instead
+ Sometimes bugs have multiple paragraphs.
+
+ Like this one.
+
+
+BUGS
+BUG(rsc) Sometimes bugs have multiple paragraphs.
+
+ Like this one.
+
diff --git a/src/go/doc/testdata/bugpara.2.golden b/src/go/doc/testdata/bugpara.2.golden
new file mode 100644
index 0000000..5804859
--- /dev/null
+++ b/src/go/doc/testdata/bugpara.2.golden
@@ -0,0 +1,20 @@
+//
+PACKAGE bugpara
+
+IMPORTPATH
+ testdata/bugpara
+
+FILENAMES
+ testdata/bugpara.go
+
+BUGS .Bugs is now deprecated, please use .Notes instead
+ Sometimes bugs have multiple paragraphs.
+
+ Like this one.
+
+
+BUGS
+BUG(rsc) Sometimes bugs have multiple paragraphs.
+
+ Like this one.
+
diff --git a/src/go/doc/testdata/bugpara.go b/src/go/doc/testdata/bugpara.go
new file mode 100644
index 0000000..0360a6f
--- /dev/null
+++ b/src/go/doc/testdata/bugpara.go
@@ -0,0 +1,9 @@
+// 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 bugpara
+
+// BUG(rsc): Sometimes bugs have multiple paragraphs.
+//
+// Like this one.
diff --git a/src/go/doc/testdata/c.0.golden b/src/go/doc/testdata/c.0.golden
new file mode 100644
index 0000000..e21959b
--- /dev/null
+++ b/src/go/doc/testdata/c.0.golden
@@ -0,0 +1,48 @@
+//
+PACKAGE c
+
+IMPORTPATH
+ testdata/c
+
+IMPORTS
+ a
+
+FILENAMES
+ testdata/c.go
+
+TYPES
+ // A (should see this)
+ type A struct{}
+
+ // B (should see this)
+ type B struct{}
+
+ // C (should see this)
+ type C struct{}
+
+ // D (should see this)
+ type D struct{}
+
+ // E1 (should see this)
+ type E1 struct{}
+
+ // E (should see this for E2 and E3)
+ type E2 struct{}
+
+ // E (should see this for E2 and E3)
+ type E3 struct{}
+
+ // E4 (should see this)
+ type E4 struct{}
+
+ //
+ type T1 struct{}
+
+ //
+ func (t1 *T1) M()
+
+ // T2 must not show methods of local T1
+ type T2 struct {
+ a.T1 // not the same as locally declared T1
+ }
+
diff --git a/src/go/doc/testdata/c.1.golden b/src/go/doc/testdata/c.1.golden
new file mode 100644
index 0000000..e21959b
--- /dev/null
+++ b/src/go/doc/testdata/c.1.golden
@@ -0,0 +1,48 @@
+//
+PACKAGE c
+
+IMPORTPATH
+ testdata/c
+
+IMPORTS
+ a
+
+FILENAMES
+ testdata/c.go
+
+TYPES
+ // A (should see this)
+ type A struct{}
+
+ // B (should see this)
+ type B struct{}
+
+ // C (should see this)
+ type C struct{}
+
+ // D (should see this)
+ type D struct{}
+
+ // E1 (should see this)
+ type E1 struct{}
+
+ // E (should see this for E2 and E3)
+ type E2 struct{}
+
+ // E (should see this for E2 and E3)
+ type E3 struct{}
+
+ // E4 (should see this)
+ type E4 struct{}
+
+ //
+ type T1 struct{}
+
+ //
+ func (t1 *T1) M()
+
+ // T2 must not show methods of local T1
+ type T2 struct {
+ a.T1 // not the same as locally declared T1
+ }
+
diff --git a/src/go/doc/testdata/c.2.golden b/src/go/doc/testdata/c.2.golden
new file mode 100644
index 0000000..e21959b
--- /dev/null
+++ b/src/go/doc/testdata/c.2.golden
@@ -0,0 +1,48 @@
+//
+PACKAGE c
+
+IMPORTPATH
+ testdata/c
+
+IMPORTS
+ a
+
+FILENAMES
+ testdata/c.go
+
+TYPES
+ // A (should see this)
+ type A struct{}
+
+ // B (should see this)
+ type B struct{}
+
+ // C (should see this)
+ type C struct{}
+
+ // D (should see this)
+ type D struct{}
+
+ // E1 (should see this)
+ type E1 struct{}
+
+ // E (should see this for E2 and E3)
+ type E2 struct{}
+
+ // E (should see this for E2 and E3)
+ type E3 struct{}
+
+ // E4 (should see this)
+ type E4 struct{}
+
+ //
+ type T1 struct{}
+
+ //
+ func (t1 *T1) M()
+
+ // T2 must not show methods of local T1
+ type T2 struct {
+ a.T1 // not the same as locally declared T1
+ }
+
diff --git a/src/go/doc/testdata/c.go b/src/go/doc/testdata/c.go
new file mode 100644
index 0000000..e0f3919
--- /dev/null
+++ b/src/go/doc/testdata/c.go
@@ -0,0 +1,62 @@
+// Copyright 2012 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 c
+
+import "a"
+
+// ----------------------------------------------------------------------------
+// Test that empty declarations don't cause problems
+
+const ()
+
+type ()
+
+var ()
+
+// ----------------------------------------------------------------------------
+// Test that types with documentation on both, the Decl and the Spec node
+// are handled correctly.
+
+// A (should see this)
+type A struct{}
+
+// B (should see this)
+type (
+ B struct{}
+)
+
+type (
+ // C (should see this)
+ C struct{}
+)
+
+// D (should not see this)
+type (
+ // D (should see this)
+ D struct{}
+)
+
+// E (should see this for E2 and E3)
+type (
+ // E1 (should see this)
+ E1 struct{}
+ E2 struct{}
+ E3 struct{}
+ // E4 (should see this)
+ E4 struct{}
+)
+
+// ----------------------------------------------------------------------------
+// Test that local and imported types are different when
+// handling anonymous fields.
+
+type T1 struct{}
+
+func (t1 *T1) M() {}
+
+// T2 must not show methods of local T1
+type T2 struct {
+ a.T1 // not the same as locally declared T1
+}
diff --git a/src/go/doc/testdata/d.0.golden b/src/go/doc/testdata/d.0.golden
new file mode 100644
index 0000000..c005199
--- /dev/null
+++ b/src/go/doc/testdata/d.0.golden
@@ -0,0 +1,104 @@
+//
+PACKAGE d
+
+IMPORTPATH
+ testdata/d
+
+FILENAMES
+ testdata/d1.go
+ testdata/d2.go
+
+CONSTANTS
+ // CBx constants should appear before CAx constants.
+ const (
+ CB2 = iota // before CB1
+ CB1 // before CB0
+ CB0 // at end
+ )
+
+ // CAx constants should appear after CBx constants.
+ const (
+ CA2 = iota // before CA1
+ CA1 // before CA0
+ CA0 // at end
+ )
+
+ // C0 should be first.
+ const C0 = 0
+
+ // C1 should be second.
+ const C1 = 1
+
+ // C2 should be third.
+ const C2 = 2
+
+ //
+ const (
+ // Single const declarations inside ()'s are considered ungrouped
+ // and show up in sorted order.
+ Cungrouped = 0
+ )
+
+
+VARIABLES
+ // VBx variables should appear before VAx variables.
+ var (
+ VB2 int // before VB1
+ VB1 int // before VB0
+ VB0 int // at end
+ )
+
+ // VAx variables should appear after VBx variables.
+ var (
+ VA2 int // before VA1
+ VA1 int // before VA0
+ VA0 int // at end
+ )
+
+ // V0 should be first.
+ var V0 uintptr
+
+ // V1 should be second.
+ var V1 uint
+
+ // V2 should be third.
+ var V2 int
+
+ //
+ var (
+ // Single var declarations inside ()'s are considered ungrouped
+ // and show up in sorted order.
+ Vungrouped = 0
+ )
+
+
+FUNCTIONS
+ // F0 should be first.
+ func F0()
+
+ // F1 should be second.
+ func F1()
+
+ // F2 should be third.
+ func F2()
+
+
+TYPES
+ // T0 should be first.
+ type T0 struct{}
+
+ // T1 should be second.
+ type T1 struct{}
+
+ // T2 should be third.
+ type T2 struct{}
+
+ // TG0 should be first.
+ type TG0 struct{}
+
+ // TG1 should be second.
+ type TG1 struct{}
+
+ // TG2 should be third.
+ type TG2 struct{}
+
diff --git a/src/go/doc/testdata/d.1.golden b/src/go/doc/testdata/d.1.golden
new file mode 100644
index 0000000..c005199
--- /dev/null
+++ b/src/go/doc/testdata/d.1.golden
@@ -0,0 +1,104 @@
+//
+PACKAGE d
+
+IMPORTPATH
+ testdata/d
+
+FILENAMES
+ testdata/d1.go
+ testdata/d2.go
+
+CONSTANTS
+ // CBx constants should appear before CAx constants.
+ const (
+ CB2 = iota // before CB1
+ CB1 // before CB0
+ CB0 // at end
+ )
+
+ // CAx constants should appear after CBx constants.
+ const (
+ CA2 = iota // before CA1
+ CA1 // before CA0
+ CA0 // at end
+ )
+
+ // C0 should be first.
+ const C0 = 0
+
+ // C1 should be second.
+ const C1 = 1
+
+ // C2 should be third.
+ const C2 = 2
+
+ //
+ const (
+ // Single const declarations inside ()'s are considered ungrouped
+ // and show up in sorted order.
+ Cungrouped = 0
+ )
+
+
+VARIABLES
+ // VBx variables should appear before VAx variables.
+ var (
+ VB2 int // before VB1
+ VB1 int // before VB0
+ VB0 int // at end
+ )
+
+ // VAx variables should appear after VBx variables.
+ var (
+ VA2 int // before VA1
+ VA1 int // before VA0
+ VA0 int // at end
+ )
+
+ // V0 should be first.
+ var V0 uintptr
+
+ // V1 should be second.
+ var V1 uint
+
+ // V2 should be third.
+ var V2 int
+
+ //
+ var (
+ // Single var declarations inside ()'s are considered ungrouped
+ // and show up in sorted order.
+ Vungrouped = 0
+ )
+
+
+FUNCTIONS
+ // F0 should be first.
+ func F0()
+
+ // F1 should be second.
+ func F1()
+
+ // F2 should be third.
+ func F2()
+
+
+TYPES
+ // T0 should be first.
+ type T0 struct{}
+
+ // T1 should be second.
+ type T1 struct{}
+
+ // T2 should be third.
+ type T2 struct{}
+
+ // TG0 should be first.
+ type TG0 struct{}
+
+ // TG1 should be second.
+ type TG1 struct{}
+
+ // TG2 should be third.
+ type TG2 struct{}
+
diff --git a/src/go/doc/testdata/d.2.golden b/src/go/doc/testdata/d.2.golden
new file mode 100644
index 0000000..c005199
--- /dev/null
+++ b/src/go/doc/testdata/d.2.golden
@@ -0,0 +1,104 @@
+//
+PACKAGE d
+
+IMPORTPATH
+ testdata/d
+
+FILENAMES
+ testdata/d1.go
+ testdata/d2.go
+
+CONSTANTS
+ // CBx constants should appear before CAx constants.
+ const (
+ CB2 = iota // before CB1
+ CB1 // before CB0
+ CB0 // at end
+ )
+
+ // CAx constants should appear after CBx constants.
+ const (
+ CA2 = iota // before CA1
+ CA1 // before CA0
+ CA0 // at end
+ )
+
+ // C0 should be first.
+ const C0 = 0
+
+ // C1 should be second.
+ const C1 = 1
+
+ // C2 should be third.
+ const C2 = 2
+
+ //
+ const (
+ // Single const declarations inside ()'s are considered ungrouped
+ // and show up in sorted order.
+ Cungrouped = 0
+ )
+
+
+VARIABLES
+ // VBx variables should appear before VAx variables.
+ var (
+ VB2 int // before VB1
+ VB1 int // before VB0
+ VB0 int // at end
+ )
+
+ // VAx variables should appear after VBx variables.
+ var (
+ VA2 int // before VA1
+ VA1 int // before VA0
+ VA0 int // at end
+ )
+
+ // V0 should be first.
+ var V0 uintptr
+
+ // V1 should be second.
+ var V1 uint
+
+ // V2 should be third.
+ var V2 int
+
+ //
+ var (
+ // Single var declarations inside ()'s are considered ungrouped
+ // and show up in sorted order.
+ Vungrouped = 0
+ )
+
+
+FUNCTIONS
+ // F0 should be first.
+ func F0()
+
+ // F1 should be second.
+ func F1()
+
+ // F2 should be third.
+ func F2()
+
+
+TYPES
+ // T0 should be first.
+ type T0 struct{}
+
+ // T1 should be second.
+ type T1 struct{}
+
+ // T2 should be third.
+ type T2 struct{}
+
+ // TG0 should be first.
+ type TG0 struct{}
+
+ // TG1 should be second.
+ type TG1 struct{}
+
+ // TG2 should be third.
+ type TG2 struct{}
+
diff --git a/src/go/doc/testdata/d1.go b/src/go/doc/testdata/d1.go
new file mode 100644
index 0000000..ebd6941
--- /dev/null
+++ b/src/go/doc/testdata/d1.go
@@ -0,0 +1,57 @@
+// Copyright 2012 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.
+
+// Test cases for sort order of declarations.
+
+package d
+
+// C2 should be third.
+const C2 = 2
+
+// V2 should be third.
+var V2 int
+
+// CBx constants should appear before CAx constants.
+const (
+ CB2 = iota // before CB1
+ CB1 // before CB0
+ CB0 // at end
+)
+
+// VBx variables should appear before VAx variables.
+var (
+ VB2 int // before VB1
+ VB1 int // before VB0
+ VB0 int // at end
+)
+
+const (
+ // Single const declarations inside ()'s are considered ungrouped
+ // and show up in sorted order.
+ Cungrouped = 0
+)
+
+var (
+ // Single var declarations inside ()'s are considered ungrouped
+ // and show up in sorted order.
+ Vungrouped = 0
+)
+
+// T2 should be third.
+type T2 struct{}
+
+// Grouped types are sorted nevertheless.
+type (
+ // TG2 should be third.
+ TG2 struct{}
+
+ // TG1 should be second.
+ TG1 struct{}
+
+ // TG0 should be first.
+ TG0 struct{}
+)
+
+// F2 should be third.
+func F2() {}
diff --git a/src/go/doc/testdata/d2.go b/src/go/doc/testdata/d2.go
new file mode 100644
index 0000000..2f56f4f
--- /dev/null
+++ b/src/go/doc/testdata/d2.go
@@ -0,0 +1,45 @@
+// Copyright 2012 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.
+
+// Test cases for sort order of declarations.
+
+package d
+
+// C1 should be second.
+const C1 = 1
+
+// C0 should be first.
+const C0 = 0
+
+// V1 should be second.
+var V1 uint
+
+// V0 should be first.
+var V0 uintptr
+
+// CAx constants should appear after CBx constants.
+const (
+ CA2 = iota // before CA1
+ CA1 // before CA0
+ CA0 // at end
+)
+
+// VAx variables should appear after VBx variables.
+var (
+ VA2 int // before VA1
+ VA1 int // before VA0
+ VA0 int // at end
+)
+
+// T1 should be second.
+type T1 struct{}
+
+// T0 should be first.
+type T0 struct{}
+
+// F1 should be second.
+func F1() {}
+
+// F0 should be first.
+func F0() {}
diff --git a/src/go/doc/testdata/e.0.golden b/src/go/doc/testdata/e.0.golden
new file mode 100644
index 0000000..6987e58
--- /dev/null
+++ b/src/go/doc/testdata/e.0.golden
@@ -0,0 +1,109 @@
+// The package e is a go/doc test for embedded methods.
+PACKAGE e
+
+IMPORTPATH
+ testdata/e
+
+FILENAMES
+ testdata/e.go
+
+TYPES
+ // T1 has no embedded (level 1) M method due to conflict.
+ type T1 struct {
+ // contains filtered or unexported fields
+ }
+
+ // T2 has only M as top-level method.
+ type T2 struct {
+ // contains filtered or unexported fields
+ }
+
+ // T2.M should appear as method of T2.
+ func (T2) M()
+
+ // T3 has only M as top-level method.
+ type T3 struct {
+ // contains filtered or unexported fields
+ }
+
+ // T3.M should appear as method of T3.
+ func (T3) M()
+
+ //
+ type T4 struct{}
+
+ // T4.M should appear as method of T5 only if AllMethods is set.
+ func (*T4) M()
+
+ //
+ type T5 struct {
+ T4
+ }
+
+ //
+ type U1 struct {
+ *U1
+ }
+
+ // U1.M should appear as method of U1.
+ func (*U1) M()
+
+ //
+ type U2 struct {
+ *U3
+ }
+
+ // U2.M should appear as method of U2 and as method of U3 only if ...
+ func (*U2) M()
+
+ //
+ type U3 struct {
+ *U2
+ }
+
+ // U3.N should appear as method of U3 and as method of U2 only if ...
+ func (*U3) N()
+
+ //
+ type U4 struct {
+ // contains filtered or unexported fields
+ }
+
+ // U4.M should appear as method of U4.
+ func (*U4) M()
+
+ //
+ type V1 struct {
+ *V2
+ *V5
+ }
+
+ //
+ type V2 struct {
+ *V3
+ }
+
+ //
+ type V3 struct {
+ *V4
+ }
+
+ //
+ type V4 struct {
+ *V5
+ }
+
+ // V4.M should appear as method of V2 and V3 if AllMethods is set.
+ func (*V4) M()
+
+ //
+ type V5 struct {
+ *V6
+ }
+
+ //
+ type V6 struct{}
+
+ // V6.M should appear as method of V1 and V5 if AllMethods is set.
+ func (*V6) M()
+
diff --git a/src/go/doc/testdata/e.1.golden b/src/go/doc/testdata/e.1.golden
new file mode 100644
index 0000000..cbe22e0
--- /dev/null
+++ b/src/go/doc/testdata/e.1.golden
@@ -0,0 +1,144 @@
+// The package e is a go/doc test for embedded methods.
+PACKAGE e
+
+IMPORTPATH
+ testdata/e
+
+FILENAMES
+ testdata/e.go
+
+TYPES
+ // T1 has no embedded (level 1) M method due to conflict.
+ type T1 struct {
+ t1
+ t2
+ }
+
+ // T2 has only M as top-level method.
+ type T2 struct {
+ t1
+ }
+
+ // T2.M should appear as method of T2.
+ func (T2) M()
+
+ // T3 has only M as top-level method.
+ type T3 struct {
+ t1e
+ t2e
+ }
+
+ // T3.M should appear as method of T3.
+ func (T3) M()
+
+ //
+ type T4 struct{}
+
+ // T4.M should appear as method of T5 only if AllMethods is set.
+ func (*T4) M()
+
+ //
+ type T5 struct {
+ T4
+ }
+
+ //
+ type U1 struct {
+ *U1
+ }
+
+ // U1.M should appear as method of U1.
+ func (*U1) M()
+
+ //
+ type U2 struct {
+ *U3
+ }
+
+ // U2.M should appear as method of U2 and as method of U3 only if ...
+ func (*U2) M()
+
+ //
+ type U3 struct {
+ *U2
+ }
+
+ // U3.N should appear as method of U3 and as method of U2 only if ...
+ func (*U3) N()
+
+ //
+ type U4 struct {
+ *u5
+ }
+
+ // U4.M should appear as method of U4.
+ func (*U4) M()
+
+ //
+ type V1 struct {
+ *V2
+ *V5
+ }
+
+ //
+ type V2 struct {
+ *V3
+ }
+
+ //
+ type V3 struct {
+ *V4
+ }
+
+ //
+ type V4 struct {
+ *V5
+ }
+
+ // V4.M should appear as method of V2 and V3 if AllMethods is set.
+ func (*V4) M()
+
+ //
+ type V5 struct {
+ *V6
+ }
+
+ //
+ type V6 struct{}
+
+ // V6.M should appear as method of V1 and V5 if AllMethods is set.
+ func (*V6) M()
+
+ //
+ type t1 struct{}
+
+ // t1.M should not appear as method in a Tx type.
+ func (t1) M()
+
+ //
+ type t1e struct {
+ t1
+ }
+
+ // t1.M should not appear as method in a Tx type.
+ func (t1e) M()
+
+ //
+ type t2 struct{}
+
+ // t2.M should not appear as method in a Tx type.
+ func (t2) M()
+
+ //
+ type t2e struct {
+ t2
+ }
+
+ // t2.M should not appear as method in a Tx type.
+ func (t2e) M()
+
+ //
+ type u5 struct {
+ *U4
+ }
+
diff --git a/src/go/doc/testdata/e.2.golden b/src/go/doc/testdata/e.2.golden
new file mode 100644
index 0000000..e7b05e8
--- /dev/null
+++ b/src/go/doc/testdata/e.2.golden
@@ -0,0 +1,130 @@
+// The package e is a go/doc test for embedded methods.
+PACKAGE e
+
+IMPORTPATH
+ testdata/e
+
+FILENAMES
+ testdata/e.go
+
+TYPES
+ // T1 has no embedded (level 1) M method due to conflict.
+ type T1 struct {
+ // contains filtered or unexported fields
+ }
+
+ // T2 has only M as top-level method.
+ type T2 struct {
+ // contains filtered or unexported fields
+ }
+
+ // T2.M should appear as method of T2.
+ func (T2) M()
+
+ // T3 has only M as top-level method.
+ type T3 struct {
+ // contains filtered or unexported fields
+ }
+
+ // T3.M should appear as method of T3.
+ func (T3) M()
+
+ //
+ type T4 struct{}
+
+ // T4.M should appear as method of T5 only if AllMethods is set.
+ func (*T4) M()
+
+ //
+ type T5 struct {
+ T4
+ }
+
+ // T4.M should appear as method of T5 only if AllMethods is set.
+ func (*T5) M()
+
+ //
+ type U1 struct {
+ *U1
+ }
+
+ // U1.M should appear as method of U1.
+ func (*U1) M()
+
+ //
+ type U2 struct {
+ *U3
+ }
+
+ // U2.M should appear as method of U2 and as method of U3 only if ...
+ func (*U2) M()
+
+ // U3.N should appear as method of U3 and as method of U2 only if ...
+ func (U2) N()
+
+ //
+ type U3 struct {
+ *U2
+ }
+
+ // U2.M should appear as method of U2 and as method of U3 only if ...
+ func (U3) M()
+
+ // U3.N should appear as method of U3 and as method of U2 only if ...
+ func (*U3) N()
+
+ //
+ type U4 struct {
+ // contains filtered or unexported fields
+ }
+
+ // U4.M should appear as method of U4.
+ func (*U4) M()
+
+ //
+ type V1 struct {
+ *V2
+ *V5
+ }
+
+ // V6.M should appear as method of V1 and V5 if AllMethods is set.
+ func (V1) M()
+
+ //
+ type V2 struct {
+ *V3
+ }
+
+ // V4.M should appear as method of V2 and V3 if AllMethods is set.
+ func (V2) M()
+
+ //
+ type V3 struct {
+ *V4
+ }
+
+ // V4.M should appear as method of V2 and V3 if AllMethods is set.
+ func (V3) M()
+
+ //
+ type V4 struct {
+ *V5
+ }
+
+ // V4.M should appear as method of V2 and V3 if AllMethods is set.
+ func (*V4) M()
+
+ //
+ type V5 struct {
+ *V6
+ }
+
+ // V6.M should appear as method of V1 and V5 if AllMethods is set.
+ func (V5) M()
+
+ //
+ type V6 struct{}
+
+ // V6.M should appear as method of V1 and V5 if AllMethods is set.
+ func (*V6) M()
+
diff --git a/src/go/doc/testdata/e.go b/src/go/doc/testdata/e.go
new file mode 100644
index 0000000..ec432e3
--- /dev/null
+++ b/src/go/doc/testdata/e.go
@@ -0,0 +1,147 @@
+// Copyright 2012 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.
+
+// The package e is a go/doc test for embedded methods.
+package e
+
+// ----------------------------------------------------------------------------
+// Conflicting methods M must not show up.
+
+type t1 struct{}
+
+// t1.M should not appear as method in a Tx type.
+func (t1) M() {}
+
+type t2 struct{}
+
+// t2.M should not appear as method in a Tx type.
+func (t2) M() {}
+
+// T1 has no embedded (level 1) M method due to conflict.
+type T1 struct {
+ t1
+ t2
+}
+
+// ----------------------------------------------------------------------------
+// Higher-level method M wins over lower-level method M.
+
+// T2 has only M as top-level method.
+type T2 struct {
+ t1
+}
+
+// T2.M should appear as method of T2.
+func (T2) M() {}
+
+// ----------------------------------------------------------------------------
+// Higher-level method M wins over lower-level conflicting methods M.
+
+type t1e struct {
+ t1
+}
+
+type t2e struct {
+ t2
+}
+
+// T3 has only M as top-level method.
+type T3 struct {
+ t1e
+ t2e
+}
+
+// T3.M should appear as method of T3.
+func (T3) M() {}
+
+// ----------------------------------------------------------------------------
+// Don't show conflicting methods M embedded via an exported and non-exported
+// type.
+
+// T1 has no embedded (level 1) M method due to conflict.
+type T4 struct {
+ t2
+ T2
+}
+
+// ----------------------------------------------------------------------------
+// Don't show embedded methods of exported anonymous fields unless AllMethods
+// is set.
+
+type T4 struct{}
+
+// T4.M should appear as method of T5 only if AllMethods is set.
+func (*T4) M() {}
+
+type T5 struct {
+ T4
+}
+
+// ----------------------------------------------------------------------------
+// Recursive type declarations must not lead to endless recursion.
+
+type U1 struct {
+ *U1
+}
+
+// U1.M should appear as method of U1.
+func (*U1) M() {}
+
+type U2 struct {
+ *U3
+}
+
+// U2.M should appear as method of U2 and as method of U3 only if AllMethods is set.
+func (*U2) M() {}
+
+type U3 struct {
+ *U2
+}
+
+// U3.N should appear as method of U3 and as method of U2 only if AllMethods is set.
+func (*U3) N() {}
+
+type U4 struct {
+ *u5
+}
+
+// U4.M should appear as method of U4.
+func (*U4) M() {}
+
+type u5 struct {
+ *U4
+}
+
+// ----------------------------------------------------------------------------
+// A higher-level embedded type (and its methods) wins over the same type (and
+// its methods) embedded at a lower level.
+
+type V1 struct {
+ *V2
+ *V5
+}
+
+type V2 struct {
+ *V3
+}
+
+type V3 struct {
+ *V4
+}
+
+type V4 struct {
+ *V5
+}
+
+type V5 struct {
+ *V6
+}
+
+type V6 struct{}
+
+// V4.M should appear as method of V2 and V3 if AllMethods is set.
+func (*V4) M() {}
+
+// V6.M should appear as method of V1 and V5 if AllMethods is set.
+func (*V6) M() {}
diff --git a/src/go/doc/testdata/error1.0.golden b/src/go/doc/testdata/error1.0.golden
new file mode 100644
index 0000000..6c6fe5d
--- /dev/null
+++ b/src/go/doc/testdata/error1.0.golden
@@ -0,0 +1,30 @@
+//
+PACKAGE error1
+
+IMPORTPATH
+ testdata/error1
+
+FILENAMES
+ testdata/error1.go
+
+TYPES
+ //
+ type I0 interface {
+ // When embedded, the predeclared error interface
+ // must remain visible in interface types.
+ error
+ }
+
+ //
+ type S0 struct {
+ // contains filtered or unexported fields
+ }
+
+ //
+ type T0 struct {
+ ExportedField interface {
+ // error should be visible
+ error
+ }
+ }
+
diff --git a/src/go/doc/testdata/error1.1.golden b/src/go/doc/testdata/error1.1.golden
new file mode 100644
index 0000000..a8dc2e7
--- /dev/null
+++ b/src/go/doc/testdata/error1.1.golden
@@ -0,0 +1,32 @@
+//
+PACKAGE error1
+
+IMPORTPATH
+ testdata/error1
+
+FILENAMES
+ testdata/error1.go
+
+TYPES
+ //
+ type I0 interface {
+ // When embedded, the predeclared error interface
+ // must remain visible in interface types.
+ error
+ }
+
+ //
+ type S0 struct {
+ // In struct types, an embedded error must only be visible
+ // if AllDecls is set.
+ error
+ }
+
+ //
+ type T0 struct {
+ ExportedField interface {
+ // error should be visible
+ error
+ }
+ }
+
diff --git a/src/go/doc/testdata/error1.2.golden b/src/go/doc/testdata/error1.2.golden
new file mode 100644
index 0000000..6c6fe5d
--- /dev/null
+++ b/src/go/doc/testdata/error1.2.golden
@@ -0,0 +1,30 @@
+//
+PACKAGE error1
+
+IMPORTPATH
+ testdata/error1
+
+FILENAMES
+ testdata/error1.go
+
+TYPES
+ //
+ type I0 interface {
+ // When embedded, the predeclared error interface
+ // must remain visible in interface types.
+ error
+ }
+
+ //
+ type S0 struct {
+ // contains filtered or unexported fields
+ }
+
+ //
+ type T0 struct {
+ ExportedField interface {
+ // error should be visible
+ error
+ }
+ }
+
diff --git a/src/go/doc/testdata/error1.go b/src/go/doc/testdata/error1.go
new file mode 100644
index 0000000..3c777a7
--- /dev/null
+++ b/src/go/doc/testdata/error1.go
@@ -0,0 +1,24 @@
+// Copyright 2012 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 error1
+
+type I0 interface {
+ // When embedded, the predeclared error interface
+ // must remain visible in interface types.
+ error
+}
+
+type T0 struct {
+ ExportedField interface {
+ // error should be visible
+ error
+ }
+}
+
+type S0 struct {
+ // In struct types, an embedded error must only be visible
+ // if AllDecls is set.
+ error
+}
diff --git a/src/go/doc/testdata/error2.0.golden b/src/go/doc/testdata/error2.0.golden
new file mode 100644
index 0000000..dedfe41
--- /dev/null
+++ b/src/go/doc/testdata/error2.0.golden
@@ -0,0 +1,27 @@
+//
+PACKAGE error2
+
+IMPORTPATH
+ testdata/error2
+
+FILENAMES
+ testdata/error2.go
+
+TYPES
+ //
+ type I0 interface {
+ // contains filtered or unexported methods
+ }
+
+ //
+ type S0 struct {
+ // contains filtered or unexported fields
+ }
+
+ //
+ type T0 struct {
+ ExportedField interface {
+ // contains filtered or unexported methods
+ }
+ }
+
diff --git a/src/go/doc/testdata/error2.1.golden b/src/go/doc/testdata/error2.1.golden
new file mode 100644
index 0000000..dbcc1b0
--- /dev/null
+++ b/src/go/doc/testdata/error2.1.golden
@@ -0,0 +1,37 @@
+//
+PACKAGE error2
+
+IMPORTPATH
+ testdata/error2
+
+FILENAMES
+ testdata/error2.go
+
+TYPES
+ //
+ type I0 interface {
+ // When embedded, the locally-declared error interface
+ // is only visible if all declarations are shown.
+ error
+ }
+
+ //
+ type S0 struct {
+ // In struct types, an embedded error must only be visible
+ // if AllDecls is set.
+ error
+ }
+
+ //
+ type T0 struct {
+ ExportedField interface {
+ // error should not be visible
+ error
+ }
+ }
+
+ // This error declaration shadows the predeclared error type.
+ type error interface {
+ Error() string
+ }
+
diff --git a/src/go/doc/testdata/error2.2.golden b/src/go/doc/testdata/error2.2.golden
new file mode 100644
index 0000000..dedfe41
--- /dev/null
+++ b/src/go/doc/testdata/error2.2.golden
@@ -0,0 +1,27 @@
+//
+PACKAGE error2
+
+IMPORTPATH
+ testdata/error2
+
+FILENAMES
+ testdata/error2.go
+
+TYPES
+ //
+ type I0 interface {
+ // contains filtered or unexported methods
+ }
+
+ //
+ type S0 struct {
+ // contains filtered or unexported fields
+ }
+
+ //
+ type T0 struct {
+ ExportedField interface {
+ // contains filtered or unexported methods
+ }
+ }
+
diff --git a/src/go/doc/testdata/error2.go b/src/go/doc/testdata/error2.go
new file mode 100644
index 0000000..6ee96c2
--- /dev/null
+++ b/src/go/doc/testdata/error2.go
@@ -0,0 +1,29 @@
+// Copyright 2012 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 error2
+
+type I0 interface {
+ // When embedded, the locally-declared error interface
+ // is only visible if all declarations are shown.
+ error
+}
+
+type T0 struct {
+ ExportedField interface {
+ // error should not be visible
+ error
+ }
+}
+
+type S0 struct {
+ // In struct types, an embedded error must only be visible
+ // if AllDecls is set.
+ error
+}
+
+// This error declaration shadows the predeclared error type.
+type error interface {
+ Error() string
+}
diff --git a/src/go/doc/testdata/example.go b/src/go/doc/testdata/example.go
new file mode 100644
index 0000000..fdeda13
--- /dev/null
+++ b/src/go/doc/testdata/example.go
@@ -0,0 +1,81 @@
+// Copyright 2009 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 testing
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "os"
+ "strings"
+ "time"
+)
+
+type InternalExample struct {
+ Name string
+ F func()
+ Output string
+}
+
+func RunExamples(examples []InternalExample) (ok bool) {
+ ok = true
+
+ var eg InternalExample
+
+ stdout, stderr := os.Stdout, os.Stderr
+ defer func() {
+ os.Stdout, os.Stderr = stdout, stderr
+ if e := recover(); e != nil {
+ fmt.Printf("--- FAIL: %s\npanic: %v\n", eg.Name, e)
+ os.Exit(1)
+ }
+ }()
+
+ for _, eg = range examples {
+ if *chatty {
+ fmt.Printf("=== RUN: %s\n", eg.Name)
+ }
+
+ // capture stdout and stderr
+ r, w, err := os.Pipe()
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+ os.Stdout, os.Stderr = w, w
+ outC := make(chan string)
+ go func() {
+ buf := new(bytes.Buffer)
+ _, err := io.Copy(buf, r)
+ if err != nil {
+ fmt.Fprintf(stderr, "testing: copying pipe: %v\n", err)
+ os.Exit(1)
+ }
+ outC <- buf.String()
+ }()
+
+ // run example
+ t0 := time.Now()
+ eg.F()
+ dt := time.Now().Sub(t0)
+
+ // close pipe, restore stdout/stderr, get output
+ w.Close()
+ os.Stdout, os.Stderr = stdout, stderr
+ out := <-outC
+
+ // report any errors
+ tstr := fmt.Sprintf("(%.2f seconds)", dt.Seconds())
+ if g, e := strings.TrimSpace(out), strings.TrimSpace(eg.Output); g != e {
+ fmt.Printf("--- FAIL: %s %s\ngot:\n%s\nwant:\n%s\n",
+ eg.Name, tstr, g, e)
+ ok = false
+ } else if *chatty {
+ fmt.Printf("--- PASS: %s %s\n", eg.Name, tstr)
+ }
+ }
+
+ return
+}
diff --git a/src/go/doc/testdata/f.0.golden b/src/go/doc/testdata/f.0.golden
new file mode 100644
index 0000000..8175901
--- /dev/null
+++ b/src/go/doc/testdata/f.0.golden
@@ -0,0 +1,13 @@
+// The package f is a go/doc test for functions and factory ...
+PACKAGE f
+
+IMPORTPATH
+ testdata/f
+
+FILENAMES
+ testdata/f.go
+
+FUNCTIONS
+ // Exported must always be visible. Was issue 2824.
+ func Exported() private
+
diff --git a/src/go/doc/testdata/f.1.golden b/src/go/doc/testdata/f.1.golden
new file mode 100644
index 0000000..ba68e88
--- /dev/null
+++ b/src/go/doc/testdata/f.1.golden
@@ -0,0 +1,16 @@
+// The package f is a go/doc test for functions and factory ...
+PACKAGE f
+
+IMPORTPATH
+ testdata/f
+
+FILENAMES
+ testdata/f.go
+
+TYPES
+ //
+ type private struct{}
+
+ // Exported must always be visible. Was issue 2824.
+ func Exported() private
+
diff --git a/src/go/doc/testdata/f.2.golden b/src/go/doc/testdata/f.2.golden
new file mode 100644
index 0000000..8175901
--- /dev/null
+++ b/src/go/doc/testdata/f.2.golden
@@ -0,0 +1,13 @@
+// The package f is a go/doc test for functions and factory ...
+PACKAGE f
+
+IMPORTPATH
+ testdata/f
+
+FILENAMES
+ testdata/f.go
+
+FUNCTIONS
+ // Exported must always be visible. Was issue 2824.
+ func Exported() private
+
diff --git a/src/go/doc/testdata/f.go b/src/go/doc/testdata/f.go
new file mode 100644
index 0000000..7e9add9
--- /dev/null
+++ b/src/go/doc/testdata/f.go
@@ -0,0 +1,14 @@
+// Copyright 2012 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.
+
+// The package f is a go/doc test for functions and factory methods.
+package f
+
+// ----------------------------------------------------------------------------
+// Factory functions for non-exported types must not get lost.
+
+type private struct{}
+
+// Exported must always be visible. Was issue 2824.
+func Exported() private {}
diff --git a/src/go/doc/testdata/g.0.golden b/src/go/doc/testdata/g.0.golden
new file mode 100644
index 0000000..487cf06
--- /dev/null
+++ b/src/go/doc/testdata/g.0.golden
@@ -0,0 +1,32 @@
+// The package g is a go/doc test for mixed exported/unexported ...
+PACKAGE g
+
+IMPORTPATH
+ testdata/g
+
+FILENAMES
+ testdata/g.go
+
+CONSTANTS
+ //
+ const (
+ A, _ = iota, iota
+ _, D
+ E, _
+ G, H
+ )
+
+
+VARIABLES
+ //
+ var (
+ _, C2, _ = 1, 2, 3
+ C4, _, C6 = 4, 5, 6
+ _, C8, _ = 7, 8, 9
+ )
+
+ //
+ var (
+ _, X = f()
+ )
+
diff --git a/src/go/doc/testdata/g.1.golden b/src/go/doc/testdata/g.1.golden
new file mode 100644
index 0000000..438441a
--- /dev/null
+++ b/src/go/doc/testdata/g.1.golden
@@ -0,0 +1,34 @@
+// The package g is a go/doc test for mixed exported/unexported ...
+PACKAGE g
+
+IMPORTPATH
+ testdata/g
+
+FILENAMES
+ testdata/g.go
+
+CONSTANTS
+ //
+ const (
+ A, b = iota, iota
+ c, D
+ E, f
+ G, H
+ )
+
+
+VARIABLES
+ //
+ var (
+ c1, C2, c3 = 1, 2, 3
+ C4, c5, C6 = 4, 5, 6
+ c7, C8, c9 = 7, 8, 9
+ xx, yy, zz = 0, 0, 0 // all unexported and hidden
+ )
+
+ //
+ var (
+ x, X = f()
+ y, z = f()
+ )
+
diff --git a/src/go/doc/testdata/g.2.golden b/src/go/doc/testdata/g.2.golden
new file mode 100644
index 0000000..487cf06
--- /dev/null
+++ b/src/go/doc/testdata/g.2.golden
@@ -0,0 +1,32 @@
+// The package g is a go/doc test for mixed exported/unexported ...
+PACKAGE g
+
+IMPORTPATH
+ testdata/g
+
+FILENAMES
+ testdata/g.go
+
+CONSTANTS
+ //
+ const (
+ A, _ = iota, iota
+ _, D
+ E, _
+ G, H
+ )
+
+
+VARIABLES
+ //
+ var (
+ _, C2, _ = 1, 2, 3
+ C4, _, C6 = 4, 5, 6
+ _, C8, _ = 7, 8, 9
+ )
+
+ //
+ var (
+ _, X = f()
+ )
+
diff --git a/src/go/doc/testdata/g.go b/src/go/doc/testdata/g.go
new file mode 100644
index 0000000..ceeb417
--- /dev/null
+++ b/src/go/doc/testdata/g.go
@@ -0,0 +1,25 @@
+// Copyright 2018 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.
+
+// The package g is a go/doc test for mixed exported/unexported values.
+package g
+
+const (
+ A, b = iota, iota
+ c, D
+ E, f
+ G, H
+)
+
+var (
+ c1, C2, c3 = 1, 2, 3
+ C4, c5, C6 = 4, 5, 6
+ c7, C8, c9 = 7, 8, 9
+ xx, yy, zz = 0, 0, 0 // all unexported and hidden
+)
+
+var (
+ x, X = f()
+ y, z = f()
+)
diff --git a/src/go/doc/testdata/issue12839.0.golden b/src/go/doc/testdata/issue12839.0.golden
new file mode 100644
index 0000000..6b59774
--- /dev/null
+++ b/src/go/doc/testdata/issue12839.0.golden
@@ -0,0 +1,51 @@
+// Package issue12839 is a go/doc test to test association of a ...
+PACKAGE issue12839
+
+IMPORTPATH
+ testdata/issue12839
+
+IMPORTS
+ p
+
+FILENAMES
+ testdata/issue12839.go
+
+FUNCTIONS
+ // F1 should not be associated with T1
+ func F1() (*T1, *T2)
+
+ // F10 should not be associated with T1.
+ func F10() (T1, T2, error)
+
+ // F4 should not be associated with a type (same as F1)
+ func F4() (a T1, b T2)
+
+ // F9 should not be associated with T1.
+ func F9() (int, T1, T2)
+
+
+TYPES
+ //
+ type T1 struct{}
+
+ // F2 should be associated with T1
+ func F2() (a, b, c T1)
+
+ // F3 should be associated with T1 because b.T3 is from a ...
+ func F3() (a T1, b p.T3)
+
+ // F5 should be associated with T1.
+ func F5() (T1, error)
+
+ // F6 should be associated with T1.
+ func F6() (*T1, error)
+
+ // F7 should be associated with T1.
+ func F7() (T1, string)
+
+ // F8 should be associated with T1.
+ func F8() (int, T1, string)
+
+ //
+ type T2 struct{}
+
diff --git a/src/go/doc/testdata/issue12839.1.golden b/src/go/doc/testdata/issue12839.1.golden
new file mode 100644
index 0000000..4b9b9f6
--- /dev/null
+++ b/src/go/doc/testdata/issue12839.1.golden
@@ -0,0 +1,54 @@
+// Package issue12839 is a go/doc test to test association of a ...
+PACKAGE issue12839
+
+IMPORTPATH
+ testdata/issue12839
+
+IMPORTS
+ p
+
+FILENAMES
+ testdata/issue12839.go
+
+FUNCTIONS
+ // F1 should not be associated with T1
+ func F1() (*T1, *T2)
+
+ // F10 should not be associated with T1.
+ func F10() (T1, T2, error)
+
+ // F4 should not be associated with a type (same as F1)
+ func F4() (a T1, b T2)
+
+ // F9 should not be associated with T1.
+ func F9() (int, T1, T2)
+
+
+TYPES
+ //
+ type T1 struct{}
+
+ // F2 should be associated with T1
+ func F2() (a, b, c T1)
+
+ // F3 should be associated with T1 because b.T3 is from a ...
+ func F3() (a T1, b p.T3)
+
+ // F5 should be associated with T1.
+ func F5() (T1, error)
+
+ // F6 should be associated with T1.
+ func F6() (*T1, error)
+
+ // F7 should be associated with T1.
+ func F7() (T1, string)
+
+ // F8 should be associated with T1.
+ func F8() (int, T1, string)
+
+ //
+ func (t T1) hello() string
+
+ //
+ type T2 struct{}
+
diff --git a/src/go/doc/testdata/issue12839.2.golden b/src/go/doc/testdata/issue12839.2.golden
new file mode 100644
index 0000000..6b59774
--- /dev/null
+++ b/src/go/doc/testdata/issue12839.2.golden
@@ -0,0 +1,51 @@
+// Package issue12839 is a go/doc test to test association of a ...
+PACKAGE issue12839
+
+IMPORTPATH
+ testdata/issue12839
+
+IMPORTS
+ p
+
+FILENAMES
+ testdata/issue12839.go
+
+FUNCTIONS
+ // F1 should not be associated with T1
+ func F1() (*T1, *T2)
+
+ // F10 should not be associated with T1.
+ func F10() (T1, T2, error)
+
+ // F4 should not be associated with a type (same as F1)
+ func F4() (a T1, b T2)
+
+ // F9 should not be associated with T1.
+ func F9() (int, T1, T2)
+
+
+TYPES
+ //
+ type T1 struct{}
+
+ // F2 should be associated with T1
+ func F2() (a, b, c T1)
+
+ // F3 should be associated with T1 because b.T3 is from a ...
+ func F3() (a T1, b p.T3)
+
+ // F5 should be associated with T1.
+ func F5() (T1, error)
+
+ // F6 should be associated with T1.
+ func F6() (*T1, error)
+
+ // F7 should be associated with T1.
+ func F7() (T1, string)
+
+ // F8 should be associated with T1.
+ func F8() (int, T1, string)
+
+ //
+ type T2 struct{}
+
diff --git a/src/go/doc/testdata/issue12839.go b/src/go/doc/testdata/issue12839.go
new file mode 100644
index 0000000..51c7ac1
--- /dev/null
+++ b/src/go/doc/testdata/issue12839.go
@@ -0,0 +1,69 @@
+// Copyright 2018 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 issue12839 is a go/doc test to test association of a function
+// that returns multiple types.
+// See golang.org/issue/12839.
+// (See also golang.org/issue/27928.)
+package issue12839
+
+import "p"
+
+type T1 struct{}
+
+type T2 struct{}
+
+func (t T1) hello() string {
+ return "hello"
+}
+
+// F1 should not be associated with T1
+func F1() (*T1, *T2) {
+ return &T1{}, &T2{}
+}
+
+// F2 should be associated with T1
+func F2() (a, b, c T1) {
+ return T1{}, T1{}, T1{}
+}
+
+// F3 should be associated with T1 because b.T3 is from a different package
+func F3() (a T1, b p.T3) {
+ return T1{}, p.T3{}
+}
+
+// F4 should not be associated with a type (same as F1)
+func F4() (a T1, b T2) {
+ return T1{}, T2{}
+}
+
+// F5 should be associated with T1.
+func F5() (T1, error) {
+ return T1{}, nil
+}
+
+// F6 should be associated with T1.
+func F6() (*T1, error) {
+ return &T1{}, nil
+}
+
+// F7 should be associated with T1.
+func F7() (T1, string) {
+ return T1{}, nil
+}
+
+// F8 should be associated with T1.
+func F8() (int, T1, string) {
+ return 0, T1{}, nil
+}
+
+// F9 should not be associated with T1.
+func F9() (int, T1, T2) {
+ return 0, T1{}, T2{}
+}
+
+// F10 should not be associated with T1.
+func F10() (T1, T2, error) {
+ return T1{}, T2{}, nil
+}
diff --git a/src/go/doc/testdata/issue13742.0.golden b/src/go/doc/testdata/issue13742.0.golden
new file mode 100644
index 0000000..8dee9aa
--- /dev/null
+++ b/src/go/doc/testdata/issue13742.0.golden
@@ -0,0 +1,25 @@
+//
+PACKAGE issue13742
+
+IMPORTPATH
+ testdata/issue13742
+
+IMPORTS
+ go/ast
+
+FILENAMES
+ testdata/issue13742.go
+
+FUNCTIONS
+ // Both F0 and G0 should appear as functions.
+ func F0(Node)
+
+ // Both F1 and G1 should appear as functions.
+ func F1(ast.Node)
+
+ //
+ func G0() Node
+
+ //
+ func G1() ast.Node
+
diff --git a/src/go/doc/testdata/issue13742.1.golden b/src/go/doc/testdata/issue13742.1.golden
new file mode 100644
index 0000000..8dee9aa
--- /dev/null
+++ b/src/go/doc/testdata/issue13742.1.golden
@@ -0,0 +1,25 @@
+//
+PACKAGE issue13742
+
+IMPORTPATH
+ testdata/issue13742
+
+IMPORTS
+ go/ast
+
+FILENAMES
+ testdata/issue13742.go
+
+FUNCTIONS
+ // Both F0 and G0 should appear as functions.
+ func F0(Node)
+
+ // Both F1 and G1 should appear as functions.
+ func F1(ast.Node)
+
+ //
+ func G0() Node
+
+ //
+ func G1() ast.Node
+
diff --git a/src/go/doc/testdata/issue13742.2.golden b/src/go/doc/testdata/issue13742.2.golden
new file mode 100644
index 0000000..8dee9aa
--- /dev/null
+++ b/src/go/doc/testdata/issue13742.2.golden
@@ -0,0 +1,25 @@
+//
+PACKAGE issue13742
+
+IMPORTPATH
+ testdata/issue13742
+
+IMPORTS
+ go/ast
+
+FILENAMES
+ testdata/issue13742.go
+
+FUNCTIONS
+ // Both F0 and G0 should appear as functions.
+ func F0(Node)
+
+ // Both F1 and G1 should appear as functions.
+ func F1(ast.Node)
+
+ //
+ func G0() Node
+
+ //
+ func G1() ast.Node
+
diff --git a/src/go/doc/testdata/issue13742.go b/src/go/doc/testdata/issue13742.go
new file mode 100644
index 0000000..dbc1941
--- /dev/null
+++ b/src/go/doc/testdata/issue13742.go
@@ -0,0 +1,18 @@
+// Copyright 2016 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 issue13742
+
+import (
+ "go/ast"
+ . "go/ast"
+)
+
+// Both F0 and G0 should appear as functions.
+func F0(Node) {}
+func G0() Node { return nil }
+
+// Both F1 and G1 should appear as functions.
+func F1(ast.Node) {}
+func G1() ast.Node { return nil }
diff --git a/src/go/doc/testdata/issue16153.0.golden b/src/go/doc/testdata/issue16153.0.golden
new file mode 100644
index 0000000..189260b
--- /dev/null
+++ b/src/go/doc/testdata/issue16153.0.golden
@@ -0,0 +1,32 @@
+//
+PACKAGE issue16153
+
+IMPORTPATH
+ testdata/issue16153
+
+FILENAMES
+ testdata/issue16153.go
+
+CONSTANTS
+ //
+ const (
+ X3 int64 = iota
+ Y3 = 1
+ )
+
+ //
+ const (
+ X4 int64 = iota
+ Y4
+ )
+
+ // original test case
+ const (
+ Y1 = 256
+ )
+
+ // variations
+ const (
+ Y2 uint8
+ )
+
diff --git a/src/go/doc/testdata/issue16153.1.golden b/src/go/doc/testdata/issue16153.1.golden
new file mode 100644
index 0000000..803df3e
--- /dev/null
+++ b/src/go/doc/testdata/issue16153.1.golden
@@ -0,0 +1,34 @@
+//
+PACKAGE issue16153
+
+IMPORTPATH
+ testdata/issue16153
+
+FILENAMES
+ testdata/issue16153.go
+
+CONSTANTS
+ // original test case
+ const (
+ x1 uint8 = 255
+ Y1 = 256
+ )
+
+ // variations
+ const (
+ x2 uint8 = 255
+ Y2
+ )
+
+ //
+ const (
+ X3 int64 = iota
+ Y3 = 1
+ )
+
+ //
+ const (
+ X4 int64 = iota
+ Y4
+ )
+
diff --git a/src/go/doc/testdata/issue16153.2.golden b/src/go/doc/testdata/issue16153.2.golden
new file mode 100644
index 0000000..189260b
--- /dev/null
+++ b/src/go/doc/testdata/issue16153.2.golden
@@ -0,0 +1,32 @@
+//
+PACKAGE issue16153
+
+IMPORTPATH
+ testdata/issue16153
+
+FILENAMES
+ testdata/issue16153.go
+
+CONSTANTS
+ //
+ const (
+ X3 int64 = iota
+ Y3 = 1
+ )
+
+ //
+ const (
+ X4 int64 = iota
+ Y4
+ )
+
+ // original test case
+ const (
+ Y1 = 256
+ )
+
+ // variations
+ const (
+ Y2 uint8
+ )
+
diff --git a/src/go/doc/testdata/issue16153.go b/src/go/doc/testdata/issue16153.go
new file mode 100644
index 0000000..528be42
--- /dev/null
+++ b/src/go/doc/testdata/issue16153.go
@@ -0,0 +1,27 @@
+// Copyright 2017 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 issue16153
+
+// original test case
+const (
+ x1 uint8 = 255
+ Y1 = 256
+)
+
+// variations
+const (
+ x2 uint8 = 255
+ Y2
+)
+
+const (
+ X3 int64 = iota
+ Y3 = 1
+)
+
+const (
+ X4 int64 = iota
+ Y4
+)
diff --git a/src/go/doc/testdata/issue17788.0.golden b/src/go/doc/testdata/issue17788.0.golden
new file mode 100644
index 0000000..42c00da
--- /dev/null
+++ b/src/go/doc/testdata/issue17788.0.golden
@@ -0,0 +1,8 @@
+//
+PACKAGE issue17788
+
+IMPORTPATH
+ testdata/issue17788
+
+FILENAMES
+ testdata/issue17788.go
diff --git a/src/go/doc/testdata/issue17788.1.golden b/src/go/doc/testdata/issue17788.1.golden
new file mode 100644
index 0000000..42c00da
--- /dev/null
+++ b/src/go/doc/testdata/issue17788.1.golden
@@ -0,0 +1,8 @@
+//
+PACKAGE issue17788
+
+IMPORTPATH
+ testdata/issue17788
+
+FILENAMES
+ testdata/issue17788.go
diff --git a/src/go/doc/testdata/issue17788.2.golden b/src/go/doc/testdata/issue17788.2.golden
new file mode 100644
index 0000000..42c00da
--- /dev/null
+++ b/src/go/doc/testdata/issue17788.2.golden
@@ -0,0 +1,8 @@
+//
+PACKAGE issue17788
+
+IMPORTPATH
+ testdata/issue17788
+
+FILENAMES
+ testdata/issue17788.go
diff --git a/src/go/doc/testdata/issue17788.go b/src/go/doc/testdata/issue17788.go
new file mode 100644
index 0000000..883ad5f
--- /dev/null
+++ b/src/go/doc/testdata/issue17788.go
@@ -0,0 +1,8 @@
+// Copyright 2016 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 issue17788
+
+func ( /* receiver type */ ) f0() {
+}
diff --git a/src/go/doc/testdata/issue22856.0.golden b/src/go/doc/testdata/issue22856.0.golden
new file mode 100644
index 0000000..a88f43f
--- /dev/null
+++ b/src/go/doc/testdata/issue22856.0.golden
@@ -0,0 +1,45 @@
+//
+PACKAGE issue22856
+
+IMPORTPATH
+ testdata/issue22856
+
+FILENAMES
+ testdata/issue22856.go
+
+FUNCTIONS
+ // NewPointerSliceOfSlice is not a factory function because slices ...
+ func NewPointerSliceOfSlice() [][]*T
+
+ // NewSlice3 is not a factory function because 3 nested slices of ...
+ func NewSlice3() [][][]T
+
+ // NewSliceOfSlice is not a factory function because slices of a ...
+ func NewSliceOfSlice() [][]T
+
+
+TYPES
+ //
+ type T struct{}
+
+ //
+ func New() T
+
+ //
+ func NewArray() [1]T
+
+ //
+ func NewPointer() *T
+
+ //
+ func NewPointerArray() [1]*T
+
+ //
+ func NewPointerOfPointer() **T
+
+ //
+ func NewPointerSlice() []*T
+
+ //
+ func NewSlice() []T
+
diff --git a/src/go/doc/testdata/issue22856.1.golden b/src/go/doc/testdata/issue22856.1.golden
new file mode 100644
index 0000000..a88f43f
--- /dev/null
+++ b/src/go/doc/testdata/issue22856.1.golden
@@ -0,0 +1,45 @@
+//
+PACKAGE issue22856
+
+IMPORTPATH
+ testdata/issue22856
+
+FILENAMES
+ testdata/issue22856.go
+
+FUNCTIONS
+ // NewPointerSliceOfSlice is not a factory function because slices ...
+ func NewPointerSliceOfSlice() [][]*T
+
+ // NewSlice3 is not a factory function because 3 nested slices of ...
+ func NewSlice3() [][][]T
+
+ // NewSliceOfSlice is not a factory function because slices of a ...
+ func NewSliceOfSlice() [][]T
+
+
+TYPES
+ //
+ type T struct{}
+
+ //
+ func New() T
+
+ //
+ func NewArray() [1]T
+
+ //
+ func NewPointer() *T
+
+ //
+ func NewPointerArray() [1]*T
+
+ //
+ func NewPointerOfPointer() **T
+
+ //
+ func NewPointerSlice() []*T
+
+ //
+ func NewSlice() []T
+
diff --git a/src/go/doc/testdata/issue22856.2.golden b/src/go/doc/testdata/issue22856.2.golden
new file mode 100644
index 0000000..a88f43f
--- /dev/null
+++ b/src/go/doc/testdata/issue22856.2.golden
@@ -0,0 +1,45 @@
+//
+PACKAGE issue22856
+
+IMPORTPATH
+ testdata/issue22856
+
+FILENAMES
+ testdata/issue22856.go
+
+FUNCTIONS
+ // NewPointerSliceOfSlice is not a factory function because slices ...
+ func NewPointerSliceOfSlice() [][]*T
+
+ // NewSlice3 is not a factory function because 3 nested slices of ...
+ func NewSlice3() [][][]T
+
+ // NewSliceOfSlice is not a factory function because slices of a ...
+ func NewSliceOfSlice() [][]T
+
+
+TYPES
+ //
+ type T struct{}
+
+ //
+ func New() T
+
+ //
+ func NewArray() [1]T
+
+ //
+ func NewPointer() *T
+
+ //
+ func NewPointerArray() [1]*T
+
+ //
+ func NewPointerOfPointer() **T
+
+ //
+ func NewPointerSlice() []*T
+
+ //
+ func NewSlice() []T
+
diff --git a/src/go/doc/testdata/issue22856.go b/src/go/doc/testdata/issue22856.go
new file mode 100644
index 0000000..f456998
--- /dev/null
+++ b/src/go/doc/testdata/issue22856.go
@@ -0,0 +1,27 @@
+// Copyright 2017 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 issue22856
+
+type T struct{}
+
+func New() T { return T{} }
+func NewPointer() *T { return &T{} }
+func NewPointerSlice() []*T { return []*T{&T{}} }
+func NewSlice() []T { return []T{T{}} }
+func NewPointerOfPointer() **T { x := &T{}; return &x }
+func NewArray() [1]T { return [1]T{T{}} }
+func NewPointerArray() [1]*T { return [1]*T{&T{}} }
+
+// NewSliceOfSlice is not a factory function because slices of a slice of
+// type *T are not factory functions of type T.
+func NewSliceOfSlice() [][]T { return []T{[]T{}} }
+
+// NewPointerSliceOfSlice is not a factory function because slices of a
+// slice of type *T are not factory functions of type T.
+func NewPointerSliceOfSlice() [][]*T { return []*T{[]*T{}} }
+
+// NewSlice3 is not a factory function because 3 nested slices of type T
+// are not factory functions of type T.
+func NewSlice3() [][][]T { return []T{[]T{[]T{}}} }
diff --git a/src/go/doc/testdata/predeclared.0.golden b/src/go/doc/testdata/predeclared.0.golden
new file mode 100644
index 0000000..9f37b06
--- /dev/null
+++ b/src/go/doc/testdata/predeclared.0.golden
@@ -0,0 +1,8 @@
+// Package predeclared is a go/doc test for handling of exported ...
+PACKAGE predeclared
+
+IMPORTPATH
+ testdata/predeclared
+
+FILENAMES
+ testdata/predeclared.go
diff --git a/src/go/doc/testdata/predeclared.1.golden b/src/go/doc/testdata/predeclared.1.golden
new file mode 100644
index 0000000..2ff8ee6
--- /dev/null
+++ b/src/go/doc/testdata/predeclared.1.golden
@@ -0,0 +1,22 @@
+// Package predeclared is a go/doc test for handling of exported ...
+PACKAGE predeclared
+
+IMPORTPATH
+ testdata/predeclared
+
+FILENAMES
+ testdata/predeclared.go
+
+TYPES
+ //
+ type bool int
+
+ // Must not be visible.
+ func (b bool) String() string
+
+ //
+ type error struct{}
+
+ // Must not be visible.
+ func (e error) Error() string
+
diff --git a/src/go/doc/testdata/predeclared.2.golden b/src/go/doc/testdata/predeclared.2.golden
new file mode 100644
index 0000000..9f37b06
--- /dev/null
+++ b/src/go/doc/testdata/predeclared.2.golden
@@ -0,0 +1,8 @@
+// Package predeclared is a go/doc test for handling of exported ...
+PACKAGE predeclared
+
+IMPORTPATH
+ testdata/predeclared
+
+FILENAMES
+ testdata/predeclared.go
diff --git a/src/go/doc/testdata/predeclared.go b/src/go/doc/testdata/predeclared.go
new file mode 100644
index 0000000..c6dd806
--- /dev/null
+++ b/src/go/doc/testdata/predeclared.go
@@ -0,0 +1,22 @@
+// Copyright 2016 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 predeclared is a go/doc test for handling of
+// exported methods on locally-defined predeclared types.
+// See issue 9860.
+package predeclared
+
+type error struct{}
+
+// Must not be visible.
+func (e error) Error() string {
+ return ""
+}
+
+type bool int
+
+// Must not be visible.
+func (b bool) String() string {
+ return ""
+}
diff --git a/src/go/doc/testdata/template.txt b/src/go/doc/testdata/template.txt
new file mode 100644
index 0000000..1b07382
--- /dev/null
+++ b/src/go/doc/testdata/template.txt
@@ -0,0 +1,68 @@
+{{synopsis .Doc}}
+PACKAGE {{.Name}}
+
+IMPORTPATH
+ {{.ImportPath}}
+
+{{with .Imports}}IMPORTS
+{{range .}} {{.}}
+{{end}}
+{{end}}{{/*
+
+*/}}FILENAMES
+{{range .Filenames}} {{.}}
+{{end}}{{/*
+
+*/}}{{with .Consts}}
+CONSTANTS
+{{range .}} {{synopsis .Doc}}
+ {{node .Decl $.FSet}}
+
+{{end}}{{end}}{{/*
+
+*/}}{{with .Vars}}
+VARIABLES
+{{range .}} {{synopsis .Doc}}
+ {{node .Decl $.FSet}}
+
+{{end}}{{end}}{{/*
+
+*/}}{{with .Funcs}}
+FUNCTIONS
+{{range .}} {{synopsis .Doc}}
+ {{node .Decl $.FSet}}
+
+{{end}}{{end}}{{/*
+
+*/}}{{with .Types}}
+TYPES
+{{range .}} {{synopsis .Doc}}
+ {{node .Decl $.FSet}}
+
+{{range .Consts}} {{synopsis .Doc}}
+ {{node .Decl $.FSet}}
+
+{{end}}{{/*
+
+*/}}{{range .Vars}} {{synopsis .Doc}}
+ {{node .Decl $.FSet}}
+
+{{end}}{{/*
+
+*/}}{{range .Funcs}} {{synopsis .Doc}}
+ {{node .Decl $.FSet}}
+
+{{end}}{{/*
+
+*/}}{{range .Methods}} {{synopsis .Doc}}
+ {{node .Decl $.FSet}}
+
+{{end}}{{end}}{{end}}{{/*
+
+*/}}{{with .Bugs}}
+BUGS .Bugs is now deprecated, please use .Notes instead
+{{range .}}{{indent "\t" .}}
+{{end}}{{end}}{{with .Notes}}{{range $marker, $content := .}}
+{{$marker}}S
+{{range $content}}{{$marker}}({{.UID}}){{indent "\t" .Body}}
+{{end}}{{end}}{{end}} \ No newline at end of file
diff --git a/src/go/doc/testdata/testing.0.golden b/src/go/doc/testdata/testing.0.golden
new file mode 100644
index 0000000..83cf37c
--- /dev/null
+++ b/src/go/doc/testdata/testing.0.golden
@@ -0,0 +1,156 @@
+// Package testing provides support for automated testing of Go ...
+PACKAGE testing
+
+IMPORTPATH
+ testdata/testing
+
+IMPORTS
+ bytes
+ flag
+ fmt
+ io
+ os
+ runtime
+ runtime/pprof
+ strconv
+ strings
+ time
+
+FILENAMES
+ testdata/benchmark.go
+ testdata/example.go
+ testdata/testing.go
+
+FUNCTIONS
+ // An internal function but exported because it is cross-package; ...
+ func Main(matchString func(pat, str string) (bool, error), tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample)
+
+ // An internal function but exported because it is cross-package; ...
+ func RunBenchmarks(matchString func(pat, str string) (bool, error), benchmarks []InternalBenchmark)
+
+ //
+ func RunExamples(examples []InternalExample) (ok bool)
+
+ //
+ func RunTests(matchString func(pat, str string) (bool, error), tests []InternalTest) (ok bool)
+
+ // Short reports whether the -test.short flag is set.
+ func Short() bool
+
+
+TYPES
+ // B is a type passed to Benchmark functions to manage benchmark ...
+ type B struct {
+ N int
+ // contains filtered or unexported fields
+ }
+
+ // Error is equivalent to Log() followed by Fail().
+ func (c *B) Error(args ...interface{})
+
+ // Errorf is equivalent to Logf() followed by Fail().
+ func (c *B) Errorf(format string, args ...interface{})
+
+ // Fail marks the function as having failed but continues ...
+ func (c *B) Fail()
+
+ // FailNow marks the function as having failed and stops its ...
+ func (c *B) FailNow()
+
+ // Failed reports whether the function has failed.
+ func (c *B) Failed() bool
+
+ // Fatal is equivalent to Log() followed by FailNow().
+ func (c *B) Fatal(args ...interface{})
+
+ // Fatalf is equivalent to Logf() followed by FailNow().
+ func (c *B) Fatalf(format string, args ...interface{})
+
+ // Log formats its arguments using default formatting, analogous ...
+ func (c *B) Log(args ...interface{})
+
+ // Logf formats its arguments according to the format, analogous ...
+ func (c *B) Logf(format string, args ...interface{})
+
+ // ResetTimer sets the elapsed benchmark time to zero. It does not ...
+ func (b *B) ResetTimer()
+
+ // SetBytes records the number of bytes processed in a single ...
+ func (b *B) SetBytes(n int64)
+
+ // StartTimer starts timing a test. This function is called ...
+ func (b *B) StartTimer()
+
+ // StopTimer stops timing a test. This can be used to pause the ...
+ func (b *B) StopTimer()
+
+ // The results of a benchmark run.
+ type BenchmarkResult struct {
+ N int // The number of iterations.
+ T time.Duration // The total time taken.
+ Bytes int64 // Bytes processed in one iteration.
+ }
+
+ // Benchmark benchmarks a single function. Useful for creating ...
+ func Benchmark(f func(b *B)) BenchmarkResult
+
+ //
+ func (r BenchmarkResult) NsPerOp() int64
+
+ //
+ func (r BenchmarkResult) String() string
+
+ // An internal type but exported because it is cross-package; part ...
+ type InternalBenchmark struct {
+ Name string
+ F func(b *B)
+ }
+
+ //
+ type InternalExample struct {
+ Name string
+ F func()
+ Output string
+ }
+
+ // An internal type but exported because it is cross-package; part ...
+ type InternalTest struct {
+ Name string
+ F func(*T)
+ }
+
+ // T is a type passed to Test functions to manage test state and ...
+ type T struct {
+ // contains filtered or unexported fields
+ }
+
+ // Error is equivalent to Log() followed by Fail().
+ func (c *T) Error(args ...interface{})
+
+ // Errorf is equivalent to Logf() followed by Fail().
+ func (c *T) Errorf(format string, args ...interface{})
+
+ // Fail marks the function as having failed but continues ...
+ func (c *T) Fail()
+
+ // FailNow marks the function as having failed and stops its ...
+ func (c *T) FailNow()
+
+ // Failed reports whether the function has failed.
+ func (c *T) Failed() bool
+
+ // Fatal is equivalent to Log() followed by FailNow().
+ func (c *T) Fatal(args ...interface{})
+
+ // Fatalf is equivalent to Logf() followed by FailNow().
+ func (c *T) Fatalf(format string, args ...interface{})
+
+ // Log formats its arguments using default formatting, analogous ...
+ func (c *T) Log(args ...interface{})
+
+ // Logf formats its arguments according to the format, analogous ...
+ func (c *T) Logf(format string, args ...interface{})
+
+ // Parallel signals that this test is to be run in parallel with ...
+ func (t *T) Parallel()
+
diff --git a/src/go/doc/testdata/testing.1.golden b/src/go/doc/testdata/testing.1.golden
new file mode 100644
index 0000000..b9d1451
--- /dev/null
+++ b/src/go/doc/testdata/testing.1.golden
@@ -0,0 +1,298 @@
+// Package testing provides support for automated testing of Go ...
+PACKAGE testing
+
+IMPORTPATH
+ testdata/testing
+
+IMPORTS
+ bytes
+ flag
+ fmt
+ io
+ os
+ runtime
+ runtime/pprof
+ strconv
+ strings
+ time
+
+FILENAMES
+ testdata/benchmark.go
+ testdata/example.go
+ testdata/testing.go
+
+VARIABLES
+ //
+ var (
+ // The short flag requests that tests run more quickly, but its functionality
+ // is provided by test writers themselves. The testing package is just its
+ // home. The all.bash installation script sets it to make installation more
+ // efficient, but by default the flag is off so a plain "go test" will do a
+ // full test of the package.
+ short = flag.Bool("test.short", false, "run smaller test suite to save time")
+
+ // Report as tests are run; default is silent for success.
+ chatty = flag.Bool("test.v", false, "verbose: print additional output")
+ match = flag.String("test.run", "", "regular expression to select tests to run")
+ memProfile = flag.String("test.memprofile", "", "write a memory profile to the named file after execution")
+ memProfileRate = flag.Int("test.memprofilerate", 0, "if >=0, sets runtime.MemProfileRate")
+ cpuProfile = flag.String("test.cpuprofile", "", "write a cpu profile to the named file during execution")
+ timeout = flag.Duration("test.timeout", 0, "if positive, sets an aggregate time limit for all tests")
+ cpuListStr = flag.String("test.cpu", "", "comma-separated list of number of CPUs to use for each test")
+ parallel = flag.Int("test.parallel", runtime.GOMAXPROCS(0), "maximum test parallelism")
+
+ cpuList []int
+ )
+
+ //
+ 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")
+
+ //
+ var timer *time.Timer
+
+
+FUNCTIONS
+ // An internal function but exported because it is cross-package; ...
+ func Main(matchString func(pat, str string) (bool, error), tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample)
+
+ // An internal function but exported because it is cross-package; ...
+ func RunBenchmarks(matchString func(pat, str string) (bool, error), benchmarks []InternalBenchmark)
+
+ //
+ func RunExamples(examples []InternalExample) (ok bool)
+
+ //
+ func RunTests(matchString func(pat, str string) (bool, error), tests []InternalTest) (ok bool)
+
+ // Short reports whether the -test.short flag is set.
+ func Short() bool
+
+ // after runs after all testing.
+ func after()
+
+ // alarm is called if the timeout expires.
+ func alarm()
+
+ // before runs before all testing.
+ func before()
+
+ // decorate inserts the final newline if needed and indentation ...
+ func decorate(s string, addFileLine bool) string
+
+ //
+ func max(x, y int) int
+
+ //
+ func min(x, y int) int
+
+ //
+ func parseCpuList()
+
+ // roundDown10 rounds a number down to the nearest power of 10.
+ func roundDown10(n int) int
+
+ // roundUp rounds x up to a number of the form [1eX, 2eX, 5eX].
+ func roundUp(n int) int
+
+ // startAlarm starts an alarm if requested.
+ func startAlarm()
+
+ // stopAlarm turns off the alarm.
+ func stopAlarm()
+
+ //
+ func tRunner(t *T, test *InternalTest)
+
+
+TYPES
+ // B is a type passed to Benchmark functions to manage benchmark ...
+ type B struct {
+ common
+ N int
+ benchmark InternalBenchmark
+ bytes int64
+ timerOn bool
+ result BenchmarkResult
+ }
+
+ // Error is equivalent to Log() followed by Fail().
+ func (c *B) Error(args ...interface{})
+
+ // Errorf is equivalent to Logf() followed by Fail().
+ func (c *B) Errorf(format string, args ...interface{})
+
+ // Fail marks the function as having failed but continues ...
+ func (c *B) Fail()
+
+ // FailNow marks the function as having failed and stops its ...
+ func (c *B) FailNow()
+
+ // Failed reports whether the function has failed.
+ func (c *B) Failed() bool
+
+ // Fatal is equivalent to Log() followed by FailNow().
+ func (c *B) Fatal(args ...interface{})
+
+ // Fatalf is equivalent to Logf() followed by FailNow().
+ func (c *B) Fatalf(format string, args ...interface{})
+
+ // Log formats its arguments using default formatting, analogous ...
+ func (c *B) Log(args ...interface{})
+
+ // Logf formats its arguments according to the format, analogous ...
+ func (c *B) Logf(format string, args ...interface{})
+
+ // ResetTimer sets the elapsed benchmark time to zero. It does not ...
+ func (b *B) ResetTimer()
+
+ // SetBytes records the number of bytes processed in a single ...
+ func (b *B) SetBytes(n int64)
+
+ // StartTimer starts timing a test. This function is called ...
+ func (b *B) StartTimer()
+
+ // StopTimer stops timing a test. This can be used to pause the ...
+ func (b *B) StopTimer()
+
+ // launch launches the benchmark function. It gradually increases ...
+ func (b *B) launch()
+
+ // log generates the output. It's always at the same stack depth.
+ func (c *B) log(s string)
+
+ //
+ func (b *B) nsPerOp() int64
+
+ // run times the benchmark function in a separate goroutine.
+ func (b *B) run() BenchmarkResult
+
+ // runN runs a single benchmark for the specified number of ...
+ func (b *B) runN(n int)
+
+ // trimOutput shortens the output from a benchmark, which can be ...
+ func (b *B) trimOutput()
+
+ // The results of a benchmark run.
+ type BenchmarkResult struct {
+ N int // The number of iterations.
+ T time.Duration // The total time taken.
+ Bytes int64 // Bytes processed in one iteration.
+ }
+
+ // Benchmark benchmarks a single function. Useful for creating ...
+ func Benchmark(f func(b *B)) BenchmarkResult
+
+ //
+ func (r BenchmarkResult) NsPerOp() int64
+
+ //
+ func (r BenchmarkResult) String() string
+
+ //
+ func (r BenchmarkResult) mbPerSec() float64
+
+ // An internal type but exported because it is cross-package; part ...
+ type InternalBenchmark struct {
+ Name string
+ F func(b *B)
+ }
+
+ //
+ type InternalExample struct {
+ Name string
+ F func()
+ Output string
+ }
+
+ // An internal type but exported because it is cross-package; part ...
+ type InternalTest struct {
+ Name string
+ F func(*T)
+ }
+
+ // T is a type passed to Test functions to manage test state and ...
+ type T struct {
+ common
+ name string // Name of test.
+ startParallel chan bool // Parallel tests will wait on this.
+ }
+
+ // Error is equivalent to Log() followed by Fail().
+ func (c *T) Error(args ...interface{})
+
+ // Errorf is equivalent to Logf() followed by Fail().
+ func (c *T) Errorf(format string, args ...interface{})
+
+ // Fail marks the function as having failed but continues ...
+ func (c *T) Fail()
+
+ // FailNow marks the function as having failed and stops its ...
+ func (c *T) FailNow()
+
+ // Failed reports whether the function has failed.
+ func (c *T) Failed() bool
+
+ // Fatal is equivalent to Log() followed by FailNow().
+ func (c *T) Fatal(args ...interface{})
+
+ // Fatalf is equivalent to Logf() followed by FailNow().
+ func (c *T) Fatalf(format string, args ...interface{})
+
+ // Log formats its arguments using default formatting, analogous ...
+ func (c *T) Log(args ...interface{})
+
+ // Logf formats its arguments according to the format, analogous ...
+ func (c *T) Logf(format string, args ...interface{})
+
+ // Parallel signals that this test is to be run in parallel with ...
+ func (t *T) Parallel()
+
+ // log generates the output. It's always at the same stack depth.
+ func (c *T) log(s string)
+
+ //
+ func (t *T) report()
+
+ // common holds the elements common between T and B and captures ...
+ type common struct {
+ output []byte // Output generated by test or benchmark.
+ failed bool // Test or benchmark has failed.
+ start time.Time // Time test or benchmark started
+ duration time.Duration
+ self interface{} // To be sent on signal channel when done.
+ signal chan interface{} // Output for serial tests.
+ }
+
+ // Error is equivalent to Log() followed by Fail().
+ func (c *common) Error(args ...interface{})
+
+ // Errorf is equivalent to Logf() followed by Fail().
+ func (c *common) Errorf(format string, args ...interface{})
+
+ // Fail marks the function as having failed but continues ...
+ func (c *common) Fail()
+
+ // FailNow marks the function as having failed and stops its ...
+ func (c *common) FailNow()
+
+ // Failed reports whether the function has failed.
+ func (c *common) Failed() bool
+
+ // Fatal is equivalent to Log() followed by FailNow().
+ func (c *common) Fatal(args ...interface{})
+
+ // Fatalf is equivalent to Logf() followed by FailNow().
+ func (c *common) Fatalf(format string, args ...interface{})
+
+ // Log formats its arguments using default formatting, analogous ...
+ func (c *common) Log(args ...interface{})
+
+ // Logf formats its arguments according to the format, analogous ...
+ func (c *common) Logf(format string, args ...interface{})
+
+ // log generates the output. It's always at the same stack depth.
+ func (c *common) log(s string)
+
diff --git a/src/go/doc/testdata/testing.2.golden b/src/go/doc/testdata/testing.2.golden
new file mode 100644
index 0000000..83cf37c
--- /dev/null
+++ b/src/go/doc/testdata/testing.2.golden
@@ -0,0 +1,156 @@
+// Package testing provides support for automated testing of Go ...
+PACKAGE testing
+
+IMPORTPATH
+ testdata/testing
+
+IMPORTS
+ bytes
+ flag
+ fmt
+ io
+ os
+ runtime
+ runtime/pprof
+ strconv
+ strings
+ time
+
+FILENAMES
+ testdata/benchmark.go
+ testdata/example.go
+ testdata/testing.go
+
+FUNCTIONS
+ // An internal function but exported because it is cross-package; ...
+ func Main(matchString func(pat, str string) (bool, error), tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample)
+
+ // An internal function but exported because it is cross-package; ...
+ func RunBenchmarks(matchString func(pat, str string) (bool, error), benchmarks []InternalBenchmark)
+
+ //
+ func RunExamples(examples []InternalExample) (ok bool)
+
+ //
+ func RunTests(matchString func(pat, str string) (bool, error), tests []InternalTest) (ok bool)
+
+ // Short reports whether the -test.short flag is set.
+ func Short() bool
+
+
+TYPES
+ // B is a type passed to Benchmark functions to manage benchmark ...
+ type B struct {
+ N int
+ // contains filtered or unexported fields
+ }
+
+ // Error is equivalent to Log() followed by Fail().
+ func (c *B) Error(args ...interface{})
+
+ // Errorf is equivalent to Logf() followed by Fail().
+ func (c *B) Errorf(format string, args ...interface{})
+
+ // Fail marks the function as having failed but continues ...
+ func (c *B) Fail()
+
+ // FailNow marks the function as having failed and stops its ...
+ func (c *B) FailNow()
+
+ // Failed reports whether the function has failed.
+ func (c *B) Failed() bool
+
+ // Fatal is equivalent to Log() followed by FailNow().
+ func (c *B) Fatal(args ...interface{})
+
+ // Fatalf is equivalent to Logf() followed by FailNow().
+ func (c *B) Fatalf(format string, args ...interface{})
+
+ // Log formats its arguments using default formatting, analogous ...
+ func (c *B) Log(args ...interface{})
+
+ // Logf formats its arguments according to the format, analogous ...
+ func (c *B) Logf(format string, args ...interface{})
+
+ // ResetTimer sets the elapsed benchmark time to zero. It does not ...
+ func (b *B) ResetTimer()
+
+ // SetBytes records the number of bytes processed in a single ...
+ func (b *B) SetBytes(n int64)
+
+ // StartTimer starts timing a test. This function is called ...
+ func (b *B) StartTimer()
+
+ // StopTimer stops timing a test. This can be used to pause the ...
+ func (b *B) StopTimer()
+
+ // The results of a benchmark run.
+ type BenchmarkResult struct {
+ N int // The number of iterations.
+ T time.Duration // The total time taken.
+ Bytes int64 // Bytes processed in one iteration.
+ }
+
+ // Benchmark benchmarks a single function. Useful for creating ...
+ func Benchmark(f func(b *B)) BenchmarkResult
+
+ //
+ func (r BenchmarkResult) NsPerOp() int64
+
+ //
+ func (r BenchmarkResult) String() string
+
+ // An internal type but exported because it is cross-package; part ...
+ type InternalBenchmark struct {
+ Name string
+ F func(b *B)
+ }
+
+ //
+ type InternalExample struct {
+ Name string
+ F func()
+ Output string
+ }
+
+ // An internal type but exported because it is cross-package; part ...
+ type InternalTest struct {
+ Name string
+ F func(*T)
+ }
+
+ // T is a type passed to Test functions to manage test state and ...
+ type T struct {
+ // contains filtered or unexported fields
+ }
+
+ // Error is equivalent to Log() followed by Fail().
+ func (c *T) Error(args ...interface{})
+
+ // Errorf is equivalent to Logf() followed by Fail().
+ func (c *T) Errorf(format string, args ...interface{})
+
+ // Fail marks the function as having failed but continues ...
+ func (c *T) Fail()
+
+ // FailNow marks the function as having failed and stops its ...
+ func (c *T) FailNow()
+
+ // Failed reports whether the function has failed.
+ func (c *T) Failed() bool
+
+ // Fatal is equivalent to Log() followed by FailNow().
+ func (c *T) Fatal(args ...interface{})
+
+ // Fatalf is equivalent to Logf() followed by FailNow().
+ func (c *T) Fatalf(format string, args ...interface{})
+
+ // Log formats its arguments using default formatting, analogous ...
+ func (c *T) Log(args ...interface{})
+
+ // Logf formats its arguments according to the format, analogous ...
+ func (c *T) Logf(format string, args ...interface{})
+
+ // Parallel signals that this test is to be run in parallel with ...
+ func (t *T) Parallel()
+
diff --git a/src/go/doc/testdata/testing.go b/src/go/doc/testdata/testing.go
new file mode 100644
index 0000000..52810f7
--- /dev/null
+++ b/src/go/doc/testdata/testing.go
@@ -0,0 +1,404 @@
+// Copyright 2009 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 testing provides support for automated testing of Go packages.
+// It is intended to be used in concert with the ``go test'' utility, which automates
+// execution of any function of the form
+// func TestXxx(*testing.T)
+// where Xxx can be any alphanumeric string (but the first letter must not be in
+// [a-z]) and serves to identify the test routine.
+// These TestXxx routines should be declared within the package they are testing.
+//
+// Functions of the form
+// func BenchmarkXxx(*testing.B)
+// are considered benchmarks, and are executed by go test when the -test.bench
+// flag is provided.
+//
+// A sample benchmark function looks like this:
+// func BenchmarkHello(b *testing.B) {
+// for i := 0; i < b.N; i++ {
+// fmt.Sprintf("hello")
+// }
+// }
+// The benchmark package will vary b.N until the benchmark function lasts
+// long enough to be timed reliably. The output
+// testing.BenchmarkHello 10000000 282 ns/op
+// means that the loop ran 10000000 times at a speed of 282 ns per loop.
+//
+// If a benchmark needs some expensive setup before running, the timer
+// may be stopped:
+// func BenchmarkBigLen(b *testing.B) {
+// b.StopTimer()
+// big := NewBig()
+// b.StartTimer()
+// for i := 0; i < b.N; i++ {
+// big.Len()
+// }
+// }
+package testing
+
+import (
+ "flag"
+ "fmt"
+ "os"
+ "runtime"
+ "runtime/pprof"
+ "strconv"
+ "strings"
+ "time"
+)
+
+var (
+ // The short flag requests that tests run more quickly, but its functionality
+ // is provided by test writers themselves. The testing package is just its
+ // home. The all.bash installation script sets it to make installation more
+ // efficient, but by default the flag is off so a plain "go test" will do a
+ // full test of the package.
+ short = flag.Bool("test.short", false, "run smaller test suite to save time")
+
+ // Report as tests are run; default is silent for success.
+ chatty = flag.Bool("test.v", false, "verbose: print additional output")
+ match = flag.String("test.run", "", "regular expression to select tests to run")
+ memProfile = flag.String("test.memprofile", "", "write a memory profile to the named file after execution")
+ memProfileRate = flag.Int("test.memprofilerate", 0, "if >=0, sets runtime.MemProfileRate")
+ cpuProfile = flag.String("test.cpuprofile", "", "write a cpu profile to the named file during execution")
+ timeout = flag.Duration("test.timeout", 0, "if positive, sets an aggregate time limit for all tests")
+ cpuListStr = flag.String("test.cpu", "", "comma-separated list of number of CPUs to use for each test")
+ parallel = flag.Int("test.parallel", runtime.GOMAXPROCS(0), "maximum test parallelism")
+
+ cpuList []int
+)
+
+// common holds the elements common between T and B and
+// captures common methods such as Errorf.
+type common struct {
+ output []byte // Output generated by test or benchmark.
+ failed bool // Test or benchmark has failed.
+ start time.Time // Time test or benchmark started
+ duration time.Duration
+ self interface{} // To be sent on signal channel when done.
+ signal chan interface{} // Output for serial tests.
+}
+
+// Short reports whether the -test.short flag is set.
+func Short() bool {
+ return *short
+}
+
+// decorate inserts the final newline if needed and indentation tabs for formatting.
+// If addFileLine is true, it also prefixes the string with the file and line of the call site.
+func decorate(s string, addFileLine bool) string {
+ if addFileLine {
+ _, file, line, ok := runtime.Caller(3) // decorate + log + public function.
+ if ok {
+ // Truncate file name at last file name separator.
+ if index := strings.LastIndex(file, "/"); index >= 0 {
+ file = file[index+1:]
+ } else if index = strings.LastIndex(file, "\\"); index >= 0 {
+ file = file[index+1:]
+ }
+ } else {
+ file = "???"
+ line = 1
+ }
+ s = fmt.Sprintf("%s:%d: %s", file, line, s)
+ }
+ s = "\t" + s // Every line is indented at least one tab.
+ n := len(s)
+ if n > 0 && s[n-1] != '\n' {
+ s += "\n"
+ n++
+ }
+ for i := 0; i < n-1; i++ { // -1 to avoid final newline
+ if s[i] == '\n' {
+ // Second and subsequent lines are indented an extra tab.
+ return s[0:i+1] + "\t" + decorate(s[i+1:n], false)
+ }
+ }
+ return s
+}
+
+// T is a type passed to Test functions to manage test state and support formatted test logs.
+// Logs are accumulated during execution and dumped to standard error when done.
+type T struct {
+ common
+ name string // Name of test.
+ startParallel chan bool // Parallel tests will wait on this.
+}
+
+// Fail marks the function as having failed but continues execution.
+func (c *common) Fail() { c.failed = true }
+
+// Failed reports whether the function has failed.
+func (c *common) Failed() bool { return c.failed }
+
+// FailNow marks the function as having failed and stops its execution.
+// Execution will continue at the next Test.
+func (c *common) FailNow() {
+ c.Fail()
+
+ // Calling runtime.Goexit will exit the goroutine, which
+ // will run the deferred functions in this goroutine,
+ // which will eventually run the deferred lines in tRunner,
+ // which will signal to the test loop that this test is done.
+ //
+ // A previous version of this code said:
+ //
+ // c.duration = ...
+ // c.signal <- c.self
+ // runtime.Goexit()
+ //
+ // This previous version duplicated code (those lines are in
+ // tRunner no matter what), but worse the goroutine teardown
+ // implicit in runtime.Goexit was not guaranteed to complete
+ // before the test exited. If a test deferred an important cleanup
+ // function (like removing temporary files), there was no guarantee
+ // it would run on a test failure. Because we send on c.signal during
+ // a top-of-stack deferred function now, we know that the send
+ // only happens after any other stacked defers have completed.
+ runtime.Goexit()
+}
+
+// log generates the output. It's always at the same stack depth.
+func (c *common) log(s string) {
+ c.output = append(c.output, decorate(s, true)...)
+}
+
+// Log formats its arguments using default formatting, analogous to Println(),
+// and records the text in the error log.
+func (c *common) Log(args ...interface{}) { c.log(fmt.Sprintln(args...)) }
+
+// Logf formats its arguments according to the format, analogous to Printf(),
+// and records the text in the error log.
+func (c *common) Logf(format string, args ...interface{}) { c.log(fmt.Sprintf(format, args...)) }
+
+// Error is equivalent to Log() followed by Fail().
+func (c *common) Error(args ...interface{}) {
+ c.log(fmt.Sprintln(args...))
+ c.Fail()
+}
+
+// Errorf is equivalent to Logf() followed by Fail().
+func (c *common) Errorf(format string, args ...interface{}) {
+ c.log(fmt.Sprintf(format, args...))
+ c.Fail()
+}
+
+// Fatal is equivalent to Log() followed by FailNow().
+func (c *common) Fatal(args ...interface{}) {
+ c.log(fmt.Sprintln(args...))
+ c.FailNow()
+}
+
+// Fatalf is equivalent to Logf() followed by FailNow().
+func (c *common) Fatalf(format string, args ...interface{}) {
+ c.log(fmt.Sprintf(format, args...))
+ c.FailNow()
+}
+
+// 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
+ <-t.startParallel // Wait for serial tests to finish
+}
+
+// An internal type but exported because it is cross-package; part of the implementation
+// of go test.
+type InternalTest struct {
+ Name string
+ F func(*T)
+}
+
+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
+ // a call to runtime.Goexit, record the duration and send
+ // a signal saying that the test is done.
+ defer func() {
+ t.duration = time.Now().Sub(t.start)
+ t.signal <- t
+ }()
+
+ test.F(t)
+}
+
+// An internal function but exported because it is cross-package; part of the implementation
+// of go test.
+func Main(matchString func(pat, str string) (bool, error), tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) {
+ flag.Parse()
+ parseCpuList()
+
+ before()
+ startAlarm()
+ testOk := RunTests(matchString, tests)
+ exampleOk := RunExamples(examples)
+ if !testOk || !exampleOk {
+ fmt.Println("FAIL")
+ os.Exit(1)
+ }
+ fmt.Println("PASS")
+ stopAlarm()
+ RunBenchmarks(matchString, benchmarks)
+ after()
+}
+
+func (t *T) report() {
+ tstr := fmt.Sprintf("(%.2f seconds)", t.duration.Seconds())
+ format := "--- %s: %s %s\n%s"
+ if t.failed {
+ fmt.Printf(format, "FAIL", t.name, tstr, t.output)
+ } else if *chatty {
+ fmt.Printf(format, "PASS", t.name, tstr, t.output)
+ }
+}
+
+func RunTests(matchString func(pat, str string) (bool, error), tests []InternalTest) (ok bool) {
+ ok = true
+ if len(tests) == 0 {
+ fmt.Fprintln(os.Stderr, "testing: warning: no tests to run")
+ return
+ }
+ for _, procs := range cpuList {
+ runtime.GOMAXPROCS(procs)
+ // We build a new channel tree for each run of the loop.
+ // collector merges in one channel all the upstream signals from parallel tests.
+ // If all tests pump to the same channel, a bug can occur where a test
+ // kicks off a goroutine that Fails, yet the test still delivers a completion signal,
+ // which skews the counting.
+ var collector = make(chan interface{})
+
+ numParallel := 0
+ startParallel := make(chan bool)
+
+ for i := 0; i < len(tests); i++ {
+ matched, err := matchString(*match, tests[i].Name)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "testing: invalid regexp for -test.run: %s\n", err)
+ os.Exit(1)
+ }
+ if !matched {
+ continue
+ }
+ testName := tests[i].Name
+ if procs != 1 {
+ testName = fmt.Sprintf("%s-%d", tests[i].Name, procs)
+ }
+ t := &T{
+ common: common{
+ signal: make(chan interface{}),
+ },
+ name: testName,
+ startParallel: startParallel,
+ }
+ t.self = t
+ if *chatty {
+ fmt.Printf("=== RUN %s\n", t.name)
+ }
+ go tRunner(t, &tests[i])
+ out := (<-t.signal).(*T)
+ if out == nil { // Parallel run.
+ go func() {
+ collector <- <-t.signal
+ }()
+ numParallel++
+ continue
+ }
+ t.report()
+ ok = ok && !out.failed
+ }
+
+ running := 0
+ for numParallel+running > 0 {
+ if running < *parallel && numParallel > 0 {
+ startParallel <- true
+ running++
+ numParallel--
+ continue
+ }
+ t := (<-collector).(*T)
+ t.report()
+ ok = ok && !t.failed
+ running--
+ }
+ }
+ return
+}
+
+// before runs before all testing.
+func before() {
+ if *memProfileRate > 0 {
+ runtime.MemProfileRate = *memProfileRate
+ }
+ if *cpuProfile != "" {
+ f, err := os.Create(*cpuProfile)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "testing: %s", err)
+ return
+ }
+ if err := pprof.StartCPUProfile(f); err != nil {
+ fmt.Fprintf(os.Stderr, "testing: can't start cpu profile: %s", err)
+ f.Close()
+ return
+ }
+ // Could save f so after can call f.Close; not worth the effort.
+ }
+
+}
+
+// after runs after all testing.
+func after() {
+ if *cpuProfile != "" {
+ pprof.StopCPUProfile() // flushes profile to disk
+ }
+ if *memProfile != "" {
+ f, err := os.Create(*memProfile)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "testing: %s", err)
+ return
+ }
+ if err = pprof.WriteHeapProfile(f); err != nil {
+ fmt.Fprintf(os.Stderr, "testing: can't write %s: %s", *memProfile, err)
+ }
+ f.Close()
+ }
+}
+
+var timer *time.Timer
+
+// startAlarm starts an alarm if requested.
+func startAlarm() {
+ if *timeout > 0 {
+ timer = time.AfterFunc(*timeout, alarm)
+ }
+}
+
+// stopAlarm turns off the alarm.
+func stopAlarm() {
+ if *timeout > 0 {
+ timer.Stop()
+ }
+}
+
+// alarm is called if the timeout expires.
+func alarm() {
+ panic("test timed out")
+}
+
+func parseCpuList() {
+ if len(*cpuListStr) == 0 {
+ cpuList = append(cpuList, runtime.GOMAXPROCS(-1))
+ } else {
+ for _, val := range strings.Split(*cpuListStr, ",") {
+ cpu, err := strconv.Atoi(val)
+ if err != nil || cpu <= 0 {
+ fmt.Fprintf(os.Stderr, "testing: invalid value %q for -test.cpu", val)
+ os.Exit(1)
+ }
+ cpuList = append(cpuList, cpu)
+ }
+ }
+}