Augment. Report incomplete augmented executable declarations.

Report new diagnostics when an introductory function, member, or factory
constructor is still incomplete after applying all augmentations.

Keep the existing missing-body diagnostics for declarations that have no
augmentations, but report augmentation-specific diagnostics when an
augmentation chain exists and none of the fragments provides a body or
factory redirection.

Move the checks into error verification so that extension and extension
type members can participate in augmentation completion before reporting
the existing abstract-member diagnostics. Also suppress the
corresponding shared parser diagnostic when it is reported by the
verifier.

Consolidate body-related tests by declaration shape instead of by
individual diagnostic. This keeps missing bodies, external bodies,
augmentation completion, and already-complete checks side by side,
making the interaction between these rules easier to review and extend.

Move the constructorAlreadyComplete coverage from its dedicated test
file into constructor_body_test.dart, and add the factory body
completeness cases there as well. Add executable_body_test.dart for
function and member body coverage, including top-level declarations,
static members, extension members, and extension type members.

Add diagnostic definitions and fix-status entries for the new
diagnostics.

Change-Id: I9ee803c1e767ff47a608c86413ef7ffc71518cfd
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/503540
Reviewed-by: Paul Berry <paulberry@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Konstantin Shcheglov
2026-05-14 15:08:39 -07:00
committed by dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent 433b4fa3c8
commit 56706b715d
20 changed files with 2750 additions and 790 deletions
+2
View File
@@ -1639,6 +1639,7 @@ extensionDeclaresAbstractMember:
problemMessage: "Extensions can't declare abstract members."
correctionMessage: "Try providing an implementation for the member."
script: |
// @dart=3.5
extension E on int {
void method();
}
@@ -1656,6 +1657,7 @@ extensionDeclaresAbstractMember:
have a body:
```dart
%language=3.5
extension E on String {
int [!a!]();
}
@@ -371,6 +371,10 @@ conflicting_constructor_and_static_setter:
The fix is to rename one of the two, but we can't know what name to use.
constructor_already_complete:
status: needsEvaluation
function_not_complete_after_augmentations:
status: needsEvaluation
factory_not_complete_after_augmentations:
status: needsEvaluation
conflicting_field_and_method:
status: noFix
notes: |-
@@ -76,13 +76,15 @@ enum E {
''');
}
test_extenstionTypeWithAbstractMember_getter() async {
test_extensionTypeWithAbstractMember_getter() async {
await resolveTestCode('''
// @dart = 3.5
extension type A(int it) {
int get g;
}
''');
await assertHasFix('''
// @dart = 3.5
extension type A(int it) {
int get g {
// TODO: implement g
@@ -92,13 +94,15 @@ extension type A(int it) {
''');
}
test_extenstionTypeWithAbstractMember_method() async {
test_extensionTypeWithAbstractMember_method() async {
await resolveTestCode('''
// @dart = 3.5
extension type A(int it) {
void f();
}
''');
await assertHasFix('''
// @dart = 3.5
extension type A(int it) {
void f() {
// TODO: implement f
@@ -340,6 +340,7 @@ class ElementBindingVisitor extends RecursiveAstVisitor<void> {
fragment.isExternal = true;
}
fragment.isCompleteDeclaration = node.isCompleteDeclaration;
fragment.isAsynchronous = body.isAsynchronous;
fragment.isGenerator = body.isGenerator;
if (node.returnType == null) {
@@ -5815,6 +5815,25 @@ const DiagnosticWithoutArguments factoryConstructorNewName =
expectedTypes: [],
);
/// Parameters:
/// String name: the name of the factory constructor
const DiagnosticWithArguments<
LocatableDiagnostic Function({required String name})
>
factoryNotCompleteAfterAugmentations = DiagnosticWithArguments(
name: 'factory_not_complete_after_augmentations',
problemMessage:
"The factory constructor '{0}' must have a body or redirection after all "
"augmentations are applied.",
correctionMessage:
"Try adding a body or redirection to the introductory declaration, or "
"providing an augmentation with a body or redirection.",
type: DiagnosticType.COMPILE_TIME_ERROR,
uniqueName: 'factory_not_complete_after_augmentations',
withArguments: _withArgumentsFactoryNotCompleteAfterAugmentations,
expectedTypes: [ExpectedType.string],
);
/// No parameters.
const DiagnosticWithoutArguments factoryTopLevelDeclaration =
DiagnosticWithoutArgumentsImpl(
@@ -6474,6 +6493,25 @@ functionAlreadyComplete = DiagnosticWithoutArgumentsImpl(
expectedTypes: [],
);
/// Parameters:
/// String name: the name of the function, method, getter, or setter
const DiagnosticWithArguments<
LocatableDiagnostic Function({required String name})
>
functionNotCompleteAfterAugmentations = DiagnosticWithArguments(
name: 'function_not_complete_after_augmentations',
problemMessage:
"The function or member '{0}' must have a body after all augmentations are "
"applied.",
correctionMessage:
"Try adding a body to the introductory declaration, or providing an "
"augmentation with a body.",
type: DiagnosticType.COMPILE_TIME_ERROR,
uniqueName: 'function_not_complete_after_augmentations',
withArguments: _withArgumentsFunctionNotCompleteAfterAugmentations,
expectedTypes: [ExpectedType.string],
);
/// No parameters.
const DiagnosticWithoutArguments
functionTypedParameterVar = DiagnosticWithoutArgumentsImpl(
@@ -19335,6 +19373,14 @@ LocatableDiagnostic _withArgumentsExtraPositionalArgumentsCouldBeNamed({
]);
}
LocatableDiagnostic _withArgumentsFactoryNotCompleteAfterAugmentations({
required String name,
}) {
return LocatableDiagnosticImpl(diag.factoryNotCompleteAfterAugmentations, [
name,
]);
}
LocatableDiagnostic _withArgumentsFfiNativeUnexpectedNumberOfParameters({
required int expected,
required int actual,
@@ -19477,6 +19523,14 @@ LocatableDiagnostic _withArgumentsForInOfInvalidType({
]);
}
LocatableDiagnostic _withArgumentsFunctionNotCompleteAfterAugmentations({
required String name,
}) {
return LocatableDiagnosticImpl(diag.functionNotCompleteAfterAugmentations, [
name,
]);
}
LocatableDiagnostic _withArgumentsGenericStructSubclass({
required String className,
}) {
@@ -388,6 +388,7 @@ const List<DiagnosticCode> diagnosticCodeValues = [
diag.extraneousModifierInExtensionType,
diag.extraneousModifierInPrimaryConstructor,
diag.factoryConstructorNewName,
diag.factoryNotCompleteAfterAugmentations,
diag.factoryTopLevelDeclaration,
diag.factoryWithInitializers,
diag.factoryWithoutBody,
@@ -431,6 +432,7 @@ const List<DiagnosticCode> diagnosticCodeValues = [
diag.forInOfInvalidType,
diag.forInWithConstVariable,
diag.functionAlreadyComplete,
diag.functionNotCompleteAfterAugmentations,
diag.functionTypedParameterVar,
diag.genericFunctionTypeCannotBeBound,
diag.genericFunctionTypeCannotBeTypeArgument,
@@ -361,7 +361,8 @@ class FastaErrorReporter {
// Reported by [ErrorVerifier].
if (sharedCode == SharedCode.externalFactoryWithBody ||
sharedCode == SharedCode.redirectingConstructorWithBody ||
sharedCode == SharedCode.externalMethodWithBody) {
sharedCode == SharedCode.externalMethodWithBody ||
sharedCode == SharedCode.extensionDeclaresAbstractMember) {
return;
}
var diagnosticCode = sharedAnalyzerCodes[sharedCode.index];
@@ -686,6 +686,8 @@ class ErrorVerifier extends RecursiveAstVisitor<void>
}
}
_checkForFactoryBodyCompleteness(node);
_withEnclosingExecutable(
element,
() {
@@ -1160,6 +1162,11 @@ class ErrorVerifier extends RecursiveAstVisitor<void>
augmentKeyword: node.augmentKeyword,
fragment: fragment,
);
_checkForFunctionBodyCompleteness(
node: node,
nameToken: node.name,
fragment: fragment,
);
_checkForAugmentationTypeParameters(
fragment: fragment,
firstTypeParameters: element.firstFragment.typeParameters,
@@ -1414,6 +1421,12 @@ class ErrorVerifier extends RecursiveAstVisitor<void>
augmentKeyword: node.augmentKeyword,
fragment: fragment,
);
_checkForFunctionBodyCompleteness(
node: node,
nameToken: node.name,
fragment: fragment,
);
_checkForExtensionDeclaresAbstractMember(node);
_checkForAugmentationTypeParameters(
fragment: fragment,
firstTypeParameters: element.firstFragment.typeParameters,
@@ -4088,6 +4101,27 @@ class ErrorVerifier extends RecursiveAstVisitor<void>
_typeProvider.isNonSubtypableClass(type.element);
}
void _checkForExtensionDeclaresAbstractMember(MethodDeclarationImpl node) {
if (_featureSet.isEnabled(Feature.augmentations)) {
return;
}
if (_enclosingExtension == null) {
return;
}
// Static members without bodies are already reported by the parser.
if (node.isStatic) {
return;
}
if (node.isAbstract) {
diagnosticReporter.report(
diag.extensionDeclaresAbstractMember.at(node.name),
);
}
}
void _checkForExtensionDeclaresInstanceField(FieldDeclaration node) {
if (node.parent?.parent is! ExtensionDeclaration) {
return;
@@ -4359,6 +4393,9 @@ class ErrorVerifier extends RecursiveAstVisitor<void>
void _checkForExtensionTypeWithAbstractMember(
ExtensionTypeDeclarationImpl node,
) {
if (_featureSet.isEnabled(Feature.augmentations)) {
return;
}
for (var member in node.body.members) {
if (member is MethodDeclarationImpl && !member.isStatic) {
if (member.isAbstract) {
@@ -4395,6 +4432,38 @@ class ErrorVerifier extends RecursiveAstVisitor<void>
return false;
}
void _checkForFactoryBodyCompleteness(ConstructorDeclarationImpl node) {
if (!_featureSet.isEnabled(Feature.augmentations)) {
return;
}
// Report only on the introductory declaration.
if (node.augmentKeyword != null) {
return;
}
if (node.factoryKeyword == null) {
return;
}
var element = node.declaredFragment!.element;
if (element.fragments.any((f) => f.isCompleteDeclaration)) {
return;
}
if (element.fragments.length == 1) {
diagnosticReporter.report(
diag.factoryWithoutBody.atSourceRange(node.errorRange),
);
} else {
diagnosticReporter.report(
diag.factoryNotCompleteAfterAugmentations
.withArguments(name: node.declaredFragment!.name)
.atSourceRange(node.errorRange),
);
}
}
/// Verify that the given field formal [parameter] is in a constructor
/// declaration.
///
@@ -4541,6 +4610,75 @@ class ErrorVerifier extends RecursiveAstVisitor<void>
}
}
void _checkForFunctionBodyCompleteness({
required AstNode node,
required Token nameToken,
required ExecutableFragmentImpl fragment,
}) {
if (!_featureSet.isEnabled(Feature.augmentations)) {
return;
}
// Report only on the introductory declaration.
if (fragment.isAugmentation) {
return;
}
var element = fragment.element;
var enclosingElement = element.enclosingElement;
// Instance members are validated for the whole interface.
if (!element.isStatic) {
if (enclosingElement is ClassElement ||
enclosingElement is EnumElement ||
enclosingElement is MixinElement) {
return;
}
}
var name = element.name;
if (name == null) {
return;
}
if (element.fragments.any((f) => f.isCompleteDeclaration)) {
return;
}
if (element.fragments.length == 1) {
switch (enclosingElement) {
case ExtensionElement() when !element.isStatic:
diagnosticReporter.report(
diag.extensionDeclaresAbstractMember.at(nameToken),
);
case ExtensionTypeElement() when !element.isStatic:
diagnosticReporter.report(
diag.extensionTypeWithAbstractMember
.withArguments(
methodName: name,
extensionTypeName: enclosingElement.name!,
)
.at(node),
);
case _:
var body = switch (node) {
MethodDeclaration(:var body) => body,
FunctionDeclaration(:var functionExpression) =>
functionExpression.body,
_ => throw StateError('Unexpected node type: ${node.runtimeType}'),
};
var errorToken = (body as EmptyFunctionBody).semicolon;
diagnosticReporter.report(diag.missingFunctionBody.at(errorToken));
}
} else {
diagnosticReporter.report(
diag.functionNotCompleteAfterAugmentations
.withArguments(name: name)
.at(nameToken),
);
}
}
void _checkForGenericFunctionType(TypeAnnotation? node) {
if (node == null) {
return;
+17
View File
@@ -5022,6 +5022,22 @@ CompileTimeErrorCode:
problemMessage: "The augmentation can't provide a body because the function or member is already complete."
correctionMessage: "Try removing the body from the augmentation, or removing the body from the preceding declaration."
hasPublishedDocs: false
functionNotCompleteAfterAugmentations:
type: compileTimeError
parameters:
String name: the name of the function, method, getter, or setter
experiment: augmentations
problemMessage: "The function or member '#name' must have a body after all augmentations are applied."
correctionMessage: "Try adding a body to the introductory declaration, or providing an augmentation with a body."
hasPublishedDocs: false
factoryNotCompleteAfterAugmentations:
type: compileTimeError
parameters:
String name: the name of the factory constructor
experiment: augmentations
problemMessage: "The factory constructor '#name' must have a body or redirection after all augmentations are applied."
correctionMessage: "Try adding a body or redirection to the introductory declaration, or providing an augmentation with a body or redirection."
hasPublishedDocs: false
duplicateNamedArgument:
type: compileTimeError
parameters:
@@ -6864,6 +6880,7 @@ CompileTimeErrorCode:
extension type `E` is abstract:
```dart
%language=3.5
extension type E(String s) {
[!void m();!]
}
@@ -2,7 +2,6 @@
// 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.
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';
@@ -16,33 +15,31 @@ main() {
@reflectiveTest
class AugmentationReturnTypeMismatchTest extends PubPackageResolutionTest {
test_class_getter_int_String() async {
await assertErrorsInCode(
r'''
await resolveTestCodeWithDiagnostics(r'''
class A {
int get foo => 0;
}
augment class A {
augment String get foo;
// ^^^^^^
// [diag.augmentationReturnTypeMismatch] The augmentation's return type 'String' must be the same as the introductory declaration's return type 'int'.
}
''',
[error(diag.augmentationReturnTypeMismatch, 61, 6)],
);
''');
}
test_class_method_void_int() async {
await assertErrorsInCode(
r'''
await resolveTestCodeWithDiagnostics(r'''
class A {
void foo() {}
}
augment class A {
augment int foo();
// ^^^
// [diag.augmentationReturnTypeMismatch] The augmentation's return type 'int' must be the same as the introductory declaration's return type 'void'.
}
''',
[error(diag.augmentationReturnTypeMismatch, 57, 3)],
);
''');
}
test_class_method_void_void() async {
@@ -58,105 +55,87 @@ augment class A {
}
test_extension_getter_int_String() async {
await assertErrorsInCode(
r'''
await resolveTestCodeWithDiagnostics(r'''
extension E on int {
int get foo => 0;
}
augment extension E {
augment String get foo;
// ^^^^^^
// [diag.augmentationReturnTypeMismatch] The augmentation's return type 'String' must be the same as the introductory declaration's return type 'int'.
}
''',
[
error(diag.augmentationReturnTypeMismatch, 76, 6),
error(diag.extensionDeclaresAbstractMember, 87, 3),
],
);
''');
}
test_extension_method_void_int() async {
await assertErrorsInCode(
r'''
await resolveTestCodeWithDiagnostics(r'''
extension E on int {
void foo() {}
}
augment extension E {
augment int foo();
// ^^^
// [diag.augmentationReturnTypeMismatch] The augmentation's return type 'int' must be the same as the introductory declaration's return type 'void'.
}
''',
[
error(diag.augmentationReturnTypeMismatch, 72, 3),
error(diag.extensionDeclaresAbstractMember, 76, 3),
],
);
''');
}
test_extensionType_getter_int_String() async {
await assertErrorsInCode(
r'''
await resolveTestCodeWithDiagnostics(r'''
extension type A(int it) {
int get foo => 0;
}
augment extension type A(int it) {
augment String get foo;
// ^^^^^^
// [diag.augmentationReturnTypeMismatch] The augmentation's return type 'String' must be the same as the introductory declaration's return type 'int'.
}
''',
[
error(diag.extensionTypeWithAbstractMember, 87, 23),
error(diag.augmentationReturnTypeMismatch, 95, 6),
],
);
''');
}
test_extensionType_method_void_int() async {
await assertErrorsInCode(
r'''
await resolveTestCodeWithDiagnostics(r'''
extension type A(int it) {
void foo() {}
}
augment extension type A(int it) {
augment int foo();
// ^^^
// [diag.augmentationReturnTypeMismatch] The augmentation's return type 'int' must be the same as the introductory declaration's return type 'void'.
}
''',
[
error(diag.extensionTypeWithAbstractMember, 83, 18),
error(diag.augmentationReturnTypeMismatch, 91, 3),
],
);
''');
}
test_mixin_getter_int_String() async {
await assertErrorsInCode(
r'''
await resolveTestCodeWithDiagnostics(r'''
mixin M {
int get foo => 0;
}
augment mixin M {
augment String get foo;
// ^^^^^^
// [diag.augmentationReturnTypeMismatch] The augmentation's return type 'String' must be the same as the introductory declaration's return type 'int'.
}
''',
[error(diag.augmentationReturnTypeMismatch, 61, 6)],
);
''');
}
test_mixin_method_void_int() async {
await assertErrorsInCode(
r'''
await resolveTestCodeWithDiagnostics(r'''
mixin M {
void foo() {}
}
augment mixin M {
augment int foo();
// ^^^
// [diag.augmentationReturnTypeMismatch] The augmentation's return type 'int' must be the same as the introductory declaration's return type 'void'.
}
''',
[error(diag.augmentationReturnTypeMismatch, 57, 3)],
);
''');
}
test_topLevelFunction_int_int_withImportPrefix() async {
@@ -171,39 +150,36 @@ augment core.int foo();
}
test_topLevelFunction_void_int() async {
await assertErrorsInCode(
r'''
await resolveTestCodeWithDiagnostics(r'''
void foo() {}
augment int foo();
''',
[error(diag.augmentationReturnTypeMismatch, 23, 3)],
);
// ^^^
// [diag.augmentationReturnTypeMismatch] The augmentation's return type 'int' must be the same as the introductory declaration's return type 'void'.
''');
}
test_topLevelFunction_void_int_viaTypeAlias() async {
await assertErrorsInCode(
r'''
await resolveTestCodeWithDiagnostics(r'''
typedef IntAlias = int;
void foo() {}
augment IntAlias foo();
''',
[error(diag.augmentationReturnTypeMismatch, 48, 8)],
);
// ^^^^^^^^
// [diag.augmentationReturnTypeMismatch] The augmentation's return type 'IntAlias' must be the same as the introductory declaration's return type 'void'.
''');
}
test_topLevelFunction_void_int_withImportPrefix() async {
await assertErrorsInCode(
r'''
await resolveTestCodeWithDiagnostics(r'''
import 'dart:core' as core;
void foo() {}
augment core.int foo();
''',
[error(diag.augmentationReturnTypeMismatch, 51, 8)],
);
// ^^^^^^^^
// [diag.augmentationReturnTypeMismatch] The augmentation's return type 'int' must be the same as the introductory declaration's return type 'void'.
''');
}
test_topLevelFunction_void_nothing() async {
@@ -233,13 +209,12 @@ augment VoidAlias foo();
}
test_topLevelGetter_int_String() async {
await assertErrorsInCode(
r'''
await resolveTestCodeWithDiagnostics(r'''
int get foo => 0;
augment String get foo;
''',
[error(diag.augmentationReturnTypeMismatch, 27, 6)],
);
// ^^^^^^
// [diag.augmentationReturnTypeMismatch] The augmentation's return type 'String' must be the same as the introductory declaration's return type 'int'.
''');
}
}
@@ -1,197 +0,0 @@
// Copyright (c) 2026, 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.
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';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(ConstructorAlreadyCompleteTest);
});
}
@reflectiveTest
class ConstructorAlreadyCompleteTest extends PubPackageResolutionTest {
test_secondary_factory_completedBy_body() async {
await assertErrorsInCode(
r'''
class A {
A._();
factory A() => A._();
augment factory A() => A._();
}
''',
[
error(
diag.constructorAlreadyComplete,
45,
7,
contextMessages: [message(testFile, 29, 1)],
),
],
);
}
test_secondary_factory_completedBy_redirection() async {
await assertErrorsInCode(
r'''
class A {
A._();
factory A() = A._;
augment factory A() => A._();
}
''',
[
error(
diag.constructorAlreadyComplete,
42,
7,
contextMessages: [message(testFile, 29, 1)],
),
],
);
}
test_secondary_factory_introductory_noBody() async {
await assertNoErrorsInCode(r'''
class A {
A._();
factory A();
augment factory A() => A._();
}
''');
}
test_secondary_generative_completedBy_assertInitializer() async {
await assertErrorsInCode(
r'''
class A {
A() : assert(true);
augment A() {}
}
''',
[
error(
diag.constructorAlreadyComplete,
34,
7,
contextMessages: [message(testFile, 12, 1)],
),
],
);
}
test_secondary_generative_completedBy_augmentation() async {
await assertErrorsInCode(
r'''
class A {
A();
augment A() {}
augment A() {}
}
''',
[
error(
diag.constructorAlreadyComplete,
36,
7,
contextMessages: [message(testFile, 27, 1)],
),
],
);
}
test_secondary_generative_completedBy_body() async {
await assertErrorsInCode(
r'''
class A {
A() {}
augment A() {}
}
''',
[
error(
diag.constructorAlreadyComplete,
21,
7,
contextMessages: [message(testFile, 12, 1)],
),
],
);
}
test_secondary_generative_completedBy_fieldFormalParameter() async {
await assertErrorsInCode(
r'''
class A {
final int x;
A(this.x);
augment A() {}
}
''',
[
error(
diag.constructorAlreadyComplete,
40,
7,
contextMessages: [message(testFile, 27, 1)],
),
],
);
}
test_secondary_generative_completedBy_fieldInitializer() async {
await assertErrorsInCode(
r'''
class A {
final int x;
A() : x = 0;
augment A() {}
}
''',
[
error(
diag.constructorAlreadyComplete,
42,
7,
contextMessages: [message(testFile, 27, 1)],
),
],
);
}
@FailingTest() // TODO(scheglov): implement augmentation
test_secondary_generative_completedBy_superFormalParameter() async {
await assertErrorsInCode(
r'''
class A {
A(int x);
}
class B extends A {
B(super.x);
augment B(int x) {}
}
''',
[
error(
diag.constructorAlreadyComplete,
60,
7,
contextMessages: [message(testFile, 46, 1)],
),
],
);
}
test_secondary_generative_introductory_noBody() async {
await assertNoErrorsInCode(r'''
class A {
A();
augment A() {}
}
''');
}
}
@@ -6,10 +6,12 @@ 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(ConstructorBodyTest);
defineReflectiveTests(UpdateNodeTextExpectations);
});
}
@@ -340,6 +342,182 @@ class C {
''');
}
test_class_secondaryConstructor_factory_factoryHead_named_augmentation_hasBody() async {
await assertNoErrorsInCode(r'''
class A {
factory named();
augment factory named() => throw 0;
}
''');
}
test_class_secondaryConstructor_factory_factoryHead_named_augmentation_noBody() async {
await resolveTestCodeWithDiagnostics(r'''
class A {
factory named();
//^^^^^^^^^^^^^
// [diag.factoryNotCompleteAfterAugmentations] The factory constructor 'named' must have a body or redirection after all augmentations are applied.
augment factory named();
}
''');
}
test_class_secondaryConstructor_factory_factoryHead_named_external_noBody() async {
await assertNoErrorsInCode(r'''
class A {
external factory named();
}
''');
}
test_class_secondaryConstructor_factory_factoryHead_named_hasBody() async {
await assertNoErrorsInCode(r'''
class A {
factory named() => throw 0;
}
''');
}
test_class_secondaryConstructor_factory_factoryHead_named_noBody() async {
await resolveTestCodeWithDiagnostics(r'''
class A {
factory named();
//^^^^^^^^^^^^^
// [diag.factoryWithoutBody] A non-redirecting 'factory' constructor must have a body.
}
''');
}
test_class_secondaryConstructor_factory_factoryHead_unnamed_augmentation_noBody() async {
await resolveTestCodeWithDiagnostics(r'''
class A {
factory ();
//^^^^^^^
// [diag.factoryNotCompleteAfterAugmentations] The factory constructor 'new' must have a body or redirection after all augmentations are applied.
augment factory ();
}
''');
}
test_class_secondaryConstructor_factory_factoryHead_unnamed_noBody() async {
await resolveTestCodeWithDiagnostics(r'''
class A {
factory ();
//^^^^^^^
// [diag.factoryWithoutBody] A non-redirecting 'factory' constructor must have a body.
}
''');
}
test_class_secondaryConstructor_factory_typeName_named_augmentation_noBody() async {
await resolveTestCodeWithDiagnostics(r'''
class A {
factory A.named();
// ^^^^^^^
// [diag.factoryNotCompleteAfterAugmentations] The factory constructor 'named' must have a body or redirection after all augmentations are applied.
augment factory A.named();
}
''');
}
test_class_secondaryConstructor_factory_typeName_named_external_noBody_language305() async {
await assertNoErrorsInCode(r'''
// @dart = 3.5
class A {
external factory A.named();
}
''');
}
test_class_secondaryConstructor_factory_typeName_named_hasBody_language305() async {
await assertNoErrorsInCode(r'''
// @dart = 3.5
class A {
factory A.named() => throw 0;
}
''');
}
test_class_secondaryConstructor_factory_typeName_named_noBody() async {
await resolveTestCodeWithDiagnostics(r'''
class A {
factory A.named();
// ^^^^^^^
// [diag.factoryWithoutBody] A non-redirecting 'factory' constructor must have a body.
}
''');
}
test_class_secondaryConstructor_factory_typeName_named_noBody_language305() async {
await resolveTestCodeWithDiagnostics(r'''
// @dart = 3.5
class A {
factory A.named();
// ^
// [diag.missingFunctionBody] A function body must be provided.
}
''');
}
test_class_secondaryConstructor_factory_typeName_unnamed_augmentation_hasBody() async {
await assertNoErrorsInCode(r'''
class A {
A._();
factory A();
augment factory A() => A._();
}
''');
}
test_class_secondaryConstructor_factory_typeName_unnamed_hasBody_augmentation_hasBody() async {
await resolveTestCodeWithDiagnostics(r'''
class A {
A._();
factory A() => A._();
// ^
// [context 1] The complete declaration is here.
augment factory A() => A._();
//^^^^^^^
// [diag.constructorAlreadyComplete][context 1] The augmentation can't provide a body, initializers, or initializing formal or super formal parameters because the constructor is already complete.
}
''');
}
test_class_secondaryConstructor_factory_typeName_unnamed_noBody() async {
await resolveTestCodeWithDiagnostics(r'''
class A {
factory A();
// ^
// [diag.factoryWithoutBody] A non-redirecting 'factory' constructor must have a body.
}
''');
}
test_class_secondaryConstructor_factory_typeName_unnamed_noBody_language305() async {
await resolveTestCodeWithDiagnostics(r'''
// @dart = 3.5
class A {
factory A();
// ^
// [diag.missingFunctionBody] A function body must be provided.
}
''');
}
test_class_secondaryConstructor_factory_typeName_unnamed_redirecting_augmentation_hasBody() async {
await resolveTestCodeWithDiagnostics(r'''
class A {
A._();
factory A() = A._;
// ^
// [context 1] The complete declaration is here.
augment factory A() => A._();
//^^^^^^^
// [diag.constructorAlreadyComplete][context 1] The augmentation can't provide a body, initializers, or initializing formal or super formal parameters because the constructor is already complete.
}
''');
}
test_class_secondaryConstructor_generative_external_blockBody() async {
await resolveTestCodeWithDiagnostics(r'''
class C {
@@ -372,6 +550,100 @@ class C {
''');
}
test_class_secondaryConstructor_generative_unnamed_assertInitializer_augmentation_hasBody() async {
await resolveTestCodeWithDiagnostics(r'''
class A {
A() : assert(true);
//^
// [context 1] The complete declaration is here.
augment A() {}
//^^^^^^^
// [diag.constructorAlreadyComplete][context 1] The augmentation can't provide a body, initializers, or initializing formal or super formal parameters because the constructor is already complete.
}
''');
}
test_class_secondaryConstructor_generative_unnamed_augmentation_hasBody() async {
await assertNoErrorsInCode(r'''
class A {
A();
augment A() {}
}
''');
}
test_class_secondaryConstructor_generative_unnamed_augmentation_hasBody_augmentation_hasBody() async {
await resolveTestCodeWithDiagnostics(r'''
class A {
A();
augment A() {}
// ^
// [context 1] The complete declaration is here.
augment A() {}
//^^^^^^^
// [diag.constructorAlreadyComplete][context 1] The augmentation can't provide a body, initializers, or initializing formal or super formal parameters because the constructor is already complete.
}
''');
}
test_class_secondaryConstructor_generative_unnamed_fieldFormalParameter_augmentation_hasBody() async {
await resolveTestCodeWithDiagnostics(r'''
class A {
final int x;
A(this.x);
//^
// [context 1] The complete declaration is here.
augment A() {}
//^^^^^^^
// [diag.constructorAlreadyComplete][context 1] The augmentation can't provide a body, initializers, or initializing formal or super formal parameters because the constructor is already complete.
}
''');
}
test_class_secondaryConstructor_generative_unnamed_fieldInitializer_augmentation_hasBody() async {
await resolveTestCodeWithDiagnostics(r'''
class A {
final int x;
A() : x = 0;
//^
// [context 1] The complete declaration is here.
augment A() {}
//^^^^^^^
// [diag.constructorAlreadyComplete][context 1] The augmentation can't provide a body, initializers, or initializing formal or super formal parameters because the constructor is already complete.
}
''');
}
test_class_secondaryConstructor_generative_unnamed_hasBody_augmentation_hasBody() async {
await resolveTestCodeWithDiagnostics(r'''
class A {
A() {}
//^
// [context 1] The complete declaration is here.
augment A() {}
//^^^^^^^
// [diag.constructorAlreadyComplete][context 1] The augmentation can't provide a body, initializers, or initializing formal or super formal parameters because the constructor is already complete.
}
''');
}
@SkippedTest() // TODO(scheglov): implement augmentation
test_class_secondaryConstructor_generative_unnamed_superFormalParameter_augmentation_hasBody() async {
await resolveTestCodeWithDiagnostics(r'''
class A {
A(int x);
}
class B extends A {
B(super.x);
//^
// [context 1] The complete declaration is here.
augment B(int x) {}
//^^^^^^^
// [diag.constructorAlreadyComplete][context 1] The augmentation can't provide a body, initializers, or initializing formal or super formal parameters because the constructor is already complete.
}
''');
}
test_enum_primaryConstructor_const_blockBody() async {
await resolveTestCodeWithDiagnostics(r'''
enum const E() {
File diff suppressed because it is too large Load Diff
@@ -1,62 +0,0 @@
// Copyright (c) 2019, 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.
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../dart/resolution/context_collection_resolution.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(ExtensionDeclaresAbstractMethodTest);
});
}
@reflectiveTest
class ExtensionDeclaresAbstractMethodTest extends PubPackageResolutionTest {
test_getter() async {
await resolveTestCodeWithDiagnostics(r'''
extension E on String {
bool get isPalindrome;
// ^^^^^^^^^^^^
// [diag.extensionDeclaresAbstractMember] Extensions can't declare abstract members.
}
''');
}
test_method() async {
await resolveTestCodeWithDiagnostics(r'''
extension E on String {
String reversed();
// ^^^^^^^^
// [diag.extensionDeclaresAbstractMember] Extensions can't declare abstract members.
}
''');
}
test_none() async {
await resolveTestCodeWithDiagnostics(r'''
extension E on String {}
''');
}
test_operator() async {
await resolveTestCodeWithDiagnostics(r'''
extension E on String {
String operator -(String otherString);
// ^
// [diag.extensionDeclaresAbstractMember] Extensions can't declare abstract members.
}
''');
}
test_setter() async {
await resolveTestCodeWithDiagnostics(r'''
extension E on String {
set length(int newLength);
// ^^^^^^
// [diag.extensionDeclaresAbstractMember] Extensions can't declare abstract members.
}
''');
}
}
@@ -1,127 +0,0 @@
// Copyright (c) 2023, 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.
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../dart/resolution/context_collection_resolution.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(ExtensionTypeWithAbstractMemberTest);
});
}
@reflectiveTest
class ExtensionTypeWithAbstractMemberTest extends PubPackageResolutionTest {
test_getter() async {
await resolveTestCodeWithDiagnostics(r'''
extension type A(int it) {
int get foo;
//^^^^^^^^^^^^
// [diag.extensionTypeWithAbstractMember] 'foo' must have a method body because 'A' is an extension type.
}
''');
}
test_getter_external() async {
await resolveTestCodeWithDiagnostics(r'''
extension type A(int it) {
external int get foo;
}
''');
}
test_getter_static() async {
await resolveTestCodeWithDiagnostics(r'''
extension type A(int it) {
static int get foo;
}
''');
}
test_getter_static_language305() async {
await resolveTestCodeWithDiagnostics(r'''
// @dart = 3.5
extension type A(int it) {
static int get foo;
// ^
// [diag.missingFunctionBody] A function body must be provided.
}
''');
}
test_method() async {
await resolveTestCodeWithDiagnostics(r'''
extension type A(int it) {
void foo();
//^^^^^^^^^^^
// [diag.extensionTypeWithAbstractMember] 'foo' must have a method body because 'A' is an extension type.
}
''');
}
test_method_external() async {
await resolveTestCodeWithDiagnostics(r'''
extension type A(int it) {
external void foo();
}
''');
}
test_method_static() async {
await resolveTestCodeWithDiagnostics(r'''
extension type A(int it) {
static void foo();
}
''');
}
test_method_static_language305() async {
await resolveTestCodeWithDiagnostics(r'''
// @dart = 3.5
extension type A(int it) {
static void foo();
// ^
// [diag.missingFunctionBody] A function body must be provided.
}
''');
}
test_setter() async {
await resolveTestCodeWithDiagnostics(r'''
extension type A(int it) {
set foo(int _);
//^^^^^^^^^^^^^^^
// [diag.extensionTypeWithAbstractMember] 'foo' must have a method body because 'A' is an extension type.
}
''');
}
test_setter_external() async {
await resolveTestCodeWithDiagnostics(r'''
extension type A(int it) {
external set foo(int _);
}
''');
}
test_setter_static() async {
await resolveTestCodeWithDiagnostics(r'''
extension type A(int it) {
static set foo(int _);
}
''');
}
test_setter_static_language305() async {
await resolveTestCodeWithDiagnostics(r'''
// @dart = 3.5
extension type A(int it) {
static set foo(int _);
// ^
// [diag.missingFunctionBody] A function body must be provided.
}
''');
}
}
@@ -1,96 +0,0 @@
// Copyright (c) 2026, 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.
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../dart/resolution/context_collection_resolution.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(ExternalMethodWithBodyTest);
});
}
@reflectiveTest
class ExternalMethodWithBodyTest extends PubPackageResolutionTest {
test_class_getter_external_blockBody() async {
await resolveTestCodeWithDiagnostics(r'''
class C {
external int get foo {}
// ^^^
// [diag.bodyMightCompleteNormally] The body might complete normally, causing 'null' to be returned, but the return type, 'int', is a potentially non-nullable type.
// ^
// [diag.externalMethodWithBody] An external or native method can't have a body.
}
''');
}
test_class_getter_external_expressionBody() async {
await resolveTestCodeWithDiagnostics(r'''
class C {
external int get foo => 0;
// ^^
// [diag.externalMethodWithBody] An external or native method can't have a body.
}
''');
}
test_class_method_external_blockBody() async {
await resolveTestCodeWithDiagnostics(r'''
class C {
external void foo() {}
// ^
// [diag.externalMethodWithBody] An external or native method can't have a body.
}
''');
}
test_class_method_external_expressionBody() async {
await resolveTestCodeWithDiagnostics(r'''
class C {
external void foo() => null;
// ^^
// [diag.externalMethodWithBody] An external or native method can't have a body.
}
''');
}
test_class_operator_external_blockBody() async {
await resolveTestCodeWithDiagnostics(r'''
class C {
external int operator +(int other) {}
// ^
// [diag.bodyMightCompleteNormally] The body might complete normally, causing 'null' to be returned, but the return type, 'int', is a potentially non-nullable type.
// ^
// [diag.externalMethodWithBody] An external or native method can't have a body.
}
''');
}
test_class_setter_external_blockBody() async {
await resolveTestCodeWithDiagnostics(r'''
class C {
external void set foo(int v) {}
// ^
// [diag.externalMethodWithBody] An external or native method can't have a body.
}
''');
}
test_topLevelFunction_external_blockBody() async {
await resolveTestCodeWithDiagnostics(r'''
external void foo() {}
// ^
// [diag.externalMethodWithBody] An external or native method can't have a body.
''');
}
test_topLevelFunction_external_expressionBody() async {
await resolveTestCodeWithDiagnostics(r'''
external void foo() => null;
// ^^
// [diag.externalMethodWithBody] An external or native method can't have a body.
''');
}
}
@@ -1,219 +0,0 @@
// Copyright (c) 2026, 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.
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';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(FunctionAlreadyCompleteTest);
});
}
@reflectiveTest
class FunctionAlreadyCompleteTest extends PubPackageResolutionTest {
test_class_instanceGetter() async {
await assertErrorsInCode(
r'''
class A {
int get foo => 0;
augment int get foo => 1;
}
''',
[
error(
diag.functionAlreadyComplete,
32,
7,
contextMessages: [message(testFile, 20, 3)],
),
],
);
}
test_class_instanceMethod() async {
await assertErrorsInCode(
r'''
class A {
void foo() {}
augment void foo() {}
}
''',
[
error(
diag.functionAlreadyComplete,
28,
7,
contextMessages: [message(testFile, 17, 3)],
),
],
);
}
test_class_instanceMethod_augmentation_noBody() async {
await assertNoErrorsInCode(r'''
class A {
void foo() {}
augment void foo();
}
''');
}
test_class_instanceMethod_introductory_noBody() async {
await assertNoErrorsInCode(r'''
abstract class A {
void foo();
augment void foo() {}
}
''');
}
test_class_instanceSetter() async {
await assertErrorsInCode(
r'''
class A {
set foo(int _) {}
augment set foo(int _) {}
}
''',
[
error(
diag.functionAlreadyComplete,
32,
7,
contextMessages: [message(testFile, 16, 3)],
),
],
);
}
test_class_operator() async {
await assertErrorsInCode(
r'''
class A {
A operator +(A _) => this;
augment A operator +(A _) => this;
}
''',
[
error(
diag.functionAlreadyComplete,
41,
7,
contextMessages: [message(testFile, 23, 1)],
),
],
);
}
test_class_staticGetter() async {
await assertErrorsInCode(
r'''
class A {
static int get foo => 0;
augment static int get foo => 1;
}
''',
[
error(
diag.functionAlreadyComplete,
39,
7,
contextMessages: [message(testFile, 27, 3)],
),
],
);
}
test_class_staticMethod() async {
await assertErrorsInCode(
r'''
class A {
static void foo() {}
augment static void foo() {}
}
''',
[
error(
diag.functionAlreadyComplete,
35,
7,
contextMessages: [message(testFile, 24, 3)],
),
],
);
}
test_class_staticSetter() async {
await assertErrorsInCode(
r'''
class A {
static set foo(int _) {}
augment static set foo(int _) {}
}
''',
[
error(
diag.functionAlreadyComplete,
39,
7,
contextMessages: [message(testFile, 23, 3)],
),
],
);
}
test_topLevelFunction() async {
await assertErrorsInCode(
r'''
void foo() {}
augment void foo() {}
''',
[
error(
diag.functionAlreadyComplete,
14,
7,
contextMessages: [message(testFile, 5, 3)],
),
],
);
}
test_topLevelGetter() async {
await assertErrorsInCode(
r'''
int get foo => 0;
augment int get foo => 1;
''',
[
error(
diag.functionAlreadyComplete,
18,
7,
contextMessages: [message(testFile, 8, 3)],
),
],
);
}
test_topLevelSetter() async {
await assertErrorsInCode(
r'''
set foo(int _) {}
augment set foo(int _) {}
''',
[
error(
diag.functionAlreadyComplete,
18,
7,
contextMessages: [message(testFile, 4, 3)],
),
],
);
}
}
@@ -170,7 +170,6 @@ import 'constant_pattern_never_matches_value_type_test.dart'
as constant_pattern_never_matches_value_type;
import 'constant_pattern_with_non_constant_expression_test.dart'
as constant_pattern_with_non_constant_expression;
import 'constructor_already_complete_test.dart' as constructor_already_complete;
import 'constructor_body_test.dart' as constructor_body;
import 'continue_label_invalid_test.dart' as continue_label_invalid;
import 'could_not_infer_test.dart' as could_not_infer;
@@ -259,6 +258,7 @@ import 'equal_elements_in_set_test.dart' as equal_elements_in_set;
import 'equal_keys_in_const_map_test.dart' as equal_keys_in_const_map;
import 'equal_keys_in_map_pattern_test.dart' as equal_keys_in_map_pattern;
import 'equal_keys_in_map_test.dart' as equal_keys_in_map;
import 'executable_body_test.dart' as executable_body;
import 'expected_one_list_pattern_type_arguments_test.dart'
as expected_one_list_pattern_type_arguments;
import 'expected_one_list_type_arguments_test.dart'
@@ -282,8 +282,6 @@ import 'extends_type_alias_expands_to_type_parameter_test.dart'
import 'extension_as_expression_test.dart' as extension_as_expression;
import 'extension_conflicting_static_and_instance_test.dart'
as extension_conflicting_static_and_instance;
import 'extension_declares_abstract_method_test.dart'
as extension_declares_abstract_method;
import 'extension_declares_constructor_test.dart'
as extension_declares_constructor;
import 'extension_declares_field_test.dart' as extension_declares_field;
@@ -319,11 +317,8 @@ import 'extension_type_representation_depends_on_itself_test.dart'
as extension_type_representation_depends_on_itself;
import 'extension_type_representation_type_bottom_test.dart'
as extension_type_representation_type_bottom;
import 'extension_type_with_abstract_member_test.dart'
as extension_type_with_abstract_member;
import 'external_field_constructor_initializer_test.dart'
as external_field_constructor_initializer;
import 'external_method_with_body_test.dart' as external_method_with_body;
import 'extra_annotation_on_struct_field_test.dart'
as extra_annotation_on_struct_field;
import 'extra_positional_arguments_test.dart' as extra_positional_arguments;
@@ -357,7 +352,6 @@ import 'for_in_of_invalid_element_type_test.dart'
as for_in_of_invalid_element_type;
import 'for_in_of_invalid_type_test.dart' as for_in_of_invalid_type;
import 'for_in_with_const_variable_test.dart' as for_in_with_const_variable;
import 'function_already_complete_test.dart' as function_already_complete;
import 'function_typed_parameter_var_test.dart' as function_typed_parameter_var;
import 'generic_function_type_cannot_be_bound_test.dart'
as generic_function_type_cannot_be_bound;
@@ -1071,7 +1065,6 @@ main() {
const_with_undefined_constructor.main();
constant_pattern_never_matches_value_type.main();
constant_pattern_with_non_constant_expression.main();
constructor_already_complete.main();
constructor_body.main();
continue_label_invalid.main();
could_not_infer.main();
@@ -1135,6 +1128,7 @@ main() {
equal_keys_in_const_map.main();
equal_keys_in_map_pattern.main();
equal_keys_in_map.main();
executable_body.main();
expected_one_list_pattern_type_arguments.main();
expected_one_list_type_arguments.main();
expected_one_set_type_arguments.main();
@@ -1151,7 +1145,6 @@ main() {
extends_type_alias_expands_to_type_parameter.main();
extension_as_expression.main();
extension_conflicting_static_and_instance.main();
extension_declares_abstract_method.main();
extension_declares_constructor.main();
extension_declares_field.main();
extension_declares_member_of_object.main();
@@ -1170,9 +1163,7 @@ main() {
extension_type_inherited_member_conflict.main();
extension_type_representation_depends_on_itself.main();
extension_type_representation_type_bottom.main();
extension_type_with_abstract_member.main();
external_field_constructor_initializer.main();
external_method_with_body.main();
extra_annotation_on_struct_field.main();
extra_positional_arguments.main();
extra_size_annotation_carray.main();
@@ -1194,7 +1185,6 @@ main() {
for_in_of_invalid_element_type.main();
for_in_of_invalid_type.main();
for_in_with_const_variable.main();
function_already_complete.main();
function_typed_parameter_var.main();
generic_function_type_cannot_be_bound.main();
generic_struct_subclass.main();
@@ -1937,7 +1937,6 @@ extension E on String { typedef A = B Function(C, D);
error(diag.typedefInClass, 24, 7),
error(diag.missingConstFinalVarOrType, 32, 1),
error(diag.expectedToken, 36, 1),
error(diag.extensionDeclaresAbstractMember, 38, 8),
]);
var node = parseResult.findNode.unit;
assertParsedNodeText(node, r'''
@@ -710,6 +710,7 @@ C<int>;
// No lint
error(diag.missingFunctionParameters, 15, 1),
error(diag.duplicateDefinition, 15, 1),
error(diag.missingFunctionBody, 21, 1),
],
);
}