Imported Upstream version 4.7.3
[platform/upstream/gcc48.git] / libgo / go / go / doc / example.go
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.
4
5 // Extract example functions from file ASTs.
6
7 package doc
8
9 import (
10         "go/ast"
11         "go/token"
12         "regexp"
13         "sort"
14         "strings"
15         "unicode"
16         "unicode/utf8"
17 )
18
19 type Example struct {
20         Name     string // name of the item being exemplified
21         Doc      string // example function doc string
22         Code     ast.Node
23         Comments []*ast.CommentGroup
24         Output   string // expected output
25 }
26
27 func Examples(files ...*ast.File) []*Example {
28         var list []*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
32                 var flist []*Example
33                 for _, decl := range file.Decls {
34                         if g, ok := decl.(*ast.GenDecl); ok && g.Tok != token.IMPORT {
35                                 numDecl++
36                                 continue
37                         }
38                         f, ok := decl.(*ast.FuncDecl)
39                         if !ok {
40                                 continue
41                         }
42                         numDecl++
43                         name := f.Name.Name
44                         if isTest(name, "Test") || isTest(name, "Benchmark") {
45                                 hasTests = true
46                                 continue
47                         }
48                         if !isTest(name, "Example") {
49                                 continue
50                         }
51                         var doc string
52                         if f.Doc != nil {
53                                 doc = f.Doc.Text()
54                         }
55                         flist = append(flist, &Example{
56                                 Name:     name[len("Example"):],
57                                 Doc:      doc,
58                                 Code:     f.Body,
59                                 Comments: file.Comments,
60                                 Output:   exampleOutput(f, file.Comments),
61                         })
62                 }
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.
67                         flist[0].Code = file
68                 }
69                 list = append(list, flist...)
70         }
71         sort.Sort(exampleByName(list))
72         return list
73 }
74
75 var outputPrefix = regexp.MustCompile(`(?i)^[[:space:]]*output:`)
76
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() {
82                         continue
83                 }
84                 if cg.End() > fun.End() {
85                         break
86                 }
87                 last = cg
88         }
89         if last != nil {
90                 // test that it begins with the correct prefix
91                 text := last.Text()
92                 if loc := outputPrefix.FindStringIndex(text); loc != nil {
93                         return strings.TrimSpace(text[loc[1]:])
94                 }
95         }
96         return "" // no suitable comment found
97 }
98
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) {
104                 return false
105         }
106         if len(name) == len(prefix) { // "Test" is ok
107                 return true
108         }
109         rune, _ := utf8.DecodeRuneInString(name[len(prefix):])
110         return !unicode.IsLower(rune)
111 }
112
113 type exampleByName []*Example
114
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 }