@@ -217,6 +217,10 @@ func (s *Server) diagnose(ctx context.Context, snapshot source.Snapshot, forceAn
217
217
defer done ()
218
218
219
219
// Wait for a free diagnostics slot.
220
+ // TODO(adonovan): opt: shouldn't it be the analysis implementation's
221
+ // job to de-dup and limit resource consumption? In any case this
222
+ // this function spends most its time waiting for awaitLoaded, at
223
+ // least initially.
220
224
select {
221
225
case <- ctx .Done ():
222
226
return
@@ -226,73 +230,62 @@ func (s *Server) diagnose(ctx context.Context, snapshot source.Snapshot, forceAn
226
230
<- s .diagnosticsSema
227
231
}()
228
232
229
- // First, diagnose the go.mod file.
230
- modReports , modErr := mod . Diagnostics ( ctx , snapshot )
231
- if ctx . Err () != nil {
232
- log . Trace . Log (ctx , "diagnose cancelled" )
233
- return
234
- }
235
- if modErr != nil {
236
- event . Error ( ctx , "warning: diagnose go.mod" , modErr , tag . Directory . Of ( snapshot . View (). Folder (). Filename ()), tag . Snapshot . Of ( snapshot . ID ()))
237
- }
238
- for id , diags := range modReports {
239
- if id . URI == "" {
240
- event . Error ( ctx , "missing URI for module diagnostics" , fmt . Errorf ( "empty URI" ), tag . Directory . Of ( snapshot . View (). Folder (). Filename ()))
241
- continue
233
+ // common code for dispatching diagnostics
234
+ store := func ( dsource diagnosticSource , operation string , diagsByFileID map [source. VersionedFileIdentity ][] * source. Diagnostic , err error ) {
235
+ if err != nil {
236
+ event . Error (ctx , "warning: while " + operation , err ,
237
+ tag . Directory . Of ( snapshot . View (). Folder (). Filename ()),
238
+ tag . Snapshot . Of ( snapshot . ID ()))
239
+ }
240
+ for id , diags := range diagsByFileID {
241
+ if id . URI == "" {
242
+ event . Error ( ctx , "missing URI while " + operation , fmt . Errorf ( "empty URI" ), tag . Directory . Of ( snapshot . View (). Folder (). Filename ()))
243
+ continue
244
+ }
245
+ s . storeDiagnostics ( snapshot , id . URI , dsource , diags )
242
246
}
243
- s .storeDiagnostics (snapshot , id .URI , modSource , diags )
244
247
}
245
- upgradeModReports , upgradeErr := mod .UpgradeDiagnostics (ctx , snapshot )
248
+
249
+ // Diagnose go.mod upgrades.
250
+ upgradeReports , upgradeErr := mod .UpgradeDiagnostics (ctx , snapshot )
246
251
if ctx .Err () != nil {
247
252
log .Trace .Log (ctx , "diagnose cancelled" )
248
253
return
249
254
}
250
- if upgradeErr != nil {
251
- event .Error (ctx , "warning: diagnose go.mod upgrades" , upgradeErr , tag .Directory .Of (snapshot .View ().Folder ().Filename ()), tag .Snapshot .Of (snapshot .ID ()))
252
- }
253
- for id , diags := range upgradeModReports {
254
- if id .URI == "" {
255
- event .Error (ctx , "missing URI for module diagnostics" , fmt .Errorf ("empty URI" ), tag .Directory .Of (snapshot .View ().Folder ().Filename ()))
256
- continue
257
- }
258
- s .storeDiagnostics (snapshot , id .URI , modCheckUpgradesSource , diags )
259
- }
260
- vulnerabilityReports , vulnErr := mod .VulnerabilityDiagnostics (ctx , snapshot )
255
+ store (modCheckUpgradesSource , "diagnosing go.mod upgrades" , upgradeReports , upgradeErr )
256
+
257
+ // Diagnose vulnerabilities.
258
+ vulnReports , vulnErr := mod .VulnerabilityDiagnostics (ctx , snapshot )
261
259
if ctx .Err () != nil {
262
260
log .Trace .Log (ctx , "diagnose cancelled" )
263
261
return
264
262
}
265
- if vulnErr != nil {
266
- event .Error (ctx , "warning: checking vulnerabilities" , vulnErr , tag .Directory .Of (snapshot .View ().Folder ().Filename ()), tag .Snapshot .Of (snapshot .ID ()))
267
- }
268
- for id , diags := range vulnerabilityReports {
269
- if id .URI == "" {
270
- event .Error (ctx , "missing URI for module diagnostics" , fmt .Errorf ("empty URI" ), tag .Directory .Of (snapshot .View ().Folder ().Filename ()))
271
- continue
272
- }
273
- s .storeDiagnostics (snapshot , id .URI , modVulncheckSource , diags )
274
- }
263
+ store (modVulncheckSource , "diagnosing vulnerabilities" , vulnReports , vulnErr )
275
264
276
- // Diagnose the go.work file, if it exists .
265
+ // Diagnose go.work file.
277
266
workReports , workErr := work .Diagnostics (ctx , snapshot )
278
267
if ctx .Err () != nil {
279
268
log .Trace .Log (ctx , "diagnose cancelled" )
280
269
return
281
270
}
282
- if workErr != nil {
283
- event .Error (ctx , "warning: diagnose go.work" , workErr , tag .Directory .Of (snapshot .View ().Folder ().Filename ()), tag .Snapshot .Of (snapshot .ID ()))
284
- }
285
- for id , diags := range workReports {
286
- if id .URI == "" {
287
- event .Error (ctx , "missing URI for work file diagnostics" , fmt .Errorf ("empty URI" ), tag .Directory .Of (snapshot .View ().Folder ().Filename ()))
288
- continue
289
- }
290
- s .storeDiagnostics (snapshot , id .URI , workSource , diags )
271
+ store (workSource , "diagnosing go.work file" , workReports , workErr )
272
+
273
+ // All subsequent steps depend on the completion of
274
+ // type-checking of the all active packages in the workspace.
275
+ // This step may take many seconds initially.
276
+ // (mod.Diagnostics would implicitly wait for this too,
277
+ // but the control is clearer if it is explicit here.)
278
+ activePkgs , activeErr := snapshot .ActivePackages (ctx )
279
+
280
+ // Diagnose go.mod file.
281
+ modReports , modErr := mod .Diagnostics (ctx , snapshot )
282
+ if ctx .Err () != nil {
283
+ log .Trace .Log (ctx , "diagnose cancelled" )
284
+ return
291
285
}
286
+ store (modSource , "diagnosing go.mod file" , modReports , modErr )
292
287
293
- // Diagnose all of the packages in the workspace.
294
- wsPkgs , err := snapshot .ActivePackages (ctx )
295
- if s .shouldIgnoreError (ctx , snapshot , err ) {
288
+ if s .shouldIgnoreError (ctx , snapshot , activeErr ) {
296
289
return
297
290
}
298
291
criticalErr := snapshot .GetCriticalError (ctx )
@@ -303,37 +296,39 @@ func (s *Server) diagnose(ctx context.Context, snapshot source.Snapshot, forceAn
303
296
// error progress reports will be closed.
304
297
s .showCriticalErrorStatus (ctx , snapshot , criticalErr )
305
298
306
- // There may be .tmpl files.
299
+ // Diagnose template ( .tmpl) files.
307
300
for _ , f := range snapshot .Templates () {
308
301
diags := template .Diagnose (f )
309
302
s .storeDiagnostics (snapshot , f .URI (), typeCheckSource , diags )
310
303
}
311
304
312
305
// If there are no workspace packages, there is nothing to diagnose and
313
306
// there are no orphaned files.
314
- if len (wsPkgs ) == 0 {
307
+ if len (activePkgs ) == 0 {
315
308
return
316
309
}
317
310
311
+ // Run go/analysis diagnosis of packages in parallel.
312
+ // TODO(adonovan): opt: it may be more efficient to
313
+ // have diagnosePkg take a set of packages.
318
314
var (
319
315
wg sync.WaitGroup
320
316
seen = map [span.URI ]struct {}{}
321
317
)
322
- for _ , pkg := range wsPkgs {
323
- wg .Add (1 )
324
-
318
+ for _ , pkg := range activePkgs {
325
319
for _ , pgf := range pkg .CompiledGoFiles () {
326
320
seen [pgf .URI ] = struct {}{}
327
321
}
328
322
323
+ wg .Add (1 )
329
324
go func (pkg source.Package ) {
330
325
defer wg .Done ()
331
-
332
326
s .diagnosePkg (ctx , snapshot , pkg , forceAnalysis )
333
327
}(pkg )
334
328
}
335
329
wg .Wait ()
336
330
331
+ // Orphaned files.
337
332
// Confirm that every opened file belongs to a package (if any exist in
338
333
// the workspace). Otherwise, add a diagnostic to the file.
339
334
for _ , o := range s .session .Overlays () {
0 commit comments