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:
committed by
dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent
433b4fa3c8
commit
56706b715d
@@ -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: |-
|
||||
|
||||
+6
-2
@@ -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;
|
||||
|
||||
@@ -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),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user