Files
Paul Berry afcfbbeba8 Migrate developer experience packages to new constructor decl syntax.
(Part of https://github.com/dart-lang/sdk/issues/63288)

This change migrates the packages owned by the developer experience
team to use the new constructor declaration syntax, described in
https://github.com/dart-lang/language/blob/main/accepted/future-releases/primary-constructors/feature-specification.md#abbreviations-of-in-body-constructor-declarations.

This change was performed in an automated fashion, by (a) bumping the
packages' SDK constraints to `3.13.0-0`, (b) enabling the lints
`unnecessary_type_name_in_constructor` and
`unnecessary_const_in_enum_constructor`, (c) fixing the resulting lint
failures using `dart fix`, and then (d) reformatting the affected
files.

To ease code review, I've reverted unrelated formatting changes.

Since this change requires bumping SDK constaints to `3.13.0-0`, it
was only performed on packages that are *not* published on
pub. (Packages that *are* published on pub should remain on lower
language versions until at least after the stable version of 3.13 is
released, so that we don't block users on the stable channel from
receiving updates to those packages.)

Change-Id: Ibb4daebafd239da58251e838ea6a3f336a6a6964
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/505046
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
SLSA-Policy-Verified: SLSA Policy Verification Service <devtools-gerritcodereview-exitgate@google.com>
2026-05-27 14:52:58 -07:00

402 lines
13 KiB
Dart

// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
/// These classes were copied from `analyzer`. They should be moved into the
/// `analyzer/lib/src/test_utilities` directory so that they can be shared.
/// (This version has been converted to a more modern style.)
library;
import 'package:analyzer/diagnostic/diagnostic.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/source/line_info.dart';
import 'package:analyzer/source/source.dart';
import 'package:analyzer_testing/utilities/extensions/diagnostic_code.dart';
import 'package:test/test.dart';
/// A description of a message that is expected to be reported with an error.
class ExpectedContextMessage {
/// The path of the file with which the message is associated.
final String filePath;
/// The offset of the beginning of the error's region.
final int offset;
/// The offset of the beginning of the error's region.
final int length;
/// The message text for the error.
final String? text;
new(this.filePath, this.offset, this.length, {this.text});
/// Return `true` if the [message] matches this description of what the state
/// of the [message] is expected to be.
bool matches(DiagnosticMessage message) {
return message.filePath == filePath &&
message.offset == offset &&
message.length == length &&
(text == null || message.messageText(includeUrl: true) == text);
}
}
/// A description of an error that is expected to be reported.
class ExpectedError {
/// An empty array of error descriptors used when no errors are expected.
static List<ExpectedError> NO_ERRORS = <ExpectedError>[];
/// The error code associated with the error.
final DiagnosticCode code;
/// The offset of the beginning of the error's region.
final int offset;
/// The offset of the beginning of the error's region.
final int length;
/// The message text of the error or `null` if the message should not be
/// checked.
final String? message;
/// A pattern that should be contained in the error message or `null` if the
/// message contents should not be checked.
final Pattern? messageContains;
/// The list of context messages that are expected to be associated with the
/// error.
final List<ExpectedContextMessage> expectedContextMessages;
/// Initialize a newly created error description.
new(
this.code,
this.offset,
this.length, {
this.message,
this.messageContains,
this.expectedContextMessages = const <ExpectedContextMessage>[],
});
/// Return `true` if the [diagnostic] matches this description of what the state
/// of the [diagnostic] is expected to be.
bool matches(Diagnostic diagnostic) {
if (diagnostic.offset != offset ||
diagnostic.length != length ||
diagnostic.diagnosticCode != code) {
return false;
}
if (message != null && diagnostic.message != message) {
return false;
}
var messageContains = this.messageContains;
if (messageContains != null &&
!diagnostic.message.contains(messageContains)) {
return false;
}
var contextMessages = diagnostic.contextMessages.toList();
contextMessages.sort((first, second) {
var result = first.filePath.compareTo(second.filePath);
if (result != 0) {
return result;
}
return second.offset - first.offset;
});
if (contextMessages.length != expectedContextMessages.length) {
return false;
}
for (var i = 0; i < expectedContextMessages.length; i++) {
if (!expectedContextMessages[i].matches(contextMessages[i])) {
return false;
}
}
return true;
}
}
/// A diagnostic listener that collects all of the diagnostics passed to it for
/// later examination.
class GatheringDiagnosticListener implements DiagnosticListener {
/// A flag indicating whether diagnostic ranges are to be compared when
/// comparing expected and actual diagnostic.
final bool checkRanges;
/// A list containing the diagnostics that were collected.
final List<Diagnostic> _diagnostics = <Diagnostic>[];
/// A table mapping sources to the line information for the source.
final Map<Source, LineInfo> _lineInfoMap = <Source, LineInfo>{};
/// Initialize a newly created diagnostic listener to collect diagnostics.
new({this.checkRanges = true});
/// The diagnostics that were collected.
List<Diagnostic> get diagnostics => _diagnostics;
/// Whether at least one diagnostic has been gathered.
bool get hasDiagnostics => _diagnostics.isNotEmpty;
/// Adds the given [diagnostics] to this listener.
void addAll(List<Diagnostic> diagnostics) {
for (var diagnostic in diagnostics) {
onDiagnostic(diagnostic);
}
}
/// Adds all of the diagnostics recorded by the given [listener] to this
/// listener.
void addAll2(RecordingDiagnosticListener listener) {
addAll(listener.diagnostics);
}
/// Assert that the number of errors that have been gathered matches the
/// number of [expectedErrors] and that they have the expected error codes and
/// locations. The order in which the errors were gathered is ignored.
void assertErrors(List<ExpectedError> expectedErrors) {
//
// Match actual errors to expected errors.
//
var unmatchedActual = diagnostics.toList();
var unmatchedExpected = expectedErrors.toList();
var actualIndex = 0;
while (actualIndex < unmatchedActual.length) {
var matchFound = false;
var expectedIndex = 0;
while (expectedIndex < unmatchedExpected.length) {
if (unmatchedExpected[expectedIndex].matches(
unmatchedActual[actualIndex],
)) {
matchFound = true;
unmatchedActual.removeAt(actualIndex);
unmatchedExpected.removeAt(expectedIndex);
break;
}
expectedIndex++;
}
if (!matchFound) {
actualIndex++;
}
}
//
// Write the results.
//
var buffer = StringBuffer();
if (unmatchedExpected.isNotEmpty) {
buffer.writeln('Expected but did not find:');
for (var expected in unmatchedExpected) {
buffer.write(' ');
buffer.write(expected.code);
buffer.write(' [');
buffer.write(expected.offset);
buffer.write(', ');
buffer.write(expected.length);
if (expected.message != null) {
buffer.write(', ');
buffer.write(expected.message);
}
buffer.writeln(']');
}
}
if (unmatchedActual.isNotEmpty) {
if (buffer.isNotEmpty) {
buffer.writeln();
}
buffer.writeln('Found but did not expect:');
for (var actual in unmatchedActual) {
buffer.write(' ');
buffer.write(actual.diagnosticCode);
buffer.write(' [');
buffer.write(actual.offset);
buffer.write(', ');
buffer.write(actual.length);
buffer.write(', ');
buffer.write(actual.message);
buffer.writeln(']');
}
}
if (buffer.isNotEmpty) {
diagnostics.sort(
(first, second) => first.offset.compareTo(second.offset),
);
buffer.writeln();
buffer.writeln('To accept the current state, expect:');
for (var actual in diagnostics) {
var contextMessages = actual.contextMessages;
buffer.write(' error(');
buffer.write(actual.diagnosticCode.constantName);
buffer.write(', ');
buffer.write(actual.offset);
buffer.write(', ');
buffer.write(actual.length);
if (contextMessages.isNotEmpty) {
buffer.write(', contextMessages: [');
for (var i = 0; i < contextMessages.length; i++) {
var message = contextMessages[i];
if (i > 0) {
buffer.write(', ');
}
buffer.write('message(\'');
buffer.write(message.filePath);
buffer.write('\', ');
buffer.write(message.offset);
buffer.write(', ');
buffer.write(message.length);
buffer.write(')');
}
buffer.write(']');
}
buffer.writeln('),');
}
fail(buffer.toString());
}
}
/// Assert that the number of errors that have been gathered matches the
/// number of [expectedCodes] and that they have the expected error
/// codes. The order in which the errors were gathered is ignored.
void assertErrorsWithCodes([
List<DiagnosticCode> expectedCodes = const <DiagnosticCode>[],
]) {
var buffer = StringBuffer();
//
// Compute the expected number of each type of error.
//
var expectedCounts = <DiagnosticCode, int>{};
for (var code in expectedCodes) {
var count = expectedCounts[code];
if (count == null) {
count = 1;
} else {
count = count + 1;
}
expectedCounts[code] = count;
}
// Compute the actual number of each type of diagnostic.
var diagnosticsByCode = <DiagnosticCode, List<Diagnostic>>{};
for (var diagnostic in _diagnostics) {
diagnosticsByCode
.putIfAbsent(diagnostic.diagnosticCode, () => <Diagnostic>[])
.add(diagnostic);
}
// Compare the expected and actual number of each type of diagnostic.
expectedCounts.forEach((DiagnosticCode code, int expectedCount) {
int actualCount;
var list = diagnosticsByCode.remove(code);
if (list == null) {
actualCount = 0;
} else {
actualCount = list.length;
}
if (actualCount != expectedCount) {
if (buffer.length == 0) {
buffer.write('Expected ');
} else {
buffer.write('; ');
}
buffer.write(expectedCount);
buffer.write(' errors of type ');
buffer.write(code.lowerCaseName);
buffer.write(', found ');
buffer.write(actualCount);
}
});
//
// Check that there are no more errors in the actual-errors map,
// otherwise record message.
//
diagnosticsByCode.forEach((
DiagnosticCode code,
List<Diagnostic> actualDiagnostics,
) {
var actualCount = actualDiagnostics.length;
if (buffer.length == 0) {
buffer.write('Expected ');
} else {
buffer.write('; ');
}
buffer.write('0 errors of type ');
buffer.write(code.lowerCaseUniqueName);
buffer.write(', found ');
buffer.write(actualCount);
buffer.write(' (');
for (var i = 0; i < actualDiagnostics.length; i++) {
var error = actualDiagnostics[i];
if (i > 0) {
buffer.write(', ');
}
buffer.write(error.offset);
}
buffer.write(')');
});
if (buffer.length > 0) {
fail(buffer.toString());
}
}
/// Assert that the number of errors that have been gathered matches the
/// number of [expectedSeverities] and that there are the same number of
/// errors and warnings as specified by the argument. The order in which the
/// errors were gathered is ignored.
void assertErrorsWithSeverities(List<DiagnosticSeverity> expectedSeverities) {
var expectedErrorCount = 0;
var expectedWarningCount = 0;
for (var severity in expectedSeverities) {
if (severity == DiagnosticSeverity.ERROR) {
expectedErrorCount++;
} else {
expectedWarningCount++;
}
}
var actualErrorCount = 0;
var actualWarningCount = 0;
for (var diagnostic in _diagnostics) {
if (diagnostic.diagnosticCode.severity == DiagnosticSeverity.ERROR) {
actualErrorCount++;
} else {
actualWarningCount++;
}
}
if (expectedErrorCount != actualErrorCount ||
expectedWarningCount != actualWarningCount) {
fail(
'Expected $expectedErrorCount errors '
'and $expectedWarningCount warnings, '
'found $actualErrorCount errors '
'and $actualWarningCount warnings',
);
}
}
/// Assert that no errors have been gathered.
void assertNoErrors() {
assertErrors(ExpectedError.NO_ERRORS);
}
/// Return the line information associated with the given [source], or `null`
/// if no line information has been associated with the source.
LineInfo? getLineInfo(Source source) => _lineInfoMap[source];
/// Returns whether a diagnostic with the given [diagnosticCode] has been
/// gathered.
bool hasDiagnostic(DiagnosticCode diagnosticCode) {
for (var diagnostic in _diagnostics) {
if (identical(diagnostic.diagnosticCode, diagnosticCode)) {
return true;
}
}
return false;
}
@override
void onDiagnostic(Diagnostic diagnostic) {
_diagnostics.add(diagnostic);
}
/// Set the line information associated with the given [source] to the given
/// list of [lineStarts].
void setLineInfo(Source source, List<int> lineStarts) {
_lineInfoMap[source] = LineInfo(lineStarts);
}
}