@@ -44,14 +44,16 @@ import 'package:analyzer_plugin/protocol/protocol_constants.dart' as protocol;
4444import 'package:analyzer_plugin/protocol/protocol_generated.dart' as protocol;
4545import 'package:analyzer_plugin/src/protocol/protocol_internal.dart' ;
4646
47- typedef _ErrorAndProtocolError = ({
47+ /// A pair, matching a [Diagnostic] with it's equivalent
48+ /// [protocol.AnalysisError] .
49+ typedef _DiagnosticAndAnalysisError = ({
4850 Diagnostic diagnostic,
49- protocol.AnalysisError protocolError ,
51+ protocol.AnalysisError analysisError ,
5052});
5153
5254typedef _PluginState = ({
5355 AnalysisContext analysisContext,
54- List <_ErrorAndProtocolError > errors,
56+ List <_DiagnosticAndAnalysisError > errors,
5557});
5658
5759/// The server that communicates with the analysis server, passing requests and
@@ -201,7 +203,7 @@ class PluginServer {
201203 var errorFixesList = < protocol.AnalysisErrorFixes > [];
202204
203205 var workspace = DartChangeWorkspace ([analysisContext.currentSession]);
204- for (var (: diagnostic, : protocolError ) in lintAtOffset) {
206+ for (var (: diagnostic, : analysisError ) in lintAtOffset) {
205207 var context = DartFixContext (
206208 // TODO(srawlins): Use a real instrumentation service. Other
207209 // implementations get InstrumentationService from AnalysisServer.
@@ -223,7 +225,7 @@ class PluginServer {
223225
224226 if (fixes.isNotEmpty) {
225227 fixes.sort (Fix .compareFixes);
226- var errorFixes = protocol.AnalysisErrorFixes (protocolError );
228+ var errorFixes = protocol.AnalysisErrorFixes (analysisError );
227229 errorFixesList.add (errorFixes);
228230 for (var fix in fixes) {
229231 errorFixes.fixes.add (protocol.PrioritizedSourceChange (1 , fix.change));
@@ -282,7 +284,7 @@ class PluginServer {
282284 .where ((p) => file_paths.isDart (_resourceProvider.pathContext, p))
283285 .toSet ();
284286
285- await _analyzeFiles (analysisContext: analysisContext, paths: paths);
287+ await _analyzeLibraries (analysisContext: analysisContext, paths: paths);
286288 });
287289 _channel.sendNotification (
288290 protocol.PluginStatusParams (
@@ -291,52 +293,65 @@ class PluginServer {
291293 );
292294 }
293295
294- Future <void > _analyzeFile ({
296+ /// Analyzes the library at the given [libraryPath] , sending an
297+ /// 'analysis.errors' [Notification] for each compilation unit.
298+ Future <void > _analyzeLibrary ({
295299 required AnalysisContext analysisContext,
296- required String path ,
300+ required String libraryPath ,
297301 }) async {
298- var file = _resourceProvider.getFile (path );
302+ var file = _resourceProvider.getFile (libraryPath );
299303 var analysisOptions = analysisContext.getAnalysisOptionsForFile (file);
300- var diagnostics = await _computeDiagnostics (
304+ var analysisErrorsByPath = await _computeAnalysisErrors (
301305 analysisContext,
302- path ,
306+ libraryPath ,
303307 analysisOptions: analysisOptions as AnalysisOptionsImpl ,
304308 );
305- _channel.sendNotification (
306- protocol.AnalysisErrorsParams (path, diagnostics).toNotification (),
307- );
309+ for (var MapEntry (key: path, value: analysisErrors)
310+ in analysisErrorsByPath.entries) {
311+ _channel.sendNotification (
312+ protocol.AnalysisErrorsParams (path, analysisErrors).toNotification (),
313+ );
314+ }
308315 }
309316
310- /// Analyzes the files at the given [paths] .
311- Future <void > _analyzeFiles ({
317+ /// Analyzes the libraries at the given [paths] .
318+ Future <void > _analyzeLibraries ({
312319 required AnalysisContext analysisContext,
313320 required Set <String > paths,
314321 }) async {
315322 // First analyze priority files.
316323 for (var path in _priorityPaths) {
317324 if (paths.remove (path)) {
318- await _analyzeFile (analysisContext: analysisContext, path: path);
325+ await _analyzeLibrary (
326+ analysisContext: analysisContext,
327+ libraryPath: path,
328+ );
319329 }
320330 }
321331
322332 // Then analyze the remaining files.
323333 for (var path in paths) {
324- await _analyzeFile (analysisContext: analysisContext, path: path);
334+ await _analyzeLibrary (
335+ analysisContext: analysisContext,
336+ libraryPath: path,
337+ );
325338 }
326339 }
327340
328- Future <List <protocol.AnalysisError >> _computeDiagnostics (
341+ /// Computes and returns [protocol.AnalysisError] s for each of the parts in
342+ /// the library at [libraryPath] .
343+ Future <Map <String , List <protocol.AnalysisError >>> _computeAnalysisErrors (
329344 AnalysisContext analysisContext,
330- String path , {
345+ String libraryPath , {
331346 required AnalysisOptionsImpl analysisOptions,
332347 }) async {
333348 var libraryResult = await analysisContext.currentSession.getResolvedLibrary (
334- path ,
349+ libraryPath ,
335350 );
336351 if (libraryResult is ! ResolvedLibraryResult ) {
337352 // We only handle analyzing at the library-level. Below, we work through
338353 // each of the compilation units found in `libraryResult`.
339- return const [] ;
354+ return const {} ;
340355 }
341356
342357 var diagnosticListeners = {
@@ -426,8 +441,8 @@ class PluginServer {
426441
427442 // The list of the `AnalysisError`s and their associated
428443 // `protocol.AnalysisError`s.
429- var diagnosticsAndProtocolErrors =
430- < ({Diagnostic diagnostic, protocol.AnalysisError protocolError })> [];
444+ var diagnosticsAndAnalysisErrors =
445+ < ({Diagnostic diagnostic, protocol.AnalysisError analysisError })> [];
431446
432447 diagnosticListeners.forEach ((unitResult, listener) {
433448 var ignoreInfo = IgnoreInfo .forDart (unitResult.unit, unitResult.content);
@@ -442,9 +457,9 @@ class PluginServer {
442457 });
443458
444459 for (var diagnostic in diagnostics) {
445- diagnosticsAndProtocolErrors .add ((
460+ diagnosticsAndAnalysisErrors .add ((
446461 diagnostic: diagnostic,
447- protocolError : protocol.AnalysisError (
462+ analysisError : protocol.AnalysisError (
448463 severityMapping[diagnostic.diagnosticCode] ??
449464 protocol.AnalysisErrorSeverity .INFO ,
450465 protocol.AnalysisErrorType .STATIC_WARNING ,
@@ -459,11 +474,22 @@ class PluginServer {
459474 }
460475 });
461476
462- _recentState[path ] = (
477+ _recentState[libraryPath ] = (
463478 analysisContext: analysisContext,
464- errors: [...diagnosticsAndProtocolErrors ],
479+ errors: [...diagnosticsAndAnalysisErrors ],
465480 );
466- return diagnosticsAndProtocolErrors.map ((e) => e.protocolError).toList ();
481+
482+ // A map that has a key for each unit's path. It is important to collect the
483+ // analysis errors for each unit, even if it has none. We must send a
484+ // notification for each unit, even if there are no analysis errors to
485+ // report.
486+ var analysisErrorsByPath = < String , List <protocol.AnalysisError >> {
487+ for (var unitResult in libraryResult.units) unitResult.path: [],
488+ };
489+ for (var (diagnostic: _, : analysisError) in diagnosticsAndAnalysisErrors) {
490+ analysisErrorsByPath[analysisError.location.file]! .add (analysisError);
491+ }
492+ return analysisErrorsByPath;
467493 }
468494
469495 /// Converts the severity of [code] into a [protocol.AnalysisErrorSeverity] .
@@ -601,7 +627,7 @@ class PluginServer {
601627 /// one or more files. The implementation may check if these files should
602628 /// be analyzed, do such analysis, and send diagnostics.
603629 ///
604- /// By default invokes [_analyzeFiles ] only for files that are analyzed in
630+ /// By default invokes [_analyzeLibraries ] only for files that are analyzed in
605631 /// this [analysisContext] .
606632 Future <void > _handleAffectedFiles ({
607633 required AnalysisContext analysisContext,
@@ -611,7 +637,10 @@ class PluginServer {
611637 .where (analysisContext.contextRoot.isAnalyzed)
612638 .toSet ();
613639
614- await _analyzeFiles (analysisContext: analysisContext, paths: analyzedPaths);
640+ await _analyzeLibraries (
641+ analysisContext: analysisContext,
642+ paths: analyzedPaths,
643+ );
615644 }
616645
617646 /// Handles an 'analysis.setContextRoots' request.
0 commit comments