From 9f1438813f9cfc6202c672a6d30832dbe303b5df Mon Sep 17 00:00:00 2001 From: Konstantin Shcheglov Date: Wed, 6 May 2026 11:28:54 -0700 Subject: [PATCH] Fix JsonErrorFormatter to use right LineInfo for context messages. Change-Id: Icd9e254308a54e47ea3be63220ada5fd028712e7 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/500660 Reviewed-by: Paul Berry --- pkg/analyzer_cli/lib/src/error_formatter.dart | 42 +++---- pkg/analyzer_cli/test/reporter_test.dart | 109 ++++++++++++++++++ 2 files changed, 128 insertions(+), 23 deletions(-) diff --git a/pkg/analyzer_cli/lib/src/error_formatter.dart b/pkg/analyzer_cli/lib/src/error_formatter.dart index a1fafef52c8..68d9f71a67c 100644 --- a/pkg/analyzer_cli/lib/src/error_formatter.dart +++ b/pkg/analyzer_cli/lib/src/error_formatter.dart @@ -8,7 +8,6 @@ import 'package:analyzer/dart/analysis/results.dart'; import 'package:analyzer/diagnostic/diagnostic.dart'; import 'package:analyzer/error/error.dart'; import 'package:analyzer/source/line_info.dart'; -import 'package:analyzer/src/dart/analysis/driver_based_analysis_context.dart'; import 'package:analyzer_cli/src/ansi.dart'; import 'package:analyzer_cli/src/options.dart'; import 'package:path/path.dart' as path; @@ -223,6 +222,12 @@ abstract class ErrorFormatter { /// be filtered. DiagnosticSeverity? _computeSeverity(Diagnostic diagnostic) => _severityProcessor(diagnostic); + + // TODO(scheglov): We should add `LineInfo` to `DiagnosticMessage`. + LineInfo _getLineInfo(ErrorsResult result, String filePath) { + var fileResult = result.session.getFile(filePath) as FileResult; + return fileResult.lineInfo; + } } class HumanErrorFormatter extends ErrorFormatter { @@ -322,23 +327,16 @@ class HumanErrorFormatter extends ErrorFormatter { } var contextMessages = []; for (var message in error.contextMessages) { - // TODO(scheglov): We should add `LineInfo` to `DiagnosticMessage`. - var session = result.session.analysisContext; - if (session is DriverBasedAnalysisContext) { - var fileResult = session.driver.getFileSync(message.filePath); - if (fileResult is FileResult) { - var lineInfo = fileResult.lineInfo; - var location = lineInfo.getLocation(message.offset); - contextMessages.add( - ContextMessage( - message.filePath, - message.messageText(includeUrl: true), - location.lineNumber, - location.columnNumber, - ), - ); - } - } + var lineInfo = _getLineInfo(result, message.filePath); + var location = lineInfo.getLocation(message.offset); + contextMessages.add( + ContextMessage( + message.filePath, + message.messageText(includeUrl: true), + location.lineNumber, + location.columnNumber, + ), + ); } batchedErrors.add( @@ -413,9 +411,7 @@ class JsonErrorFormatter extends ErrorFormatter { var diagnostics = >[]; for (var result in results) { - var errors = result.diagnostics; - var lineInfo = result.lineInfo; - for (var error in errors) { + for (var error in result.diagnostics) { var severity = _computeSeverity(error); if (severity == null) { continue; @@ -427,7 +423,7 @@ class JsonErrorFormatter extends ErrorFormatter { contextMessage.filePath, contextMessage.offset, contextMessage.length, - lineInfo, + _getLineInfo(result, contextMessage.filePath), ), 'message': contextMessage.messageText(includeUrl: true), }); @@ -443,7 +439,7 @@ class JsonErrorFormatter extends ErrorFormatter { problemMessage.filePath, problemMessage.offset, problemMessage.length, - lineInfo, + result.lineInfo, ), 'problemMessage': problemMessage.messageText(includeUrl: true), if (error.correctionMessage != null) diff --git a/pkg/analyzer_cli/test/reporter_test.dart b/pkg/analyzer_cli/test/reporter_test.dart index 26998ca282e..ce599037e82 100644 --- a/pkg/analyzer_cli/test/reporter_test.dart +++ b/pkg/analyzer_cli/test/reporter_test.dart @@ -34,6 +34,48 @@ class ReporterTest extends PubPackageResolutionTest { await super.tearDown(); } + Future test_human_contextMessage_otherFile() async { + var options = CommandLineOptions.parse(resourceProvider, [ + '--dart-sdk=${sdkRoot.path}', + '--verbose', + 'test.dart', + ])!; + var reporter = HumanErrorFormatter(out, options, stats); + + var libFile = newFile('$testPackageRootPath/lib/lib.dart', r''' +class C { + final int? foo; + C(this.foo); +} +'''); + + newFile(testFile.path, r''' +import 'lib.dart'; +void f(C c) { + if (c.foo != null) { + c.foo.isEven; + } +} +'''); + + var errorsResult = await _getErrorsResultForFile(testFile); + await reporter.formatErrors([errorsResult]); + reporter.flush(); + + expect( + out.toString().trim(), + contains( + "error • The property 'isEven' can't be unconditionally accessed because the receiver can be 'null'. • package:test/test.dart:4:11 • unchecked_use_of_nullable_value", + ), + ); + expect( + out.toString().trim(), + contains( + " 'foo' refers to a public property so it couldn't be promoted. See http://dart.dev/go/non-promo-public-field at ${libFile.path}:2:14", + ), + ); + } + Future test_human_error() async { var options = CommandLineOptions.parse(resourceProvider, [ '--dart-sdk=${sdkRoot.path}', @@ -107,6 +149,73 @@ void f() { ); } + Future test_json_contextMessage_otherFile() async { + var options = CommandLineOptions.parse(resourceProvider, [ + '--format=json', + '--dart-sdk=${sdkRoot.path}', + 'test.dart', + ])!; + var reporter = JsonErrorFormatter(out, options, stats); + + var libFile = newFile('$testPackageRootPath/lib/lib.dart', r''' +class C { + final int? foo; + C(this.foo); +} +'''); + + newFile(testFile.path, r''' +import 'lib.dart'; +void f(C c) { + if (c.foo != null) { + c.foo.isEven; + } +} +'''); + + var errorsResult = await _getErrorsResultForFile(testFile); + await reporter.formatErrors([errorsResult]); + reporter.flush(); + + var expected = { + 'version': 1, + 'diagnostics': [ + { + 'code': 'unchecked_use_of_nullable_value', + 'severity': 'ERROR', + 'type': 'COMPILE_TIME_ERROR', + 'location': { + 'file': testFile.path, + 'range': { + 'start': {'offset': 66, 'line': 4, 'column': 11}, + 'end': {'offset': 72, 'line': 4, 'column': 17}, + }, + }, + 'problemMessage': + "The property 'isEven' can't be unconditionally accessed because the receiver can be 'null'.", + 'correctionMessage': + "Try making the access conditional (using '?.') or adding a null check to the target ('!').", + 'contextMessages': [ + { + 'location': { + 'file': libFile.path, + 'range': { + 'start': {'offset': 23, 'line': 2, 'column': 14}, + 'end': {'offset': 26, 'line': 2, 'column': 17}, + }, + }, + 'message': + "'foo' refers to a public property so it couldn't be promoted. See http://dart.dev/go/non-promo-public-field", + }, + ], + 'documentation': + 'https://dart.dev/diagnostics/unchecked_use_of_nullable_value', + }, + ], + }; + expect(json.decode(out.toString().trim()), expected); + } + Future test_json_error() async { var options = CommandLineOptions.parse(resourceProvider, [ '--format=json',