@@ -8,7 +8,9 @@ package tests
8
8
9
9
import (
10
10
"go/ast"
11
+ "go/token"
11
12
"go/types"
13
+ "regexp"
12
14
"strings"
13
15
"unicode"
14
16
"unicode/utf8"
@@ -42,10 +44,10 @@ func run(pass *analysis.Pass) (interface{}, error) {
42
44
// Ignore non-functions or functions with receivers.
43
45
continue
44
46
}
45
-
46
47
switch {
47
48
case strings .HasPrefix (fn .Name .Name , "Example" ):
48
- checkExample (pass , fn )
49
+ checkExampleName (pass , fn )
50
+ checkExampleOutput (pass , fn , f .Comments )
49
51
case strings .HasPrefix (fn .Name .Name , "Test" ):
50
52
checkTest (pass , fn , "Test" )
51
53
case strings .HasPrefix (fn .Name .Name , "Benchmark" ):
@@ -108,7 +110,59 @@ func lookup(pkg *types.Package, name string) []types.Object {
108
110
return ret
109
111
}
110
112
111
- func checkExample (pass * analysis.Pass , fn * ast.FuncDecl ) {
113
+ // This pattern is taken from /go/src/go/doc/example.go
114
+ var outputRe = regexp .MustCompile (`(?i)^[[:space:]]*(unordered )?output:` )
115
+
116
+ type commentMetadata struct {
117
+ isOutput bool
118
+ pos token.Pos
119
+ }
120
+
121
+ func checkExampleOutput (pass * analysis.Pass , fn * ast.FuncDecl , fileComments []* ast.CommentGroup ) {
122
+ commentsInExample := []commentMetadata {}
123
+ numOutputs := 0
124
+
125
+ // Find the comment blocks that are in the example. These comments are
126
+ // guaranteed to be in order of appearance.
127
+ for _ , cg := range fileComments {
128
+ if cg .Pos () < fn .Pos () {
129
+ continue
130
+ } else if cg .End () > fn .End () {
131
+ break
132
+ }
133
+
134
+ isOutput := outputRe .MatchString (cg .Text ())
135
+ if isOutput {
136
+ numOutputs ++
137
+ }
138
+
139
+ commentsInExample = append (commentsInExample , commentMetadata {
140
+ isOutput : isOutput ,
141
+ pos : cg .Pos (),
142
+ })
143
+ }
144
+
145
+ // Change message based on whether there are multiple output comment blocks.
146
+ msg := "output comment block must be the last comment block"
147
+ if numOutputs > 1 {
148
+ msg = "there can only be one output comment block per example"
149
+ }
150
+
151
+ for i , cg := range commentsInExample {
152
+ // Check for output comments that are not the last comment in the example.
153
+ isLast := (i == len (commentsInExample )- 1 )
154
+ if cg .isOutput && ! isLast {
155
+ pass .Report (
156
+ analysis.Diagnostic {
157
+ Pos : cg .pos ,
158
+ Message : msg ,
159
+ },
160
+ )
161
+ }
162
+ }
163
+ }
164
+
165
+ func checkExampleName (pass * analysis.Pass , fn * ast.FuncDecl ) {
112
166
fnName := fn .Name .Name
113
167
if params := fn .Type .Params ; len (params .List ) != 0 {
114
168
pass .Reportf (fn .Pos (), "%s should be niladic" , fnName )
0 commit comments