diff --git a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml index f7e553b264b..bd71c1701b6 100644 --- a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml +++ b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml @@ -1055,6 +1055,8 @@ CompileTimeErrorCode.SPREAD_EXPRESSION_FROM_DEFERRED_LIBRARY: status: needsEvaluation CompileTimeErrorCode.STATIC_ACCESS_TO_INSTANCE_MEMBER: status: needsEvaluation +CompileTimeErrorCode.SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED: + status: needsEvaluation CompileTimeErrorCode.SUPER_FORMAL_PARAMETER_TYPE_IS_NOT_SUBTYPE_OF_ASSOCIATED: status: hasFix since: 2.17 @@ -2862,4 +2864,4 @@ WarningCode.UNUSED_RESULT: WarningCode.UNUSED_RESULT_WITH_MESSAGE: status: noFix notes: |- - There is no predictable common way in which a result _should_ be used. \ No newline at end of file + There is no predictable common way in which a result _should_ be used. diff --git a/pkg/analyzer/lib/error/listener.dart b/pkg/analyzer/lib/error/listener.dart index ee239796715..44e7701ea17 100644 --- a/pkg/analyzer/lib/error/listener.dart +++ b/pkg/analyzer/lib/error/listener.dart @@ -73,10 +73,10 @@ class ErrorReporter { /// Report an error with the given [errorCode] and [arguments]. The [element] /// is used to compute the location of the error. void reportErrorForElement(ErrorCode errorCode, Element element, - [List? arguments]) { + [List? arguments, List? messages]) { var nonSynthetic = element.nonSynthetic; - reportErrorForOffset( - errorCode, nonSynthetic.nameOffset, nonSynthetic.nameLength, arguments); + reportErrorForOffset(errorCode, nonSynthetic.nameOffset, + nonSynthetic.nameLength, arguments, messages); } /// Report a diagnostic with the given [code] and [arguments]. The diff --git a/pkg/analyzer/lib/src/error/base_or_final_type_verifier.dart b/pkg/analyzer/lib/src/error/base_or_final_type_verifier.dart new file mode 100644 index 00000000000..d56e5f500c2 --- /dev/null +++ b/pkg/analyzer/lib/src/error/base_or_final_type_verifier.dart @@ -0,0 +1,128 @@ +// 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:analyzer/dart/ast/token.dart'; +import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/dart/element/type.dart'; +import 'package:analyzer/diagnostic/diagnostic.dart'; +import 'package:analyzer/error/listener.dart'; +import 'package:analyzer/src/dart/element/element.dart'; +import 'package:analyzer/src/diagnostic/diagnostic.dart'; +import 'package:analyzer/src/error/codes.dart'; +import 'package:analyzer/src/utilities/extensions/collection.dart'; + +/// Helper for verifying that subelements of a base or final element must be +/// base, final, or sealed. +class BaseOrFinalTypeVerifier { + final LibraryElement _definingLibrary; + final ErrorReporter _errorReporter; + + /// Maps an element to a base or final superelement. + /// May be null if the type does not have one. + final Map + _elementToBaseOrFinalSuperElement = {}; + + BaseOrFinalTypeVerifier({ + required LibraryElement definingLibrary, + required ErrorReporter errorReporter, + }) : _definingLibrary = definingLibrary, + _errorReporter = errorReporter; + + /// Check to ensure the subelement of a base or final element must be base, + /// final, or sealed. Otherwise, an error is reported on that element. + /// + /// See [CompileTimeErrorCode. + /// SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED] + void checkElement(ClassOrMixinElementImpl element) { + if (_elementToBaseOrFinalSuperElement.containsKey(element)) { + // We've already visited this element. Don't check it again. + return; + } else { + _elementToBaseOrFinalSuperElement[element] = null; + } + + List supertypes = []; + supertypes.addIfNotNull(element.supertype); + supertypes.addAll(element.interfaces); + supertypes.addAll(element.mixins); + if (element is MixinElementImpl) { + supertypes.addAll(element.superclassConstraints); + } + + for (final supertype in supertypes) { + final supertypeElement = supertype.element; + if (supertypeElement is ClassOrMixinElementImpl) { + // Ensure that all superelements are properly cached in + // [_elementToBaseOrFinalSuperElement]. + checkElement(supertypeElement); + + // Return early if an error has been reported to prevent reporting + // multiple errors on one element. + if (_reportRestrictionError(element, supertypeElement)) { + return; + } + } + } + } + + /// Returns true if a element modifier restriction error has been reported. + /// + /// Reports an error based on the modifier of the superElement. + bool _reportRestrictionError( + ClassOrMixinElementImpl element, ClassOrMixinElementImpl superElement) { + final cachedBaseOrFinalSuperElement = + _elementToBaseOrFinalSuperElement[superElement]; + final hasCachedBaseOrFinalSuperElement = + cachedBaseOrFinalSuperElement != null; + ClassOrMixinElementImpl? baseOrFinalSuperElement; + + if (superElement.isBase || superElement.isFinal) { + // Prefer the direct base or final superelement. + baseOrFinalSuperElement = superElement; + } else if (hasCachedBaseOrFinalSuperElement) { + // There's a base or final element higher up in the class hierarchy. + // The superelement is a sealed element. + baseOrFinalSuperElement = cachedBaseOrFinalSuperElement; + } else { + // There are no restrictions on this element's modifiers. + return false; + } + + _elementToBaseOrFinalSuperElement[element] = baseOrFinalSuperElement; + + if (element.library != _definingLibrary) { + // Only report errors on elements within the current library. + return false; + } + if (!element.isBase && !element.isFinal && !element.isSealed) { + final contextMessage = [ + DiagnosticMessageImpl( + filePath: superElement.source.fullName, + length: superElement.nameLength, + message: "The type '${superElement.name}' is a subtype of " + "'${baseOrFinalSuperElement.name}', and " + "'${superElement.name}' is defined here.", + offset: superElement.nameOffset, + url: null, + ) + ]; + + _errorReporter.reportErrorForElement( + CompileTimeErrorCode + .SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED, + element, + [ + element.displayName, + baseOrFinalSuperElement.displayName, + baseOrFinalSuperElement.isBase + ? Keyword.BASE.lexeme + : Keyword.FINAL.lexeme + ], + hasCachedBaseOrFinalSuperElement ? contextMessage : null); + return true; + } + + return false; + } +} diff --git a/pkg/analyzer/lib/src/error/codes.g.dart b/pkg/analyzer/lib/src/error/codes.g.dart index d25eaf7b0b4..c1a12767860 100644 --- a/pkg/analyzer/lib/src/error/codes.g.dart +++ b/pkg/analyzer/lib/src/error/codes.g.dart @@ -4422,6 +4422,18 @@ class CompileTimeErrorCode extends AnalyzerErrorCode { hasPublishedDocs: true, ); + /// Parameters: + /// 0: the name of the subtype that is not 'base', 'final', or 'sealed' + /// 1: the name of the supertype which is 'base' or 'final' + /// 2: the modifier on the supertype + static const CompileTimeErrorCode + SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED = + CompileTimeErrorCode( + 'SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED', + "The type '{0}' must be 'base', 'final' or 'sealed' because the supertype " + "'{1}' is '{2}'.", + ); + /// Parameters: /// 0: the type of super-parameter /// 1: the type of associated super-constructor parameter diff --git a/pkg/analyzer/lib/src/error/error_code_values.g.dart b/pkg/analyzer/lib/src/error/error_code_values.g.dart index 4570032a1c3..caf09cae5c8 100644 --- a/pkg/analyzer/lib/src/error/error_code_values.g.dart +++ b/pkg/analyzer/lib/src/error/error_code_values.g.dart @@ -448,6 +448,7 @@ const List errorCodeValues = [ CompileTimeErrorCode.SHARED_DEFERRED_PREFIX, CompileTimeErrorCode.SPREAD_EXPRESSION_FROM_DEFERRED_LIBRARY, CompileTimeErrorCode.STATIC_ACCESS_TO_INSTANCE_MEMBER, + CompileTimeErrorCode.SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED, CompileTimeErrorCode.SUPER_FORMAL_PARAMETER_TYPE_IS_NOT_SUBTYPE_OF_ASSOCIATED, CompileTimeErrorCode.SUPER_FORMAL_PARAMETER_WITHOUT_ASSOCIATED_NAMED, CompileTimeErrorCode.SUPER_FORMAL_PARAMETER_WITHOUT_ASSOCIATED_POSITIONAL, diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart index 679f7bc3ac4..465310e0298 100644 --- a/pkg/analyzer/lib/src/generated/resolver.dart +++ b/pkg/analyzer/lib/src/generated/resolver.dart @@ -74,6 +74,7 @@ import 'package:analyzer/src/dart/resolver/typed_literal_resolver.dart'; import 'package:analyzer/src/dart/resolver/variable_declaration_resolver.dart'; import 'package:analyzer/src/dart/resolver/yield_statement_resolver.dart'; import 'package:analyzer/src/diagnostic/diagnostic.dart'; +import 'package:analyzer/src/error/base_or_final_type_verifier.dart'; import 'package:analyzer/src/error/bool_expression_verifier.dart'; import 'package:analyzer/src/error/codes.dart'; import 'package:analyzer/src/error/dead_code_verifier.dart'; @@ -204,6 +205,10 @@ class ResolverVisitor extends ThrowingAstVisitor final MigrationResolutionHooks? migrationResolutionHooks; + /// Helper for checking that subtypes of a base or final type must be base, + /// final, or sealed. + late final BaseOrFinalTypeVerifier baseOrFinalTypeVerifier; + /// Helper for checking expression that should have the `bool` type. late final BoolExpressionVerifier boolExpressionVerifier; @@ -374,6 +379,8 @@ class ResolverVisitor extends ThrowingAstVisitor errorReporter: errorReporter, resolver: this, ); + baseOrFinalTypeVerifier = BaseOrFinalTypeVerifier( + definingLibrary: definingLibrary, errorReporter: errorReporter); boolExpressionVerifier = BoolExpressionVerifier( resolver: this, errorReporter: errorReporter, @@ -2034,6 +2041,9 @@ class ResolverVisitor extends ThrowingAstVisitor } finally { enclosingClass = outerType; } + + baseOrFinalTypeVerifier + .checkElement(node.declaredElement as ClassOrMixinElementImpl); } @override @@ -2959,6 +2969,9 @@ class ResolverVisitor extends ThrowingAstVisitor } finally { enclosingClass = outerType; } + + baseOrFinalTypeVerifier + .checkElement(node.declaredElement as ClassOrMixinElementImpl); } @override diff --git a/pkg/analyzer/messages.yaml b/pkg/analyzer/messages.yaml index c1e7bc4fd99..1e306a95404 100644 --- a/pkg/analyzer/messages.yaml +++ b/pkg/analyzer/messages.yaml @@ -12890,6 +12890,13 @@ CompileTimeErrorCode: int f(C c) => c.b; ``` + SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED: + problemMessage: "The type '{0}' must be 'base', 'final' or 'sealed' because the supertype '{1}' is '{2}'." + comment: |- + Parameters: + 0: the name of the subtype that is not 'base', 'final', or 'sealed' + 1: the name of the supertype which is 'base' or 'final' + 2: the modifier on the supertype SUPER_FORMAL_PARAMETER_TYPE_IS_NOT_SUBTYPE_OF_ASSOCIATED: problemMessage: The type '{0}' of this parameter isn't a subtype of the type '{1}' of the associated super constructor parameter. correctionMessage: Try removing the explicit type annotation from the parameter. @@ -23644,4 +23651,4 @@ WarningCode: Parameters: 0: the name of the annotated method, property or function - 1: message details \ No newline at end of file + 1: message details diff --git a/pkg/analyzer/test/src/diagnostics/base_class_implemented_outside_of_library_test.dart b/pkg/analyzer/test/src/diagnostics/base_class_implemented_outside_of_library_test.dart index 339b6558ee9..615b25bcdf8 100644 --- a/pkg/analyzer/test/src/diagnostics/base_class_implemented_outside_of_library_test.dart +++ b/pkg/analyzer/test/src/diagnostics/base_class_implemented_outside_of_library_test.dart @@ -19,7 +19,7 @@ class BaseClassImplementedOutsideOfLibraryTest test_class_inside() async { await assertNoErrorsInCode(r''' base class Foo {} -class Bar implements Foo {} +base class Bar implements Foo {} '''); } @@ -30,9 +30,9 @@ base class Foo {} await assertErrorsInCode(r''' import 'foo.dart'; -class Bar implements Foo {} +base class Bar implements Foo {} ''', [ - error(CompileTimeErrorCode.BASE_CLASS_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 40, + error(CompileTimeErrorCode.BASE_CLASS_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 45, 3), ]); } @@ -45,9 +45,9 @@ typedef FooTypedef = Foo; await assertErrorsInCode(r''' import 'foo.dart'; -class Bar implements FooTypedef {} +base class Bar implements FooTypedef {} ''', [ - error(CompileTimeErrorCode.BASE_CLASS_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 40, + error(CompileTimeErrorCode.BASE_CLASS_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 45, 10), ]); } @@ -60,25 +60,13 @@ base class Foo {} await assertErrorsInCode(r''' import 'foo.dart'; typedef FooTypedef = Foo; -class Bar implements FooTypedef {} +base class Bar implements FooTypedef {} ''', [ - error(CompileTimeErrorCode.BASE_CLASS_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 66, + error(CompileTimeErrorCode.BASE_CLASS_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 71, 10), ]); } - test_class_subtypeOfBase_outside() async { - newFile('$testPackageLibPath/foo.dart', r''' -base class Foo {} -class Bar implements Foo {} -'''); - - await assertNoErrorsInCode(r''' -import 'foo.dart'; -class Bar2 implements Bar {} -'''); - } - test_enum_inside() async { await assertNoErrorsInCode(r''' base class Foo {} @@ -130,22 +118,10 @@ enum Bar implements FooTypedef { bar } ]); } - test_enum_subtypeOfBase_outside() async { - newFile('$testPackageLibPath/foo.dart', r''' -base class Foo {} -class Bar implements Foo {} -'''); - - await assertNoErrorsInCode(r''' -import 'foo.dart'; -enum Bar2 implements Bar { bar } -'''); - } - test_mixin_inside() async { await assertNoErrorsInCode(r''' base class Foo {} -mixin Bar implements Foo {} +base mixin Bar implements Foo {} '''); } @@ -156,9 +132,9 @@ base class Foo {} await assertErrorsInCode(r''' import 'foo.dart'; -mixin Bar implements Foo {} +base mixin Bar implements Foo {} ''', [ - error(CompileTimeErrorCode.BASE_CLASS_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 40, + error(CompileTimeErrorCode.BASE_CLASS_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 45, 3), ]); } @@ -171,9 +147,9 @@ typedef FooTypedef = Foo; await assertErrorsInCode(r''' import 'foo.dart'; -mixin Bar implements FooTypedef {} +base mixin Bar implements FooTypedef {} ''', [ - error(CompileTimeErrorCode.BASE_CLASS_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 40, + error(CompileTimeErrorCode.BASE_CLASS_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 45, 10), ]); } @@ -186,22 +162,10 @@ base class Foo {} await assertErrorsInCode(r''' import 'foo.dart'; typedef FooTypedef = Foo; -mixin Bar implements FooTypedef {} +base mixin Bar implements FooTypedef {} ''', [ - error(CompileTimeErrorCode.BASE_CLASS_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 66, + error(CompileTimeErrorCode.BASE_CLASS_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 71, 10), ]); } - - test_mixin_subtypeOfBase_outside() async { - newFile('$testPackageLibPath/foo.dart', r''' -base class Foo {} -class Bar implements Foo {} -'''); - - await assertNoErrorsInCode(r''' -import 'foo.dart'; -mixin Bar2 implements Bar {} -'''); - } } diff --git a/pkg/analyzer/test/src/diagnostics/base_mixin_implemented_outside_of_library_test.dart b/pkg/analyzer/test/src/diagnostics/base_mixin_implemented_outside_of_library_test.dart index b520cfba6c1..9bb72a3e1b7 100644 --- a/pkg/analyzer/test/src/diagnostics/base_mixin_implemented_outside_of_library_test.dart +++ b/pkg/analyzer/test/src/diagnostics/base_mixin_implemented_outside_of_library_test.dart @@ -19,7 +19,7 @@ class BaseMixinImplementedOutsideOfLibraryTest test_class_inside() async { await assertNoErrorsInCode(r''' base mixin Foo {} -class Bar implements Foo {} +base class Bar implements Foo {} '''); } @@ -30,9 +30,9 @@ base mixin Foo {} await assertErrorsInCode(r''' import 'foo.dart'; -class Bar implements Foo {} +base class Bar implements Foo {} ''', [ - error(CompileTimeErrorCode.BASE_MIXIN_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 40, + error(CompileTimeErrorCode.BASE_MIXIN_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 45, 3), ]); } @@ -45,9 +45,9 @@ typedef FooTypedef = Foo; await assertErrorsInCode(r''' import 'foo.dart'; -class Bar implements FooTypedef {} +base class Bar implements FooTypedef {} ''', [ - error(CompileTimeErrorCode.BASE_MIXIN_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 40, + error(CompileTimeErrorCode.BASE_MIXIN_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 45, 10), ]); } @@ -60,25 +60,13 @@ base mixin Foo {} await assertErrorsInCode(r''' import 'foo.dart'; typedef FooTypedef = Foo; -class Bar implements FooTypedef {} +base class Bar implements FooTypedef {} ''', [ - error(CompileTimeErrorCode.BASE_MIXIN_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 66, + error(CompileTimeErrorCode.BASE_MIXIN_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 71, 10), ]); } - test_class_subtypeOfBase_outside() async { - newFile('$testPackageLibPath/foo.dart', r''' -base mixin Foo {} -class Bar implements Foo {} -'''); - - await assertNoErrorsInCode(r''' -import 'foo.dart'; -class Bar2 implements Bar {} -'''); - } - test_enum_inside() async { await assertNoErrorsInCode(r''' base mixin Foo {} @@ -130,22 +118,10 @@ enum Bar implements FooTypedef { bar } ]); } - test_enum_subtypeOfBase_outside() async { - newFile('$testPackageLibPath/foo.dart', r''' -base mixin Foo {} -class Bar implements Foo {} -'''); - - await assertNoErrorsInCode(r''' -import 'foo.dart'; -enum Bar2 implements Bar { bar } -'''); - } - test_mixin_inside() async { await assertNoErrorsInCode(r''' base mixin Foo {} -mixin Bar implements Foo {} +base mixin Bar implements Foo {} '''); } @@ -156,9 +132,9 @@ base mixin Foo {} await assertErrorsInCode(r''' import 'foo.dart'; -mixin Bar implements Foo {} +base mixin Bar implements Foo {} ''', [ - error(CompileTimeErrorCode.BASE_MIXIN_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 40, + error(CompileTimeErrorCode.BASE_MIXIN_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 45, 3), ]); } @@ -171,9 +147,9 @@ typedef FooTypedef = Foo; await assertErrorsInCode(r''' import 'foo.dart'; -mixin Bar implements FooTypedef {} +base mixin Bar implements FooTypedef {} ''', [ - error(CompileTimeErrorCode.BASE_MIXIN_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 40, + error(CompileTimeErrorCode.BASE_MIXIN_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 45, 10), ]); } @@ -186,22 +162,10 @@ base mixin Foo {} await assertErrorsInCode(r''' import 'foo.dart'; typedef FooTypedef = Foo; -mixin Bar implements FooTypedef {} +base mixin Bar implements FooTypedef {} ''', [ - error(CompileTimeErrorCode.BASE_MIXIN_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 66, + error(CompileTimeErrorCode.BASE_MIXIN_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 71, 10), ]); } - - test_mixin_subtypeOfBase_outside() async { - newFile('$testPackageLibPath/foo.dart', r''' -base mixin Foo {} -class Bar implements Foo {} -'''); - - await assertNoErrorsInCode(r''' -import 'foo.dart'; -mixin Bar2 implements Bar {} -'''); - } } diff --git a/pkg/analyzer/test/src/diagnostics/final_class_extended_outside_of_library_test.dart b/pkg/analyzer/test/src/diagnostics/final_class_extended_outside_of_library_test.dart index 7aa32987af6..9ecd98eca8a 100644 --- a/pkg/analyzer/test/src/diagnostics/final_class_extended_outside_of_library_test.dart +++ b/pkg/analyzer/test/src/diagnostics/final_class_extended_outside_of_library_test.dart @@ -18,7 +18,7 @@ class FinalClassExtendedOutsideOfLibraryTest extends PubPackageResolutionTest { test_inside() async { await assertNoErrorsInCode(r''' final class Foo {} -class Bar extends Foo {} +final class Bar extends Foo {} '''); } @@ -29,10 +29,10 @@ final class Foo {} await assertErrorsInCode(r''' import 'foo.dart'; -class Bar extends Foo {} +final class Bar extends Foo {} ''', [ error( - CompileTimeErrorCode.FINAL_CLASS_EXTENDED_OUTSIDE_OF_LIBRARY, 37, 3), + CompileTimeErrorCode.FINAL_CLASS_EXTENDED_OUTSIDE_OF_LIBRARY, 43, 3), ]); } @@ -44,10 +44,10 @@ typedef FooTypedef = Foo; await assertErrorsInCode(r''' import 'foo.dart'; -class Bar extends FooTypedef {} +final class Bar extends FooTypedef {} ''', [ error( - CompileTimeErrorCode.FINAL_CLASS_EXTENDED_OUTSIDE_OF_LIBRARY, 37, 10), + CompileTimeErrorCode.FINAL_CLASS_EXTENDED_OUTSIDE_OF_LIBRARY, 43, 10), ]); } @@ -59,22 +59,10 @@ final class Foo {} await assertErrorsInCode(r''' import 'foo.dart'; typedef FooTypedef = Foo; -class Bar extends FooTypedef {} +final class Bar extends FooTypedef {} ''', [ error( - CompileTimeErrorCode.FINAL_CLASS_EXTENDED_OUTSIDE_OF_LIBRARY, 63, 10), + CompileTimeErrorCode.FINAL_CLASS_EXTENDED_OUTSIDE_OF_LIBRARY, 69, 10), ]); } - - test_subtypeOfBase_outside() async { - newFile('$testPackageLibPath/foo.dart', r''' -final class Foo {} -class Bar extends Foo {} -'''); - - await assertNoErrorsInCode(r''' -import 'foo.dart'; -class Bar2 extends Bar {} -'''); - } } diff --git a/pkg/analyzer/test/src/diagnostics/final_class_implemented_outside_of_library_test.dart b/pkg/analyzer/test/src/diagnostics/final_class_implemented_outside_of_library_test.dart index 0fda9fa566a..ab6393933e2 100644 --- a/pkg/analyzer/test/src/diagnostics/final_class_implemented_outside_of_library_test.dart +++ b/pkg/analyzer/test/src/diagnostics/final_class_implemented_outside_of_library_test.dart @@ -19,7 +19,7 @@ class FinalClassImplementedOutsideOfLibraryTest test_class_inside() async { await assertNoErrorsInCode(r''' final class Foo {} -class Bar implements Foo {} +final class Bar implements Foo {} '''); } @@ -30,9 +30,9 @@ final class Foo {} await assertErrorsInCode(r''' import 'foo.dart'; -class Bar implements Foo {} +final class Bar implements Foo {} ''', [ - error(CompileTimeErrorCode.FINAL_CLASS_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 40, + error(CompileTimeErrorCode.FINAL_CLASS_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 46, 3), ]); } @@ -45,9 +45,9 @@ typedef FooTypedef = Foo; await assertErrorsInCode(r''' import 'foo.dart'; -class Bar implements FooTypedef {} +final class Bar implements FooTypedef {} ''', [ - error(CompileTimeErrorCode.FINAL_CLASS_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 40, + error(CompileTimeErrorCode.FINAL_CLASS_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 46, 10), ]); } @@ -60,25 +60,13 @@ final class Foo {} await assertErrorsInCode(r''' import 'foo.dart'; typedef FooTypedef = Foo; -class Bar implements FooTypedef {} +final class Bar implements FooTypedef {} ''', [ - error(CompileTimeErrorCode.FINAL_CLASS_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 66, + error(CompileTimeErrorCode.FINAL_CLASS_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 72, 10), ]); } - test_class_subtypeOfFinal_outside() async { - newFile('$testPackageLibPath/foo.dart', r''' -final class Foo {} -class Bar implements Foo {} -'''); - - await assertNoErrorsInCode(r''' -import 'foo.dart'; -class Bar2 implements Bar {} -'''); - } - test_enum_inside() async { await assertNoErrorsInCode(r''' final class Foo {} @@ -145,7 +133,7 @@ enum Bar2 implements Bar { bar } test_mixin_inside() async { await assertNoErrorsInCode(r''' final class Foo {} -mixin Bar implements Foo {} +final mixin Bar implements Foo {} '''); } @@ -156,9 +144,9 @@ final class Foo {} await assertErrorsInCode(r''' import 'foo.dart'; -mixin Bar implements Foo {} +final mixin Bar implements Foo {} ''', [ - error(CompileTimeErrorCode.FINAL_CLASS_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 40, + error(CompileTimeErrorCode.FINAL_CLASS_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 46, 3), ]); } @@ -171,9 +159,9 @@ typedef FooTypedef = Foo; await assertErrorsInCode(r''' import 'foo.dart'; -mixin Bar implements FooTypedef {} +final mixin Bar implements FooTypedef {} ''', [ - error(CompileTimeErrorCode.FINAL_CLASS_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 40, + error(CompileTimeErrorCode.FINAL_CLASS_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 46, 10), ]); } @@ -186,22 +174,10 @@ final class Foo {} await assertErrorsInCode(r''' import 'foo.dart'; typedef FooTypedef = Foo; -mixin Bar implements FooTypedef {} +final mixin Bar implements FooTypedef {} ''', [ - error(CompileTimeErrorCode.FINAL_CLASS_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 66, + error(CompileTimeErrorCode.FINAL_CLASS_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 72, 10), ]); } - - test_mixin_subtypeOfFinal_outside() async { - newFile('$testPackageLibPath/foo.dart', r''' -final class Foo {} -class Bar implements Foo {} -'''); - - await assertNoErrorsInCode(r''' -import 'foo.dart'; -mixin Bar2 implements Bar {} -'''); - } } diff --git a/pkg/analyzer/test/src/diagnostics/final_mixin_implemented_outside_of_library_test.dart b/pkg/analyzer/test/src/diagnostics/final_mixin_implemented_outside_of_library_test.dart index 23a7bb7fbc4..5bccd8033f0 100644 --- a/pkg/analyzer/test/src/diagnostics/final_mixin_implemented_outside_of_library_test.dart +++ b/pkg/analyzer/test/src/diagnostics/final_mixin_implemented_outside_of_library_test.dart @@ -19,7 +19,7 @@ class FinalMixinImplementedOutsideOfLibraryTest test_class_inside() async { await assertNoErrorsInCode(r''' final mixin Foo {} -class Bar implements Foo {} +final class Bar implements Foo {} '''); } @@ -30,9 +30,9 @@ final mixin Foo {} await assertErrorsInCode(r''' import 'foo.dart'; -class Bar implements Foo {} +final class Bar implements Foo {} ''', [ - error(CompileTimeErrorCode.FINAL_MIXIN_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 40, + error(CompileTimeErrorCode.FINAL_MIXIN_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 46, 3), ]); } @@ -45,9 +45,9 @@ typedef FooTypedef = Foo; await assertErrorsInCode(r''' import 'foo.dart'; -class Bar implements FooTypedef {} +final class Bar implements FooTypedef {} ''', [ - error(CompileTimeErrorCode.FINAL_MIXIN_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 40, + error(CompileTimeErrorCode.FINAL_MIXIN_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 46, 10), ]); } @@ -60,25 +60,13 @@ final mixin Foo {} await assertErrorsInCode(r''' import 'foo.dart'; typedef FooTypedef = Foo; -class Bar implements FooTypedef {} +final class Bar implements FooTypedef {} ''', [ - error(CompileTimeErrorCode.FINAL_MIXIN_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 66, + error(CompileTimeErrorCode.FINAL_MIXIN_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 72, 10), ]); } - test_class_subtypeOfFinal_outside() async { - newFile('$testPackageLibPath/foo.dart', r''' -final mixin Foo {} -class Bar implements Foo {} -'''); - - await assertNoErrorsInCode(r''' -import 'foo.dart'; -class Bar2 implements Bar {} -'''); - } - test_enum_inside() async { await assertNoErrorsInCode(r''' final mixin Foo {} @@ -145,7 +133,7 @@ enum Bar2 implements Bar { bar } test_mixin_inside() async { await assertNoErrorsInCode(r''' final mixin Foo {} -mixin Bar implements Foo {} +final mixin Bar implements Foo {} '''); } @@ -156,9 +144,9 @@ final mixin Foo {} await assertErrorsInCode(r''' import 'foo.dart'; -mixin Bar implements Foo {} +final mixin Bar implements Foo {} ''', [ - error(CompileTimeErrorCode.FINAL_MIXIN_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 40, + error(CompileTimeErrorCode.FINAL_MIXIN_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 46, 3), ]); } @@ -171,9 +159,9 @@ typedef FooTypedef = Foo; await assertErrorsInCode(r''' import 'foo.dart'; -mixin Bar implements FooTypedef {} +final mixin Bar implements FooTypedef {} ''', [ - error(CompileTimeErrorCode.FINAL_MIXIN_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 40, + error(CompileTimeErrorCode.FINAL_MIXIN_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 46, 10), ]); } @@ -186,22 +174,10 @@ final mixin Foo {} await assertErrorsInCode(r''' import 'foo.dart'; typedef FooTypedef = Foo; -mixin Bar implements FooTypedef {} +final mixin Bar implements FooTypedef {} ''', [ - error(CompileTimeErrorCode.FINAL_MIXIN_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 66, + error(CompileTimeErrorCode.FINAL_MIXIN_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 72, 10), ]); } - - test_mixin_subtypeOfFinal_outside() async { - newFile('$testPackageLibPath/foo.dart', r''' -final mixin Foo {} -class Bar implements Foo {} -'''); - - await assertNoErrorsInCode(r''' -import 'foo.dart'; -mixin Bar2 implements Bar {} -'''); - } } diff --git a/pkg/analyzer/test/src/diagnostics/final_mixin_mixed_in_outside_of_library_test.dart b/pkg/analyzer/test/src/diagnostics/final_mixin_mixed_in_outside_of_library_test.dart index 3c3b0d82d0d..61f7467337f 100644 --- a/pkg/analyzer/test/src/diagnostics/final_mixin_mixed_in_outside_of_library_test.dart +++ b/pkg/analyzer/test/src/diagnostics/final_mixin_mixed_in_outside_of_library_test.dart @@ -18,7 +18,7 @@ class FinalMixinMixedInOutsideOfLibraryTest extends PubPackageResolutionTest { test_class_inside() async { await assertNoErrorsInCode(r''' final mixin Foo {} -class Bar with Foo {} +final class Bar with Foo {} '''); } @@ -29,10 +29,10 @@ final mixin Foo {} await assertErrorsInCode(r''' import 'foo.dart'; -class Bar with Foo {} +final class Bar with Foo {} ''', [ error( - CompileTimeErrorCode.FINAL_MIXIN_MIXED_IN_OUTSIDE_OF_LIBRARY, 34, 3), + CompileTimeErrorCode.FINAL_MIXIN_MIXED_IN_OUTSIDE_OF_LIBRARY, 40, 3), ]); } @@ -44,10 +44,10 @@ typedef FooTypedef = Foo; await assertErrorsInCode(r''' import 'foo.dart'; -class Bar with FooTypedef {} +final class Bar with FooTypedef {} ''', [ error( - CompileTimeErrorCode.FINAL_MIXIN_MIXED_IN_OUTSIDE_OF_LIBRARY, 34, 10), + CompileTimeErrorCode.FINAL_MIXIN_MIXED_IN_OUTSIDE_OF_LIBRARY, 40, 10), ]); } @@ -59,25 +59,13 @@ final mixin Foo {} await assertErrorsInCode(r''' import 'foo.dart'; typedef FooTypedef = Foo; -class Bar with FooTypedef {} +final class Bar with FooTypedef {} ''', [ error( - CompileTimeErrorCode.FINAL_MIXIN_MIXED_IN_OUTSIDE_OF_LIBRARY, 60, 10), + CompileTimeErrorCode.FINAL_MIXIN_MIXED_IN_OUTSIDE_OF_LIBRARY, 66, 10), ]); } - test_class_subtypeOfBase_outside() async { - newFile('$testPackageLibPath/foo.dart', r''' -final mixin Foo {} -class Bar with Foo {} -'''); - - await assertNoErrorsInCode(r''' -import 'foo.dart'; -class Bar2 extends Bar {} -'''); - } - test_enum_inside() async { await assertNoErrorsInCode(r''' final mixin Foo {} diff --git a/pkg/analyzer/test/src/diagnostics/subtype_of_base_or_final_is_not_base_final_or_sealed_test.dart b/pkg/analyzer/test/src/diagnostics/subtype_of_base_or_final_is_not_base_final_or_sealed_test.dart new file mode 100644 index 00000000000..fffbc7197d0 --- /dev/null +++ b/pkg/analyzer/test/src/diagnostics/subtype_of_base_or_final_is_not_base_final_or_sealed_test.dart @@ -0,0 +1,570 @@ +// 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:analyzer/src/error/codes.dart'; +import 'package:test_reflective_loader/test_reflective_loader.dart'; + +import '../../generated/test_support.dart'; +import '../dart/resolution/context_collection_resolution.dart'; + +main() { + defineReflectiveSuite(() { + defineReflectiveTests(SubtypeOfBaseOrFinalIsNotBaseFinalOrSealedTest); + }); +} + +@reflectiveTest +class SubtypeOfBaseOrFinalIsNotBaseFinalOrSealedTest + extends PubPackageResolutionTest { + test_class_base_extends() async { + await assertErrorsInCode(r''' +base class A {} +class B extends A {} +''', [ + error( + CompileTimeErrorCode + .SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED, + 22, + 1, + text: + "The type 'B' must be 'base', 'final' or 'sealed' because the supertype 'A' is 'base'."), + ]); + } + + test_class_base_extends_multiple() async { + await assertErrorsInCode(r''' +base class A {} +base class B extends A {} +class C extends A {} +''', [ + error( + CompileTimeErrorCode + .SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED, + 48, + 1, + text: + "The type 'C' must be 'base', 'final' or 'sealed' because the supertype 'A' is 'base'."), + ]); + } + + test_class_base_extends_multiple_files() async { + await assertErrorsInFile('$testPackageLibPath/a.dart', r''' +base class A {} +class B extends A {} +''', [ + error( + CompileTimeErrorCode + .SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED, + 22, + 1, + text: + "The type 'B' must be 'base', 'final' or 'sealed' because the supertype 'A' is 'base'."), + ]); + await assertErrorsInFile('$testPackageLibPath/c.dart', r''' +import 'a.dart'; +class C extends B {} +''', [ + error( + CompileTimeErrorCode + .SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED, + 23, + 1, + text: + "The type 'C' must be 'base', 'final' or 'sealed' because the supertype 'A' is 'base'.", + contextMessages: [ + ExpectedContextMessage('/home/test/lib/a.dart', 22, 1, + text: + "The type 'B' is a subtype of 'A', and 'B' is defined here.") + ], + ), + ]); + await assertErrorsInCode(r''' +import 'a.dart'; +class D extends B {} +''', [ + error( + CompileTimeErrorCode + .SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED, + 23, + 1, + text: + "The type 'D' must be 'base', 'final' or 'sealed' because the supertype 'A' is 'base'.", + contextMessages: [ + ExpectedContextMessage('/home/test/lib/a.dart', 22, 1, + text: + "The type 'B' is a subtype of 'A', and 'B' is defined here.") + ], + ), + ]); + } + + test_class_base_extends_outside() async { + newFile('$testPackageLibPath/foo.dart', r''' +base class A {} +'''); + + await assertErrorsInCode(r''' +import 'foo.dart'; +class B extends A {} +''', [ + error( + CompileTimeErrorCode + .SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED, + 25, + 1, + text: + "The type 'B' must be 'base', 'final' or 'sealed' because the supertype 'A' is 'base'."), + ]); + } + + test_class_base_implements() async { + await assertErrorsInCode(r''' +base class A {} +class B implements A {} +''', [ + error( + CompileTimeErrorCode + .SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED, + 22, + 1, + text: + "The type 'B' must be 'base', 'final' or 'sealed' because the supertype 'A' is 'base'."), + ]); + } + + test_class_base_mixin_mixedIn() async { + await assertErrorsInCode(r''' +base mixin class A {} +class B with A {} +''', [ + error( + CompileTimeErrorCode + .SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED, + 28, + 1, + text: + "The type 'B' must be 'base', 'final' or 'sealed' because the supertype 'A' is 'base'."), + ]); + } + + test_class_base_mixin_mixedIn_outside() async { + newFile('$testPackageLibPath/foo.dart', r''' +base mixin class A {} +'''); + + await assertErrorsInCode(r''' +import 'foo.dart'; +class B with A {} +''', [ + error( + CompileTimeErrorCode + .SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED, + 25, + 1, + text: + "The type 'B' must be 'base', 'final' or 'sealed' because the supertype 'A' is 'base'."), + ]); + } + + test_class_base_multiple() async { + await assertErrorsInCode(r''' +base class A {} +final class B {} +class C extends B implements A {} +''', [ + error( + CompileTimeErrorCode + .SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED, + 39, + 1, + text: + "The type 'C' must be 'base', 'final' or 'sealed' because the supertype 'B' is 'final'."), + ]); + } + + test_class_final_extends() async { + await assertErrorsInCode(r''' +final class A {} +class B extends A {} +''', [ + error( + CompileTimeErrorCode + .SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED, + 23, + 1, + text: + "The type 'B' must be 'base', 'final' or 'sealed' because the supertype 'A' is 'final'."), + ]); + } + + test_class_final_implements() async { + await assertErrorsInCode(r''' +final class A {} +class B implements A {} +''', [ + error( + CompileTimeErrorCode + .SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED, + 23, + 1, + text: + "The type 'B' must be 'base', 'final' or 'sealed' because the supertype 'A' is 'final'."), + ]); + } + + test_class_sealed_base_extends() async { + await assertErrorsInCode(r''' +base class A {} +sealed class B extends A {} +class C extends B {} +''', [ + error( + CompileTimeErrorCode + .SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED, + 50, + 1, + text: + "The type 'C' must be 'base', 'final' or 'sealed' because the supertype 'A' is 'base'.", + contextMessages: [ + ExpectedContextMessage('/home/test/lib/test.dart', 29, 1, + text: + "The type 'B' is a subtype of 'A', and 'B' is defined here.") + ], + ), + ]); + } + + test_class_sealed_base_extends_multiple() async { + await assertErrorsInCode(r''' +base class A {} +sealed class B extends A {} +sealed class C extends B {} +class D extends C {} +''', [ + error( + CompileTimeErrorCode + .SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED, + 78, + 1, + text: + "The type 'D' must be 'base', 'final' or 'sealed' because the supertype 'A' is 'base'.", + contextMessages: [ + ExpectedContextMessage('/home/test/lib/test.dart', 57, 1, + text: + "The type 'C' is a subtype of 'A', and 'C' is defined here.") + ], + ), + ]); + } + + test_class_sealed_base_extends_outside() async { + newFile('$testPackageLibPath/foo.dart', r''' +base class A {} +'''); + + await assertErrorsInCode(r''' +import 'foo.dart'; +sealed class B extends A {} +class C extends B {} +''', [ + error( + CompileTimeErrorCode + .SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED, + 53, + 1, + text: + "The type 'C' must be 'base', 'final' or 'sealed' because the supertype 'A' is 'base'.", + contextMessages: [ + ExpectedContextMessage('/home/test/lib/test.dart', 32, 1, + text: + "The type 'B' is a subtype of 'A', and 'B' is defined here.") + ], + ), + ]); + } + + test_class_sealed_base_extends_unordered() async { + await assertErrorsInCode(r''' +class C extends B {} +sealed class B extends A {} +base class A {} +''', [ + error( + CompileTimeErrorCode + .SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED, + 6, + 1, + text: + "The type 'C' must be 'base', 'final' or 'sealed' because the supertype 'A' is 'base'.", + contextMessages: [ + ExpectedContextMessage('/home/test/lib/test.dart', 34, 1, + text: + "The type 'B' is a subtype of 'A', and 'B' is defined here.") + ], + ), + ]); + } + + test_class_sealed_base_implements() async { + await assertErrorsInCode(r''' +base class A {} +sealed class B implements A {} +class C implements B {} +''', [ + error( + CompileTimeErrorCode + .SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED, + 53, + 1, + text: + "The type 'C' must be 'base', 'final' or 'sealed' because the supertype 'A' is 'base'.", + contextMessages: [ + ExpectedContextMessage('/home/test/lib/test.dart', 29, 1, + text: + "The type 'B' is a subtype of 'A', and 'B' is defined here.") + ], + ), + ]); + } + + test_class_sealed_base_mixin_mixedIn() async { + await assertErrorsInCode(r''' +base mixin class A {} +sealed class B with A {} +class C extends B {} +''', [ + error( + CompileTimeErrorCode + .SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED, + 53, + 1, + text: + "The type 'C' must be 'base', 'final' or 'sealed' because the supertype 'A' is 'base'.", + contextMessages: [ + ExpectedContextMessage('/home/test/lib/test.dart', 35, 1, + text: + "The type 'B' is a subtype of 'A', and 'B' is defined here.") + ], + ), + ]); + } + + test_class_sealed_base_mixin_mixedIn_outside() async { + newFile('$testPackageLibPath/foo.dart', r''' +base mixin class A {} +'''); + + await assertErrorsInCode(r''' +import 'foo.dart'; +sealed class B with A {} +class C extends B {} +''', [ + error( + CompileTimeErrorCode + .SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED, + 50, + 1, + text: + "The type 'C' must be 'base', 'final' or 'sealed' because the supertype 'A' is 'base'.", + contextMessages: [ + ExpectedContextMessage('/home/test/lib/test.dart', 32, 1, + text: + "The type 'B' is a subtype of 'A', and 'B' is defined here.") + ], + ), + ]); + } + + test_class_sealed_final_extends() async { + await assertErrorsInCode(r''' +final class A {} +sealed class B extends A {} +class C extends B {} +''', [ + error( + CompileTimeErrorCode + .SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED, + 51, + 1, + text: + "The type 'C' must be 'base', 'final' or 'sealed' because the supertype 'A' is 'final'.", + contextMessages: [ + ExpectedContextMessage('/home/test/lib/test.dart', 30, 1, + text: + "The type 'B' is a subtype of 'A', and 'B' is defined here.") + ], + ), + ]); + } + + test_class_sealed_final_implements() async { + await assertErrorsInCode(r''' +final class A {} +sealed class B implements A {} +class C implements B {} +''', [ + error( + CompileTimeErrorCode + .SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED, + 54, + 1, + text: + "The type 'C' must be 'base', 'final' or 'sealed' because the supertype 'A' is 'final'.", + contextMessages: [ + ExpectedContextMessage('/home/test/lib/test.dart', 30, 1, + text: + "The type 'B' is a subtype of 'A', and 'B' is defined here.") + ], + ), + ]); + } + + test_mixin_base_implements() async { + await assertErrorsInCode(r''' +base class A {} +mixin B implements A {} +''', [ + error( + CompileTimeErrorCode + .SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED, + 22, + 1, + text: + "The type 'B' must be 'base', 'final' or 'sealed' because the supertype 'A' is 'base'."), + ]); + } + + test_mixin_base_on() async { + await assertErrorsInCode(r''' +base class A {} +mixin B on A {} +''', [ + error( + CompileTimeErrorCode + .SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED, + 22, + 1, + text: + "The type 'B' must be 'base', 'final' or 'sealed' because the supertype 'A' is 'base'."), + ]); + } + + test_mixin_final_implements() async { + await assertErrorsInCode(r''' +final class A {} +mixin B implements A {} +''', [ + error( + CompileTimeErrorCode + .SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED, + 23, + 1, + text: + "The type 'B' must be 'base', 'final' or 'sealed' because the supertype 'A' is 'final'."), + ]); + } + + test_mixin_final_on() async { + await assertErrorsInCode(r''' +final class A {} +mixin B on A {} +''', [ + error( + CompileTimeErrorCode + .SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED, + 23, + 1, + text: + "The type 'B' must be 'base', 'final' or 'sealed' because the supertype 'A' is 'final'."), + ]); + } + + test_mixin_sealed_base_implements() async { + await assertErrorsInCode(r''' +base class A {} +sealed mixin B implements A {} +mixin C implements B {} +''', [ + error( + CompileTimeErrorCode + .SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED, + 53, + 1, + text: + "The type 'C' must be 'base', 'final' or 'sealed' because the supertype 'A' is 'base'.", + contextMessages: [ + ExpectedContextMessage('/home/test/lib/test.dart', 29, 1, + text: + "The type 'B' is a subtype of 'A', and 'B' is defined here.") + ], + ), + ]); + } + + test_mixin_sealed_base_on() async { + await assertErrorsInCode(r''' +base class A {} +sealed mixin B on A {} +mixin C on B {} +''', [ + error( + CompileTimeErrorCode + .SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED, + 45, + 1, + text: + "The type 'C' must be 'base', 'final' or 'sealed' because the supertype 'A' is 'base'.", + contextMessages: [ + ExpectedContextMessage('/home/test/lib/test.dart', 29, 1, + text: + "The type 'B' is a subtype of 'A', and 'B' is defined here.") + ], + ), + ]); + } + + test_mixin_sealed_final_implements() async { + await assertErrorsInCode(r''' +final class A {} +sealed mixin B implements A {} +mixin C implements B {} +''', [ + error( + CompileTimeErrorCode + .SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED, + 54, + 1, + text: + "The type 'C' must be 'base', 'final' or 'sealed' because the supertype 'A' is 'final'.", + contextMessages: [ + ExpectedContextMessage('/home/test/lib/test.dart', 30, 1, + text: + "The type 'B' is a subtype of 'A', and 'B' is defined here.") + ], + ), + ]); + } + + test_mixin_sealed_final_on() async { + await assertErrorsInCode(r''' +final class A {} +sealed mixin B on A {} +mixin C on B {} +''', [ + error( + CompileTimeErrorCode + .SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED, + 46, + 1, + text: + "The type 'C' must be 'base', 'final' or 'sealed' because the supertype 'A' is 'final'.", + contextMessages: [ + ExpectedContextMessage('/home/test/lib/test.dart', 30, 1, + text: + "The type 'B' is a subtype of 'A', and 'B' is defined here.") + ], + ), + ]); + } +} diff --git a/pkg/analyzer/test/src/diagnostics/test_all.dart b/pkg/analyzer/test/src/diagnostics/test_all.dart index b52beae6e3b..b5d7b78cd37 100644 --- a/pkg/analyzer/test/src/diagnostics/test_all.dart +++ b/pkg/analyzer/test/src/diagnostics/test_all.dart @@ -741,6 +741,8 @@ import 'spread_expression_from_deferred_library_test.dart' import 'static_access_to_instance_member_test.dart' as static_access_to_instance_member; import 'strict_raw_type_test.dart' as strict_raw_type; +import 'subtype_of_base_or_final_is_not_base_final_or_sealed_test.dart' + as subtype_of_base_or_final_is_not_base_final_or_sealed; import 'subtype_of_ffi_class_test.dart' as subtype_of_ffi_class; import 'subtype_of_sealed_class_test.dart' as subtype_of_sealed_class; import 'subtype_of_struct_class_test.dart' as subtype_of_struct_class; @@ -1351,6 +1353,7 @@ main() { spread_expression_from_deferred_library.main(); static_access_to_instance_member.main(); strict_raw_type.main(); + subtype_of_base_or_final_is_not_base_final_or_sealed.main(); subtype_of_ffi_class.main(); subtype_of_sealed_class.main(); subtype_of_struct_class.main(); diff --git a/pkg/analyzer/test/src/lint/lint_rule_test.dart b/pkg/analyzer/test/src/lint/lint_rule_test.dart index 07eb2595abe..b9aff55f36d 100644 --- a/pkg/analyzer/test/src/lint/lint_rule_test.dart +++ b/pkg/analyzer/test/src/lint/lint_rule_test.dart @@ -79,7 +79,7 @@ class CollectingReporter extends ErrorReporter { @override void reportErrorForElement(ErrorCode errorCode, Element element, - [List? arguments]) { + [List? arguments, List? messages]) { code = errorCode; } diff --git a/tests/language/class_modifiers/base/base_class_extend_not_base_final_error_test.dart b/tests/language/class_modifiers/base/base_class_extend_not_base_final_error_test.dart index 89018c4c202..63ec0b04d1e 100644 --- a/tests/language/class_modifiers/base/base_class_extend_not_base_final_error_test.dart +++ b/tests/language/class_modifiers/base/base_class_extend_not_base_final_error_test.dart @@ -10,11 +10,11 @@ import 'base_class_extend_lib.dart'; abstract class AOutside extends BaseClass {} -// ^ -// [analyzer] unspecified +// ^^^^^^^^ +// [analyzer] COMPILE_TIME_ERROR.SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED // [cfe] unspecified class BOutside extends BaseClass {} -// ^ -// [analyzer] unspecified +// ^^^^^^^^ +// [analyzer] COMPILE_TIME_ERROR.SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED // [cfe] unspecified diff --git a/tests/language/class_modifiers/base/base_class_inside_not_base_final_sealed_error_test.dart b/tests/language/class_modifiers/base/base_class_inside_not_base_final_sealed_error_test.dart index d00fdedb85a..e9c95151a26 100644 --- a/tests/language/class_modifiers/base/base_class_inside_not_base_final_sealed_error_test.dart +++ b/tests/language/class_modifiers/base/base_class_inside_not_base_final_sealed_error_test.dart @@ -2,35 +2,58 @@ // 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. -// SharedOptions=--enable-experiment=class-modifiers +// SharedOptions=--enable-experiment=class-modifiers,sealed-class // Error when subtyping a base class where the subtype is not base, final or // sealed. base class BaseClass {} base mixin BaseMixin {} +final class FinalClass extends BaseClass {} +sealed class SubtypeOfBase extends BaseClass {} +class RegularClass {} class Extends extends BaseClass {} -// ^ -// [analyzer] unspecified +// ^^^^^^^ +// [analyzer] COMPILE_TIME_ERROR.SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED // [cfe] unspecified class Implements implements BaseClass {} -// ^ -// [analyzer] unspecified +// ^^^^^^^^^^ +// [analyzer] COMPILE_TIME_ERROR.SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED // [cfe] unspecified mixin MixinImplements implements BaseMixin {} -// ^ -// [analyzer] unspecified +// ^^^^^^^^^^^^^^^ +// [analyzer] COMPILE_TIME_ERROR.SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED // [cfe] unspecified class With with BaseMixin {} -// ^ -// [analyzer] unspecified +// ^^^^ +// [analyzer] COMPILE_TIME_ERROR.SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED // [cfe] unspecified mixin On on BaseClass {} -// ^ -// [analyzer] unspecified +// ^^ +// [analyzer] COMPILE_TIME_ERROR.SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED +// [cfe] unspecified + +class ExtendsExtends extends Extends {} +// ^^^^^^^^^^^^^^ +// [analyzer] COMPILE_TIME_ERROR.SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED +// [cfe] unspecified + +class Multiple extends FinalClass implements BaseMixin {} +// ^^^^^^^^ +// [analyzer] COMPILE_TIME_ERROR.SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED +// [cfe] unspecified + +class Multiple2 extends RegularClass implements BaseClass {} +// ^^^^^^^^^ +// [analyzer] COMPILE_TIME_ERROR.SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED +// [cfe] unspecified + +class IndirectSubtype extends SubtypeOfBase {} +// ^^^^^^^^^^^^^^^ +// [analyzer] COMPILE_TIME_ERROR.SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED // [cfe] unspecified diff --git a/tests/language/class_modifiers/base/base_mixin_with_not_base_final_error_test.dart b/tests/language/class_modifiers/base/base_mixin_with_not_base_final_error_test.dart index 95576a5deac..239632f481a 100644 --- a/tests/language/class_modifiers/base/base_mixin_with_not_base_final_error_test.dart +++ b/tests/language/class_modifiers/base/base_mixin_with_not_base_final_error_test.dart @@ -10,11 +10,11 @@ import 'base_mixin_with_lib.dart'; abstract class AOutside with BaseMixin {} -// ^ -// [analyzer] unspecified +// ^^^^^^^^ +// [analyzer] COMPILE_TIME_ERROR.SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED // [cfe] unspecified class BOutside with BaseMixin {} -// ^ -// [analyzer] unspecified +// ^^^^^^^^ +// [analyzer] COMPILE_TIME_ERROR.SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED // [cfe] unspecified diff --git a/tests/language/class_modifiers/final/final_class_inside_not_base_final_sealed_error_test.dart b/tests/language/class_modifiers/final/final_class_inside_not_base_final_sealed_error_test.dart index 6fe59bbfb86..00c8e460c3b 100644 --- a/tests/language/class_modifiers/final/final_class_inside_not_base_final_sealed_error_test.dart +++ b/tests/language/class_modifiers/final/final_class_inside_not_base_final_sealed_error_test.dart @@ -2,35 +2,58 @@ // 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. -// SharedOptions=--enable-experiment=class-modifiers +// SharedOptions=--enable-experiment=class-modifiers,sealed-class // Error when subtyping a final class where the subtype is not base, final or // sealed. final class FinalClass {} final mixin FinalMixin {} +base class BaseClass extends FinalClass {} +sealed class SubtypeOfFinal extends FinalClass {} +class RegularClass {} class Extends extends FinalClass {} -// ^ -// [analyzer] unspecified +// ^^^^^^^ +// [analyzer] COMPILE_TIME_ERROR.SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED // [cfe] unspecified class Implements implements FinalClass {} -// ^ -// [analyzer] unspecified +// ^^^^^^^^^^ +// [analyzer] COMPILE_TIME_ERROR.SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED // [cfe] unspecified mixin MixinImplements implements FinalMixin {} -// ^ -// [analyzer] unspecified +// ^^^^^^^^^^^^^^^ +// [analyzer] COMPILE_TIME_ERROR.SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED // [cfe] unspecified class With with FinalMixin {} -// ^ -// [analyzer] unspecified +// ^^^^ +// [analyzer] COMPILE_TIME_ERROR.SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED // [cfe] unspecified mixin On on FinalClass {} -// ^ -// [analyzer] unspecified +// ^^ +// [analyzer] COMPILE_TIME_ERROR.SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED +// [cfe] unspecified + +class ExtendsExtends extends Extends {} +// ^^^^^^^^^^^^^^ +// [analyzer] COMPILE_TIME_ERROR.SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED +// [cfe] unspecified + +class Multiple extends BaseClass implements FinalMixin {} +// ^^^^^^^^ +// [analyzer] COMPILE_TIME_ERROR.SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED +// [cfe] unspecified + +class Multiple2 extends RegularClass implements FinalClass {} +// ^^^^^^^^^ +// [analyzer] COMPILE_TIME_ERROR.SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED +// [cfe] unspecified + +class IndirectSubtype extends SubtypeOfFinal {} +// ^^^^^^^^^^^^^^^ +// [analyzer] COMPILE_TIME_ERROR.SUBTYPE_OF_BASE_OR_FINAL_IS_NOT_BASE_FINAL_OR_SEALED // [cfe] unspecified