CQ. Update resolveTestCodeWithDiagnostics() to use removeDiagnosticExpectations().

Just as parser tests do.
So, there is no interference with extra lines.

Change-Id: I1d49d6d10c69fddc6b0632eeae10ed522c0cda57
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/505483
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Paul Berry <paulberry@google.com>
This commit is contained in:
Konstantin Shcheglov
2026-05-21 13:14:17 -07:00
committed by dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent 026043d9ed
commit da7f207611
16 changed files with 74 additions and 142 deletions
@@ -4208,7 +4208,7 @@ void foo() {
MethodInvocation
target: SimpleIdentifier
token: list
element: list@237
element: list@68
staticType: List<dynamic>
operator: .
methodName: SimpleIdentifier
@@ -4225,7 +4225,7 @@ MethodInvocation
leftParenthesis: (
parameter: RegularFormalParameter
name: e
declaredFragment: <testLibraryFragment> e@389
declaredFragment: <testLibraryFragment> e@93
element: hasImplicitType isPublic
type: dynamic
rightParenthesis: )
@@ -4233,7 +4233,7 @@ MethodInvocation
functionDefinition: =>
expression: SimpleIdentifier
token: e
element: e@389
element: e@93
staticType: dynamic
declaredFragment: <testLibraryFragment> null@null
element: null@null
@@ -4254,7 +4254,7 @@ MethodInvocation
MethodInvocation
target: SimpleIdentifier
token: list
element: list@237
element: list@68
staticType: List<dynamic>
operator: .
methodName: SimpleIdentifier
@@ -4271,7 +4271,7 @@ MethodInvocation
leftParenthesis: (
parameter: RegularFormalParameter
name: e
declaredFragment: <testLibraryFragment> e@411
declaredFragment: <testLibraryFragment> e@115
element: hasImplicitType isPublic
type: dynamic
rightParenthesis: )
@@ -4563,7 +4563,7 @@ MethodDeclaration
typeParameters
TypeParameter
name: T
declaredFragment: <testLibraryFragment> T@221
declaredFragment: <testLibraryFragment> T@61
defaultType: dynamic
rightBracket: >
parameters: FormalParameterList
@@ -4574,7 +4574,7 @@ MethodDeclaration
element: #E0 T
type: T
name: y
declaredFragment: <testLibraryFragment> y@226
declaredFragment: <testLibraryFragment> y@66
element: isPublic
type: T
rightParenthesis: )
@@ -4584,7 +4584,7 @@ MethodDeclaration
literal: null
staticType: Null
semicolon: ;
declaredFragment: <testLibraryFragment> f@219
declaredFragment: <testLibraryFragment> f@59
element: <testLibrary>::@class::D::@method::f
type: T Function<T>(T)
''');
@@ -172,7 +172,7 @@ ConstructorDeclaration
thisKeyword: this
period: .
name: _x
declaredFragment: <testLibraryFragment> x@103
declaredFragment: <testLibraryFragment> x@31
element: hasImplicitType isFinal isPublic
type: int?
field: <testLibrary>::@class::C::@field::_x
@@ -21,7 +21,7 @@ class EnumDeclarationResolutionTest extends PubPackageResolutionTest {
await resolveTestCodeWithDiagnostics(r'''
enum E {
v(() {
// [diag.constWithNonConstantArgument][column 5][length 193] Arguments of a constant creation must be constant expressions.
// [diag.constWithNonConstantArgument][column 5][length 69] Arguments of a constant creation must be constant expressions.
Object? x = 0;
if (x is int) {
x.isEven;
@@ -1838,7 +1838,7 @@ FieldDeclaration
token: foo
element: <null>
staticType: InvalidType
declaredFragment: <testLibraryFragment> bar@206
declaredFragment: <testLibraryFragment> bar@39
semicolon: ;
declaredFragment: <null>
''');
@@ -2693,7 +2693,7 @@ ExtensionTypeDeclaration
equals: =
initializer: SimpleStringLiteral
literal: 'not a type'
declaredFragment: <testLibraryFragment> int@107
declaredFragment: <testLibraryFragment> int@49
semicolon: ;
declaredFragment: <null>
rightBracket: }
@@ -642,14 +642,14 @@ ForStatement
equals: =
initializer: SimpleStringLiteral
literal: 'a'
declaredFragment: isPublic i@152
declaredFragment: isPublic i@61
element: hasImplicitType isPublic
type: String
semicolon: ;
ExpressionStatement
expression: SimpleIdentifier
token: i
element: i@152
element: i@61
staticType: String
semicolon: ;
rightBracket: }
@@ -1555,14 +1555,14 @@ ForStatement
initializer: IntegerLiteral
literal: 1
staticType: int
declaredFragment: isPublic a@165
declaredFragment: isPublic a@67
element: hasImplicitType isPublic
type: int
semicolon: ;
ExpressionStatement
expression: SimpleIdentifier
token: a
element: a@165
element: a@67
staticType: int
semicolon: ;
rightBracket: }
@@ -2220,14 +2220,14 @@ ForStatement
initializer: IntegerLiteral
literal: 1
staticType: int
declaredFragment: isPublic a@145
declaredFragment: isPublic a@53
element: hasImplicitType isPublic
type: int
semicolon: ;
ExpressionStatement
expression: SimpleIdentifier
token: a
element: a@145
element: a@53
staticType: int
semicolon: ;
rightBracket: }
@@ -450,7 +450,7 @@ FormalParameterList
leftParenthesis: (
rightParenthesis: )
question: ?
declaredFragment: <testLibraryFragment> p2@118
declaredFragment: <testLibraryFragment> p2@45
element: isPublic
type: void Function()?
rightParenthesis: )
@@ -472,7 +472,7 @@ FormalParameterList
leftParenthesis: (
rightParenthesis: )
question: ?
declaredFragment: <testLibraryFragment> p3@217
declaredFragment: <testLibraryFragment> p3@71
element: isPublic
type: void Function()?
rightDelimiter: }
@@ -142,7 +142,7 @@ void f() {
assertResolvedNodeText(node, r'''
SimpleIdentifier
token: a
element: a@149
element: a@34
staticType: InvalidType
''');
}
@@ -459,11 +459,12 @@ mixin ResolutionTest implements ResourceProviderMixin {
/// Resolves [code] and checks that its inline diagnostic markers match the
/// diagnostics. Unmarked code is expected to have no diagnostics.
Future<void> resolveTestCodeWithDiagnostics(String code) async {
addTestFile(code);
var cleanCode = removeDiagnosticExpectations(code);
addTestFile(cleanCode);
await resolveTestFile();
var actual = updateExpectedDiagnostics(
content: code,
content: cleanCode,
actualDiagnostics: result.diagnostics,
);
if (actual != code) {
@@ -1197,7 +1197,7 @@ SwitchStatement
element: dart:core::@class::int
type: int
name: a
declaredFragment: isPublic a@128
declaredFragment: isPublic a@61
element: isPublic
type: int
matchedValueType: Object?
@@ -1206,7 +1206,7 @@ SwitchStatement
expression: BinaryExpression
leftOperand: SimpleIdentifier
token: a
element: a@128
element: a@61
staticType: int
operator: >
rightOperand: IntegerLiteral
@@ -5,12 +5,14 @@
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../dart/resolution/context_collection_resolution.dart';
import '../dart/resolution/node_text_expectations.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(DeadCodeTest);
defineReflectiveTests(DeadCodeTest_Language219);
defineReflectiveTests(DeadCodeTest_AnonymousMethodsExperiment);
defineReflectiveTests(UpdateNodeTextExpectations);
});
}
@@ -194,7 +196,7 @@ Never foo() => throw "Never";
test() {
int i = 0;
for (foo(); (i = 42) < 0;) {}
// [diag.deadCode][column 15][length 81] Dead code.
// [diag.deadCode][column 15][length 29] Dead code.
return i;
}
''');
@@ -551,7 +553,7 @@ class C {
class A {
int x;
A() : x = throw 0 {
// [diag.deadCode][column 21][length 64] Dead code.
// [diag.deadCode][column 21][length 12] Dead code.
x;
}
}
@@ -1072,7 +1074,7 @@ void f(bool c) {
// ^^^^^^^^^^^^
// [diag.deadCode] Dead code.
print('');
// [diag.deadCode][column 5][length 89] Dead code.
// [diag.deadCode][column 5][length 38] Dead code.
} while (c);
print('');
}
@@ -1092,7 +1094,7 @@ void f(bool c) {
// ^^^^^^^^^^^^
// [diag.deadCode] Dead code.
print('');
// [diag.deadCode][column 5][length 65] Dead code.
// [diag.deadCode][column 5][length 14] Dead code.
}
}
''');
@@ -1521,7 +1523,7 @@ void f() {
{
return;
2;
// [diag.deadCode][column 7][length 59] Dead code.
// [diag.deadCode][column 7][length 8] Dead code.
}
}
}
@@ -1585,7 +1587,7 @@ main() {
2;
return;
3;
// [diag.deadCode][column 5][length 62] Dead code.
// [diag.deadCode][column 5][length 11] Dead code.
}
4;
}
@@ -1607,7 +1609,7 @@ void f() {
await resolveTestCodeWithDiagnostics(r'''
void f() {
if (false) {
// [diag.deadCode][column 14][length 64] Dead code.
// [diag.deadCode][column 14][length 12] Dead code.
1;
} else {
2;
@@ -1623,7 +1625,7 @@ void f() {
if (true) {
1;
} else {
// [diag.deadCode][column 10][length 64] Dead code.
// [diag.deadCode][column 10][length 12] Dead code.
2;
}
3;
@@ -1640,7 +1642,7 @@ void g(A a) {
a.f(0);
//^^^
// [diag.receiverOfTypeNever] The receiver is of type 'Never', and will never complete with a value.
// [diag.deadCode][column 6][length 175] Dead code.
// [diag.deadCode][column 6][length 16] Dead code.
print(1);
}
''');
@@ -1652,7 +1654,7 @@ void g(Never f) {
(f)(0);
//^^^
// [diag.receiverOfTypeNever] The receiver is of type 'Never', and will never complete with a value.
// [diag.deadCode][column 6][length 175] Dead code.
// [diag.deadCode][column 6][length 16] Dead code.
print(1);
}
''');
@@ -1664,7 +1666,7 @@ void g(Never f) {
f(0);
//^
// [diag.receiverOfTypeNever] The receiver is of type 'Never', and will never complete with a value.
// [diag.deadCode][column 4][length 173] Dead code.
// [diag.deadCode][column 4][length 16] Dead code.
print(1);
}
''');
@@ -1992,7 +1994,7 @@ f() {
print(1);
return;
print(2);
// [diag.deadCode][column 3][length 82] Dead code.
// [diag.deadCode][column 3][length 31] Dead code.
return;
print(3);
}
@@ -44,8 +44,6 @@ void f() {
// ^^^^^^^^^^^^^^^^^^^^^
// [diag.duplicateIgnore] The diagnostic 'unused_local_variable' doesn't need to be ignored here because it's already being ignored.
var x = 0;
// ^
// [diag.unusedLocalVariable] The value of the local variable 'x' isn't used.
}
''');
}
@@ -78,8 +76,6 @@ void f() {}
// ^^^^^^^^^
// [diag.duplicateIgnore] The diagnostic 'lint' doesn't need to be ignored here because it's already being ignored.
void g(arg1(int)) {} // AVOID_TYPES_AS_PARAMETER_NAMES
// ^^^
// [diag.avoidTypesAsParameterNamesFormalParameter] The parameter name 'int' matches a visible type name.
''');
}
@@ -6,11 +6,13 @@ import 'package:analyzer/src/diagnostic/diagnostic.dart' as diag;
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../dart/resolution/context_collection_resolution.dart';
import '../dart/resolution/node_text_expectations.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(ForInOfInvalidTypeTest);
defineReflectiveTests(ForInOfInvalidTypeWithStrictCastsTest);
defineReflectiveTests(UpdateNodeTextExpectations);
});
}
@@ -44,7 +46,7 @@ f(bool e) async {
await resolveTestCodeWithDiagnostics(r'''
f(Never e) async {
await for (var id in e) {
// [diag.deadCode][column 14][length 78] Dead code.
// [diag.deadCode][column 14][length 26] Dead code.
id;
}
}
@@ -135,7 +137,7 @@ f(L e) {
await resolveTestCodeWithDiagnostics(r'''
f(Never e) {
for (var id in e) {
// [diag.deadCode][column 8][length 77] Dead code.
// [diag.deadCode][column 8][length 26] Dead code.
id;
}
}
@@ -47,7 +47,7 @@ GuardedPattern
pattern: DeclaredVariablePattern
keyword: final
name: b
declaredFragment: isFinal isPublic b@160
declaredFragment: isFinal isPublic b@61
element: hasImplicitType isFinal isPublic
type: int
matchedValueType: int
@@ -84,7 +84,7 @@ GuardedPattern
pattern: DeclaredVariablePattern
keyword: final
name: b
declaredFragment: isFinal isPublic b@159
declaredFragment: isFinal isPublic b@60
element: hasImplicitType isFinal isPublic
type: int
matchedValueType: int
@@ -692,7 +692,7 @@ GuardedPattern
pattern: DeclaredVariablePattern
keyword: final
name: a
declaredFragment: isFinal isPublic a@177
declaredFragment: isFinal isPublic a@76
element: hasImplicitType isFinal isPublic
type: int
matchedValueType: int
@@ -805,7 +805,7 @@ main() {
void f() {
int v;
if (false) {
// [diag.deadCode][column 14][length 77] Dead code.
// [diag.deadCode][column 14][length 25] Dead code.
// not assigned
} else {
v = 0;
@@ -868,7 +868,7 @@ void f() {
void f() {
int v;
if (!true) {
// [diag.deadCode][column 14][length 77] Dead code.
// [diag.deadCode][column 14][length 25] Dead code.
// not assigned
} else {
v = 0;
@@ -33,7 +33,7 @@ void f() {
assertResolvedNodeText(node, r'''
SimpleIdentifier
token: v
element: v@149
element: v@34
staticType: InvalidType
''');
}
@@ -168,7 +168,7 @@ void f(int a) {
assertResolvedNodeText(node, r'''
SimpleIdentifier
token: v
element: v@194
element: v@75
staticType: void Function()
''');
}
@@ -195,7 +195,7 @@ void f(int a) {
assertResolvedNodeText(node, r'''
SimpleIdentifier
token: v
element: v@210
element: v@91
staticType: void Function()
''');
}
@@ -221,7 +221,7 @@ void f(int a) {
assertResolvedNodeText(node, r'''
SimpleIdentifier
token: v
element: v@193
element: v@74
staticType: dynamic
''');
}
@@ -248,7 +248,7 @@ void f(int a) {
assertResolvedNodeText(node, r'''
SimpleIdentifier
token: v
element: v@209
element: v@90
staticType: dynamic
''');
}
@@ -274,7 +274,7 @@ void f(int a) {
assertResolvedNodeText(node, r'''
SimpleIdentifier
token: v
element: v@195
element: v@76
staticType: void Function()
''');
}
@@ -301,7 +301,7 @@ void f(int a) {
assertResolvedNodeText(node, r'''
SimpleIdentifier
token: v
element: v@211
element: v@92
staticType: void Function()
''');
}
@@ -327,7 +327,7 @@ void f(int a) {
assertResolvedNodeText(node, r'''
SimpleIdentifier
token: v
element: v@194
element: v@75
staticType: dynamic
''');
}
@@ -354,7 +354,7 @@ void f(int a) {
assertResolvedNodeText(node, r'''
SimpleIdentifier
token: v
element: v@210
element: v@91
staticType: dynamic
''');
}
@@ -41,8 +41,9 @@ String removeTrailingLineTerminator(String content) {
/// Returns [content] with canonical diagnostic expectation markers.
///
/// Existing diagnostic expectation marker lines are removed before the new
/// markers are inserted, so [content] can be either unmarked or already marked.
/// The [content] must not include diagnostic expectation marker lines. Use
/// [removeDiagnosticExpectations] before analysis, and pass the analyzed
/// content here.
String updateExpectedDiagnostics({
required String content,
required List<Diagnostic> actualDiagnostics,
@@ -60,7 +61,16 @@ final class _ExpectedDiagnosticsUpdater {
_ExpectedDiagnosticsUpdater(String content)
: lines = _Line.parse(content),
lineInfo = LineInfo.fromContent(content);
lineInfo = LineInfo.fromContent(content) {
for (var line in lines) {
if (_LineMarker.isMarker(line)) {
throw StateError(
'Expected content without diagnostic expectation markers, '
'found one on line ${line.number}.',
);
}
}
}
String update(List<Diagnostic> actualDiagnostics) {
_generateMarkers(actualDiagnostics);
@@ -90,10 +100,7 @@ final class _ExpectedDiagnosticsUpdater {
var id = nextContextId++;
contextRefs.add(id);
var location = _markerLocation(
offset: contextMessage.offset,
length: contextMessage.length,
);
var location = _markerLocation(offset: contextMessage.offset);
var line = lines[location.lineNumber - 1];
var presentation = _markerPresentation(
line,
@@ -115,10 +122,7 @@ final class _ExpectedDiagnosticsUpdater {
);
}
var location = _markerLocation(
offset: diagnostic.offset,
length: diagnostic.length,
);
var location = _markerLocation(offset: diagnostic.offset);
var line = lines[location.lineNumber - 1];
var presentation = _markerPresentation(
line,
@@ -150,32 +154,8 @@ final class _ExpectedDiagnosticsUpdater {
}
/// Returns where a generated marker should be written for an actual range.
///
/// For a normal diagnostic range, the marker belongs on the line reported by
/// [LineInfo]. For a zero-length diagnostic, the diagnostic is often an
/// insertion point rather than a source span. If the input is already
/// marked, that insertion point can be pushed into the existing marker
/// comments, or to the empty line after them, even though the marker should
/// still be attached to the preceding real source line. In that case, keep
/// the marker on the source line and express the insertion point as the
/// column after its last character.
({int lineNumber, int column}) _markerLocation({
required int offset,
required int length,
}) {
({int lineNumber, int column}) _markerLocation({required int offset}) {
var location = lineInfo.getLocation(offset);
if (length == 0) {
// Only zero-length diagnostics can legitimately move onto marker-only
// text from a previous update. A non-zero range on a marker line would
// describe the marker comment itself, not an insertion point in code.
var targetLine = _targetLineForMarkerShift(location.lineNumber);
if (targetLine != null) {
return (
lineNumber: targetLine.number,
column: targetLine.text.length + 1,
);
}
}
return (lineNumber: location.lineNumber, column: location.columnNumber);
}
@@ -204,59 +184,10 @@ final class _ExpectedDiagnosticsUpdater {
);
}
/// Finds the real source line that owns a shifted zero-length marker.
///
/// The updater accepts both clean source and source that already contains
/// diagnostic expectation comments. Existing marker comments are removed when
/// the new content is written, but actual diagnostics are computed before
/// that removal. This matters for zero-length diagnostics near the end of a
/// line or file: after a previous update, the analyzer may report the same
/// insertion point as being on a marker line, or on the empty line
/// immediately following marker lines.
///
/// This method recognizes only those shifted positions. If [lineNumber]
/// points at ordinary source text, or at an empty line that is not directly
/// after a marker, there is nothing to repair and `null` is returned.
/// Otherwise the search walks backward over marker lines and returns the
/// nearest preceding non-marker line, which is where the regenerated marker
/// should be attached.
_Line? _targetLineForMarkerShift(int lineNumber) {
if (lineNumber < 1 || lineNumber > lines.length) {
return null;
}
var line = lines[lineNumber - 1];
if (!_LineMarker.isMarker(line)) {
// A non-marker line normally owns the reported offset. The one exception
// is the synthetic empty line after existing markers, which can be where
// EOF-style zero-length diagnostics land.
var previousLine = lineNumber > 1 ? lines[lineNumber - 2] : null;
if (line.text.isNotEmpty ||
previousLine == null ||
!_LineMarker.isMarker(previousLine)) {
return null;
}
}
// The reported line is either a marker line or the empty line just after
// marker lines. Walk back to the line these markers annotate.
for (var index = lineNumber - 2; index >= 0; index--) {
var previousLine = lines[index];
if (!_LineMarker.isMarker(previousLine)) {
return previousLine;
}
}
return null;
}
String _writeContent() {
var buffer = StringBuffer();
var isFirstLine = true;
for (var line in lines) {
if (_LineMarker.isMarker(line)) {
continue;
}
if (isFirstLine) {
isFirstLine = false;
} else {