1 // Copyright 2011 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // Extract example functions from file ASTs.
20 Name string // name of the item being exemplified
21 Doc string // example function doc string
23 Comments []*ast.CommentGroup
24 Output string // expected output
27 func Examples(files ...*ast.File) []*Example {
29 for _, file := range files {
30 hasTests := false // file contains tests or benchmarks
31 numDecl := 0 // number of non-import declarations in the file
33 for _, decl := range file.Decls {
34 if g, ok := decl.(*ast.GenDecl); ok && g.Tok != token.IMPORT {
38 f, ok := decl.(*ast.FuncDecl)
44 if isTest(name, "Test") || isTest(name, "Benchmark") {
48 if !isTest(name, "Example") {
55 flist = append(flist, &Example{
56 Name: name[len("Example"):],
59 Comments: file.Comments,
60 Output: exampleOutput(f, file.Comments),
63 if !hasTests && numDecl > 1 && len(flist) == 1 {
64 // If this file only has one example function, some
65 // other top-level declarations, and no tests or
66 // benchmarks, use the whole file as the example.
69 list = append(list, flist...)
71 sort.Sort(exampleByName(list))
75 var outputPrefix = regexp.MustCompile(`(?i)^[[:space:]]*output:`)
77 func exampleOutput(fun *ast.FuncDecl, comments []*ast.CommentGroup) string {
78 // find the last comment in the function
79 var last *ast.CommentGroup
80 for _, cg := range comments {
81 if cg.Pos() < fun.Pos() {
84 if cg.End() > fun.End() {
90 // test that it begins with the correct prefix
92 if loc := outputPrefix.FindStringIndex(text); loc != nil {
93 return strings.TrimSpace(text[loc[1]:])
96 return "" // no suitable comment found
99 // isTest tells whether name looks like a test, example, or benchmark.
100 // It is a Test (say) if there is a character after Test that is not a
101 // lower-case letter. (We don't want Testiness.)
102 func isTest(name, prefix string) bool {
103 if !strings.HasPrefix(name, prefix) {
106 if len(name) == len(prefix) { // "Test" is ok
109 rune, _ := utf8.DecodeRuneInString(name[len(prefix):])
110 return !unicode.IsLower(rune)
113 type exampleByName []*Example
115 func (s exampleByName) Len() int { return len(s) }
116 func (s exampleByName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
117 func (s exampleByName) Less(i, j int) bool { return s[i].Name < s[j].Name }