diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart index 3a2b021786a..d643def9f83 100644 --- a/pkg/analyzer/lib/error/error.dart +++ b/pkg/analyzer/lib/error/error.dart @@ -258,6 +258,7 @@ const List errorCodeValues = [ CompileTimeErrorCode.INVALID_MODIFIER_ON_CONSTRUCTOR, CompileTimeErrorCode.INVALID_MODIFIER_ON_SETTER, CompileTimeErrorCode.INVALID_OVERRIDE, + CompileTimeErrorCode.INVALID_REFERENCE_TO_GENERATIVE_ENUM_CONSTRUCTOR, CompileTimeErrorCode.INVALID_REFERENCE_TO_THIS, CompileTimeErrorCode.INVALID_SUPER_FORMAL_PARAMETER_LOCATION, CompileTimeErrorCode.INVALID_TYPE_ARGUMENT_IN_CONST_LIST, diff --git a/pkg/analyzer/lib/src/dart/element/extensions.dart b/pkg/analyzer/lib/src/dart/element/extensions.dart index 53dc02d1027..69fc60b9e80 100644 --- a/pkg/analyzer/lib/src/dart/element/extensions.dart +++ b/pkg/analyzer/lib/src/dart/element/extensions.dart @@ -14,6 +14,12 @@ extension ClassElementExtension on ClassElement { } } +extension ConstructorElementExtension on ConstructorElement { + bool get isGenerative { + return !isFactory; + } +} + extension ElementAnnotationExtensions on ElementAnnotation { static final Map _targetKindsByName = { for (final kind in TargetKind.values) kind.toString(): kind, diff --git a/pkg/analyzer/lib/src/error/codes.g.dart b/pkg/analyzer/lib/src/error/codes.g.dart index 866dcd0bbb2..9666c16afaa 100644 --- a/pkg/analyzer/lib/src/error/codes.g.dart +++ b/pkg/analyzer/lib/src/error/codes.g.dart @@ -6568,6 +6568,7 @@ class CompileTimeErrorCode extends AnalyzerErrorCode { // instantiated: // // ```dart + // // @dart = 2.16 // enum E {a} // // var e = [!E!](); @@ -6579,6 +6580,7 @@ class CompileTimeErrorCode extends AnalyzerErrorCode { // constants defined in the enum: // // ```dart + // // @dart = 2.16 // enum E {a} // // var e = E.a; @@ -7499,6 +7501,13 @@ class CompileTimeErrorCode extends AnalyzerErrorCode { hasPublishedDocs: true, ); + static const CompileTimeErrorCode + INVALID_REFERENCE_TO_GENERATIVE_ENUM_CONSTRUCTOR = CompileTimeErrorCode( + 'INVALID_REFERENCE_TO_GENERATIVE_ENUM_CONSTRUCTOR', + "Generative enum constructors can only be used as targets of redirection.", + correctionMessage: "Try using a factory constructor, or an enum constant.", + ); + /** * No parameters. */ diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart index 8b4fd624a60..6d17094d676 100644 --- a/pkg/analyzer/lib/src/generated/error_verifier.dart +++ b/pkg/analyzer/lib/src/generated/error_verifier.dart @@ -22,6 +22,7 @@ import 'package:analyzer/src/dart/ast/ast.dart'; import 'package:analyzer/src/dart/ast/extensions.dart'; import 'package:analyzer/src/dart/element/class_hierarchy.dart'; import 'package:analyzer/src/dart/element/element.dart'; +import 'package:analyzer/src/dart/element/extensions.dart'; import 'package:analyzer/src/dart/element/inheritance_manager3.dart'; import 'package:analyzer/src/dart/element/type.dart'; import 'package:analyzer/src/dart/element/type_provider.dart'; @@ -534,6 +535,7 @@ class ErrorVerifier extends RecursiveAstVisitor @override void visitConstructorReference(ConstructorReference node) { _typeArgumentsVerifier.checkConstructorReference(node); + _checkForInvalidGenerativeConstructorReference(node.constructorName); } @override @@ -842,7 +844,7 @@ class ErrorVerifier extends RecursiveAstVisitor DartType type = namedType.typeOrThrow; if (type is InterfaceType) { _checkForConstOrNewWithAbstractClass(node, namedType, type); - _checkForConstOrNewWithEnum(node, namedType, type); + _checkForInvalidGenerativeConstructorReference(constructorName); _checkForConstOrNewWithMixin(node, namedType, type); _requiredParametersVerifier.visitInstanceCreationExpression(node); if (node.isConst) { @@ -2149,21 +2151,6 @@ class ErrorVerifier extends RecursiveAstVisitor } } - /// Verify that the given instance creation [expression] is not being invoked - /// on an enum. The [namedType] is the [NamedType] of the [ConstructorName] from - /// the [InstanceCreationExpression], this is the AST node that the error is - /// attached to. The [type] is the type being constructed with this - /// [InstanceCreationExpression]. - /// - /// See [CompileTimeErrorCode.INSTANTIATE_ENUM]. - void _checkForConstOrNewWithEnum(InstanceCreationExpression expression, - NamedType namedType, InterfaceType type) { - if (type.element.isEnum) { - errorReporter.reportErrorForNode( - CompileTimeErrorCode.INSTANTIATE_ENUM, namedType); - } - } - /// Verify that the given [expression] is not a mixin instantiation. void _checkForConstOrNewWithMixin(InstanceCreationExpression expression, NamedType namedType, InterfaceType type) { @@ -2902,6 +2889,25 @@ class ErrorVerifier extends RecursiveAstVisitor } } + void _checkForInvalidGenerativeConstructorReference(ConstructorName node) { + var constructorElement = node.staticElement; + if (constructorElement != null && + constructorElement.isGenerative && + constructorElement.enclosingElement.isEnum) { + if (_currentLibrary.featureSet.isEnabled(Feature.enhanced_enums)) { + errorReporter.reportErrorForNode( + CompileTimeErrorCode.INVALID_REFERENCE_TO_GENERATIVE_ENUM_CONSTRUCTOR, + node, + ); + } else { + errorReporter.reportErrorForNode( + CompileTimeErrorCode.INSTANTIATE_ENUM, + node.type2, + ); + } + } + } + /// Verify that if the given [identifier] is part of a constructor /// initializer, then it does not implicitly reference 'this' expression. /// diff --git a/pkg/analyzer/messages.yaml b/pkg/analyzer/messages.yaml index 3b88dc500bc..8fa7b1ae5fa 100644 --- a/pkg/analyzer/messages.yaml +++ b/pkg/analyzer/messages.yaml @@ -5713,6 +5713,7 @@ CompileTimeErrorCode: instantiated: ```dart + // @dart = 2.16 enum E {a} var e = [!E!](); @@ -5724,6 +5725,7 @@ CompileTimeErrorCode: constants defined in the enum: ```dart + // @dart = 2.16 enum E {a} var e = E.a; @@ -6504,6 +6506,9 @@ CompileTimeErrorCode: void m2(String s) {} } ``` + INVALID_REFERENCE_TO_GENERATIVE_ENUM_CONSTRUCTOR: + problemMessage: Generative enum constructors can only be used as targets of redirection. + correctionMessage: Try using a factory constructor, or an enum constant. INVALID_REFERENCE_TO_THIS: problemMessage: "Invalid reference to 'this' expression." hasPublishedDocs: true diff --git a/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart b/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart index 3f5a183b185..9f2e22741c3 100644 --- a/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart +++ b/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart @@ -440,6 +440,11 @@ mixin WithoutConstructorTearoffsMixin on PubPackageResolutionTest { String? get testPackageLanguageVersion => '2.14'; } +mixin WithoutEnhancedEnumsMixin on PubPackageResolutionTest { + @override + String? get testPackageLanguageVersion => '2.16'; +} + mixin WithoutNullSafetyMixin on PubPackageResolutionTest { @override String? get testPackageLanguageVersion => '2.9'; diff --git a/pkg/analyzer/test/src/diagnostics/instantiate_enum_test.dart b/pkg/analyzer/test/src/diagnostics/instantiate_enum_test.dart index 0fd804c63cb..2d6b19eecb0 100644 --- a/pkg/analyzer/test/src/diagnostics/instantiate_enum_test.dart +++ b/pkg/analyzer/test/src/diagnostics/instantiate_enum_test.dart @@ -14,7 +14,8 @@ main() { } @reflectiveTest -class InstantiateEnumTest extends PubPackageResolutionTest { +class InstantiateEnumTest extends PubPackageResolutionTest + with WithoutEnhancedEnumsMixin { test_const() async { await assertErrorsInCode(r''' enum E { ONE } diff --git a/pkg/analyzer/test/src/diagnostics/invalid_reference_to_generative_enum_constructor_test.dart b/pkg/analyzer/test/src/diagnostics/invalid_reference_to_generative_enum_constructor_test.dart new file mode 100644 index 00000000000..913b93f507f --- /dev/null +++ b/pkg/analyzer/test/src/diagnostics/invalid_reference_to_generative_enum_constructor_test.dart @@ -0,0 +1,177 @@ +// Copyright (c) 2022, 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/error/codes.dart'; +import 'package:test_reflective_loader/test_reflective_loader.dart'; + +import '../dart/resolution/context_collection_resolution.dart'; + +main() { + defineReflectiveSuite(() { + defineReflectiveTests(InvalidReferenceToGenerativeEnumConstructorTest); + }); +} + +@reflectiveTest +class InvalidReferenceToGenerativeEnumConstructorTest + extends PubPackageResolutionTest { + test_factory_named() async { + await assertNoErrorsInCode(''' +enum E { + v(); + + factory E.named() => v; +} + +void f() { + E.named; + E.named(); +} +'''); + } + + test_factory_unnamed() async { + await assertNoErrorsInCode(''' +enum E { + v.named(); + + const E.named(); + factory E() => v; +} + +void f() { + E.new; + E(); +} +'''); + } + + test_generative_named_constructorReference() async { + await assertErrorsInCode(''' +enum E { + v.named(); + + const E.named(); +} + +void f() { + E.named; +} +''', [ + error( + CompileTimeErrorCode.INVALID_REFERENCE_TO_GENERATIVE_ENUM_CONSTRUCTOR, + 58, + 7), + ]); + } + + test_generative_named_instanceCreation_implicitNew() async { + await assertErrorsInCode(''' +enum E { + v.named(); + + const E.named(); +} + +void f() { + E.named(); +} +''', [ + error( + CompileTimeErrorCode.INVALID_REFERENCE_TO_GENERATIVE_ENUM_CONSTRUCTOR, + 58, + 7), + ]); + } + + test_generative_named_redirectingConstructorInvocation() async { + await assertNoErrorsInCode(''' +enum E { + v; + + const E() : this.named(); + const E.named(); +} +'''); + } + + test_generative_unnamed_constructorReference() async { + await assertErrorsInCode(''' +enum E { + v +} + +void f() { + E.new; +} +''', [ + error( + CompileTimeErrorCode.INVALID_REFERENCE_TO_GENERATIVE_ENUM_CONSTRUCTOR, + 29, + 5), + ]); + } + + test_generative_unnamed_instanceCreation_explicitConst() async { + await assertErrorsInCode(''' +enum E { + v +} + +void f() { + const E(); +} +''', [ + error( + CompileTimeErrorCode.INVALID_REFERENCE_TO_GENERATIVE_ENUM_CONSTRUCTOR, + 35, + 1), + ]); + } + + test_generative_unnamed_instanceCreation_explicitNew() async { + await assertErrorsInCode(''' +enum E { + v +} + +void f() { + new E(); +} +''', [ + error( + CompileTimeErrorCode.INVALID_REFERENCE_TO_GENERATIVE_ENUM_CONSTRUCTOR, + 33, + 1), + ]); + } + + test_generative_unnamed_instanceCreation_implicitNew() async { + await assertErrorsInCode(''' +enum E { + v +} + +void f() { + E(); +} +''', [ + error( + CompileTimeErrorCode.INVALID_REFERENCE_TO_GENERATIVE_ENUM_CONSTRUCTOR, + 29, + 1), + ]); + } + + test_generative_unnamed_redirectingConstructorInvocation() async { + await assertNoErrorsInCode(''' +enum E { + v; + + const E(); + const E.named() : this(); +} +'''); + } +} diff --git a/pkg/analyzer/test/src/diagnostics/test_all.dart b/pkg/analyzer/test/src/diagnostics/test_all.dart index 83b1c99a869..32e5d071904 100644 --- a/pkg/analyzer/test/src/diagnostics/test_all.dart +++ b/pkg/analyzer/test/src/diagnostics/test_all.dart @@ -341,6 +341,8 @@ import 'invalid_override_different_default_values_positional_test.dart' import 'invalid_override_of_non_virtual_member_test.dart' as invalid_override_of_non_virtual_member; import 'invalid_override_test.dart' as invalid_override; +import 'invalid_reference_to_generative_enum_constructor_test.dart' + as invalid_reference_to_generative_enum_constructor; import 'invalid_reference_to_this_test.dart' as invalid_reference_to_this; import 'invalid_required_named_param_test.dart' as invalid_required_named_param; import 'invalid_required_optional_positional_param_test.dart' @@ -976,6 +978,7 @@ main() { invalid_override_different_default_values_positional.main(); invalid_override_of_non_virtual_member.main(); invalid_override.main(); + invalid_reference_to_generative_enum_constructor.main(); invalid_reference_to_this.main(); invalid_required_named_param.main(); invalid_required_optional_positional_param.main(); diff --git a/pkg/analyzer/tool/diagnostics/diagnostics.md b/pkg/analyzer/tool/diagnostics/diagnostics.md index c8454b0f1c1..76fd405267a 100644 --- a/pkg/analyzer/tool/diagnostics/diagnostics.md +++ b/pkg/analyzer/tool/diagnostics/diagnostics.md @@ -6616,6 +6616,7 @@ The following code produces this diagnostic because the enum `E` is being instantiated: {% prettify dart tag=pre+code %} +// @dart = 2.16 enum E {a} var e = [!E!](); @@ -6627,6 +6628,7 @@ If you intend to use an instance of the enum, then reference one of the constants defined in the enum: {% prettify dart tag=pre+code %} +// @dart = 2.16 enum E {a} var e = E.a;