@@ -19,7 +19,9 @@ import (
19
19
20
20
"golang.org/x/tools/go/analysis"
21
21
"golang.org/x/tools/go/analysis/analysistest"
22
+ "golang.org/x/tools/go/analysis/checker"
22
23
"golang.org/x/tools/go/analysis/multichecker"
24
+ "golang.org/x/tools/go/packages"
23
25
"golang.org/x/tools/internal/testenv"
24
26
)
25
27
@@ -126,15 +128,6 @@ func Foo() {
126
128
_ = bar
127
129
}
128
130
129
- // the end
130
- ` ,
131
- "duplicate/dup.go" : `package duplicate
132
-
133
- func Foo() {
134
- bar := 14
135
- _ = bar
136
- }
137
-
138
131
// the end
139
132
` ,
140
133
}
@@ -164,15 +157,6 @@ func Foo() {
164
157
_ = baz
165
158
}
166
159
167
- // the end
168
- ` ,
169
- "duplicate/dup.go" : `package duplicate
170
-
171
- func Foo() {
172
- baz := 14
173
- _ = baz
174
- }
175
-
176
160
// the end
177
161
` ,
178
162
}
@@ -182,7 +166,7 @@ func Foo() {
182
166
}
183
167
defer cleanup ()
184
168
185
- fix (t , dir , "rename,other" , exitCodeDiagnostics , "rename" , "duplicate" )
169
+ fix (t , dir , "rename,other" , exitCodeDiagnostics , "rename" )
186
170
187
171
for name , want := range fixed {
188
172
path := path .Join (dir , "src" , name )
@@ -196,6 +180,117 @@ func Foo() {
196
180
}
197
181
}
198
182
183
+ // TestReportInvalidDiagnostic tests that a call to pass.Report with
184
+ // certain kind of invalid diagnostic (e.g. conflicting fixes)
185
+ // promptly results in a panic.
186
+ func TestReportInvalidDiagnostic (t * testing.T ) {
187
+ testenv .NeedsGoPackages (t )
188
+
189
+ // Load the errors package.
190
+ cfg := & packages.Config {Mode : packages .LoadAllSyntax }
191
+ initial , err := packages .Load (cfg , "errors" )
192
+ if err != nil {
193
+ t .Fatal (err )
194
+ }
195
+
196
+ for _ , test := range []struct {
197
+ name string
198
+ want string
199
+ diag func (pos token.Pos ) analysis.Diagnostic
200
+ }{
201
+ // Diagnostic has two alternative fixes with the same Message.
202
+ {
203
+ "duplicate message" ,
204
+ `analyzer "a" suggests two fixes with same Message \(fix\)` ,
205
+ func (pos token.Pos ) analysis.Diagnostic {
206
+ return analysis.Diagnostic {
207
+ Pos : pos ,
208
+ Message : "oops" ,
209
+ SuggestedFixes : []analysis.SuggestedFix {
210
+ {Message : "fix" },
211
+ {Message : "fix" },
212
+ },
213
+ }
214
+ },
215
+ },
216
+ // TextEdit has invalid Pos.
217
+ {
218
+ "bad Pos" ,
219
+ `analyzer "a" suggests invalid fix .*: missing file info for pos` ,
220
+ func (pos token.Pos ) analysis.Diagnostic {
221
+ return analysis.Diagnostic {
222
+ Pos : pos ,
223
+ Message : "oops" ,
224
+ SuggestedFixes : []analysis.SuggestedFix {
225
+ {
226
+ Message : "fix" ,
227
+ TextEdits : []analysis.TextEdit {{}},
228
+ },
229
+ },
230
+ }
231
+ },
232
+ },
233
+ // TextEdit has invalid End.
234
+ {
235
+ "End < Pos" ,
236
+ `analyzer "a" suggests invalid fix .*: pos .* > end` ,
237
+ func (pos token.Pos ) analysis.Diagnostic {
238
+ return analysis.Diagnostic {
239
+ Pos : pos ,
240
+ Message : "oops" ,
241
+ SuggestedFixes : []analysis.SuggestedFix {
242
+ {
243
+ Message : "fix" ,
244
+ TextEdits : []analysis.TextEdit {{
245
+ Pos : pos + 2 ,
246
+ End : pos ,
247
+ }},
248
+ },
249
+ },
250
+ }
251
+ },
252
+ },
253
+ // Two TextEdits overlap.
254
+ {
255
+ "overlapping edits" ,
256
+ `analyzer "a" suggests invalid fix .*: overlapping edits to .*errors.go \(1:1-1:3 and 1:2-1:4\)` ,
257
+ func (pos token.Pos ) analysis.Diagnostic {
258
+ return analysis.Diagnostic {
259
+ Pos : pos ,
260
+ Message : "oops" ,
261
+ SuggestedFixes : []analysis.SuggestedFix {
262
+ {
263
+ Message : "fix" ,
264
+ TextEdits : []analysis.TextEdit {
265
+ {Pos : pos , End : pos + 2 },
266
+ {Pos : pos + 1 , End : pos + 3 },
267
+ },
268
+ },
269
+ },
270
+ }
271
+ },
272
+ },
273
+ } {
274
+ t .Run (test .name , func (t * testing.T ) {
275
+ reached := false
276
+ a := & analysis.Analyzer {Name : "a" , Doc : "doc" , Run : func (pass * analysis.Pass ) (any , error ) {
277
+ reached = true
278
+ panics (t , test .want , func () {
279
+ pos := pass .Files [0 ].FileStart
280
+ pass .Report (test .diag (pos ))
281
+ })
282
+ return nil , nil
283
+ }}
284
+ if _ , err := checker .Analyze ([]* analysis.Analyzer {a }, initial , & checker.Options {}); err != nil {
285
+ t .Fatalf ("Analyze failed: %v" , err )
286
+ }
287
+ if ! reached {
288
+ t .Error ("analyzer was never invoked" )
289
+ }
290
+ })
291
+ }
292
+ }
293
+
199
294
// TestConflict ensures that checker.Run detects conflicts correctly.
200
295
// This test fork/execs the main function above.
201
296
func TestConflict (t * testing.T ) {
@@ -333,3 +428,17 @@ func init() {
333
428
},
334
429
}
335
430
}
431
+
432
+ // panics asserts that f() panics with with a value whose printed form matches the regexp want.
433
+ func panics (t * testing.T , want string , f func ()) {
434
+ defer func () {
435
+ if x := recover (); x == nil {
436
+ t .Errorf ("function returned normally, wanted panic" )
437
+ } else if m , err := regexp .MatchString (want , fmt .Sprint (x )); err != nil {
438
+ t .Errorf ("panics: invalid regexp %q" , want )
439
+ } else if ! m {
440
+ t .Errorf ("function panicked with value %q, want match for %q" , x , want )
441
+ }
442
+ }()
443
+ f ()
444
+ }
0 commit comments