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 <paulberry@google.com>
This commit is contained in:
Konstantin Shcheglov
2026-05-06 11:28:54 -07:00
parent eed5d692ab
commit 9f1438813f
2 changed files with 128 additions and 23 deletions
+19 -23
View File
@@ -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 = <ContextMessage>[];
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 = <Map<String, dynamic>>[];
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)
+109
View File
@@ -34,6 +34,48 @@ class ReporterTest extends PubPackageResolutionTest {
await super.tearDown();
}
Future<void> 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<void> test_human_error() async {
var options = CommandLineOptions.parse(resourceProvider, [
'--dart-sdk=${sdkRoot.path}',
@@ -107,6 +149,73 @@ void f() {
);
}
Future<void> 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<void> test_json_error() async {
var options = CommandLineOptions.parse(resourceProvider, [
'--format=json',