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 82d6dc4d38c..d436eed5982 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 @@ -654,6 +654,8 @@ CompileTimeErrorCode.EXTENSION_TYPE_IMPLEMENTS_DISALLOWED_TYPE: status: noFix CompileTimeErrorCode.EXTENSION_TYPE_IMPLEMENTS_ITSELF: status: noFix +CompileTimeErrorCode.EXTENSION_TYPE_IMPLEMENTS_NOT_SUPERTYPE_OF_ERASURE: + status: noFix CompileTimeErrorCode.EXTENSION_TYPE_INHERITED_MEMBER_CONFLICT: status: noFix CompileTimeErrorCode.EXTENSION_TYPE_REPRESENTATION_DEPENDS_ON_ITSELF: diff --git a/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart b/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart index a7ed8f5c707..01bf94befcd 100644 --- a/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart +++ b/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart @@ -1648,15 +1648,9 @@ class ResolutionVisitor extends RecursiveAstVisitor { final type = namedType.typeOrThrow; - if (declaration is ExtensionTypeDeclaration) { - final typeSystem = _libraryElement.typeSystem; - if (!typeSystem.isValidExtensionTypeSuperinterface(type)) { - _errorReporter.reportErrorForNode( - CompileTimeErrorCode.EXTENSION_TYPE_IMPLEMENTS_DISALLOWED_TYPE, - namedType, - [type], - ); - } + final enclosingElement = _namedTypeResolver.enclosingClass; + if (enclosingElement is ExtensionTypeElementImpl) { + _verifyExtensionElementImplements(enclosingElement, namedType, type); return; } @@ -1770,6 +1764,35 @@ class ResolutionVisitor extends RecursiveAstVisitor { } } + void _verifyExtensionElementImplements( + ExtensionTypeElementImpl declaredElement, + NamedTypeImpl node, + DartType type, + ) { + final typeSystem = _libraryElement.typeSystem; + + if (!typeSystem.isValidExtensionTypeSuperinterface(type)) { + _errorReporter.reportErrorForNode( + CompileTimeErrorCode.EXTENSION_TYPE_IMPLEMENTS_DISALLOWED_TYPE, + node, + [type], + ); + return; + } + + if (type.element is! ExtensionTypeElement) { + final erasure = declaredElement.typeErasure; + if (!typeSystem.isSubtypeOf(erasure, type)) { + _errorReporter.reportErrorForNode( + CompileTimeErrorCode + .EXTENSION_TYPE_IMPLEMENTS_NOT_SUPERTYPE_OF_ERASURE, + node, + [type, erasure], + ); + } + } + } + void _visitIf(IfElementOrStatementImpl node) { var caseClause = node.caseClause; if (caseClause != null) { diff --git a/pkg/analyzer/lib/src/error/codes.g.dart b/pkg/analyzer/lib/src/error/codes.g.dart index e1ad281bd31..fdd367635c4 100644 --- a/pkg/analyzer/lib/src/error/codes.g.dart +++ b/pkg/analyzer/lib/src/error/codes.g.dart @@ -1651,6 +1651,17 @@ class CompileTimeErrorCode extends AnalyzerErrorCode { "Try removing the superinterface that references this extension type.", ); + /// Parameters: + /// 0: the implemented not extension type + /// 1: the ultimate representation type + static const CompileTimeErrorCode + EXTENSION_TYPE_IMPLEMENTS_NOT_SUPERTYPE_OF_ERASURE = CompileTimeErrorCode( + 'EXTENSION_TYPE_IMPLEMENTS_NOT_SUPERTYPE_OF_ERASURE', + "'{0}' is not a supertype of '{1}', the ultimate representation type.", + correctionMessage: + "Try specifying a different type, or remove the type from the list.", + ); + /// Parameters: /// 0: the name of the extension type /// 1: the name of the conflicting member 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 a6719d3b0fa..d1a89da4852 100644 --- a/pkg/analyzer/lib/src/error/error_code_values.g.dart +++ b/pkg/analyzer/lib/src/error/error_code_values.g.dart @@ -197,6 +197,7 @@ const List errorCodeValues = [ CompileTimeErrorCode.EXTENSION_TYPE_DECLARES_MEMBER_OF_OBJECT, CompileTimeErrorCode.EXTENSION_TYPE_IMPLEMENTS_DISALLOWED_TYPE, CompileTimeErrorCode.EXTENSION_TYPE_IMPLEMENTS_ITSELF, + CompileTimeErrorCode.EXTENSION_TYPE_IMPLEMENTS_NOT_SUPERTYPE_OF_ERASURE, CompileTimeErrorCode.EXTENSION_TYPE_INHERITED_MEMBER_CONFLICT, CompileTimeErrorCode.EXTENSION_TYPE_REPRESENTATION_DEPENDS_ON_ITSELF, CompileTimeErrorCode.EXTERNAL_FIELD_CONSTRUCTOR_INITIALIZER, diff --git a/pkg/analyzer/messages.yaml b/pkg/analyzer/messages.yaml index 0e596f9bd41..307880c44d1 100644 --- a/pkg/analyzer/messages.yaml +++ b/pkg/analyzer/messages.yaml @@ -5082,6 +5082,13 @@ CompileTimeErrorCode: problemMessage: "The extension type can't implement itself." correctionMessage: Try removing the superinterface that references this extension type. comment: No parameters. + EXTENSION_TYPE_IMPLEMENTS_NOT_SUPERTYPE_OF_ERASURE: + problemMessage: "'{0}' is not a supertype of '{1}', the ultimate representation type." + correctionMessage: Try specifying a different type, or remove the type from the list. + comment: |- + Parameters: + 0: the implemented not extension type + 1: the ultimate representation type EXTENSION_TYPE_INHERITED_MEMBER_CONFLICT: problemMessage: "The extension type '{0}' has more than one distinct member named '{1}' from implemented types." correctionMessage: Try redeclaring the corresponding member in this extension type. diff --git a/pkg/analyzer/test/src/diagnostics/extension_type_implements_not_supertype_of_erasure_test.dart b/pkg/analyzer/test/src/diagnostics/extension_type_implements_not_supertype_of_erasure_test.dart new file mode 100644 index 00000000000..11c92b2af98 --- /dev/null +++ b/pkg/analyzer/test/src/diagnostics/extension_type_implements_not_supertype_of_erasure_test.dart @@ -0,0 +1,52 @@ +// 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 '../dart/resolution/context_collection_resolution.dart'; + +main() { + defineReflectiveSuite(() { + defineReflectiveTests(ExtensionTypeImplementsDisallowedTypeTest); + }); +} + +@reflectiveTest +class ExtensionTypeImplementsDisallowedTypeTest + extends PubPackageResolutionTest { + test_notSupertype() async { + await assertErrorsInCode(''' +extension type A(int it) implements String {} +''', [ + error( + CompileTimeErrorCode + .EXTENSION_TYPE_IMPLEMENTS_NOT_SUPERTYPE_OF_ERASURE, + 36, + 6), + ]); + } + + test_supertype2() async { + await assertNoErrorsInCode(''' +extension type A(S3 it) implements S1 {} +class S1 {} +class S2 extends S1 {} +class S3 extends S2 {} +'''); + } + + test_supertype_erasure() async { + await assertNoErrorsInCode(''' +extension type A(int it) {} +extension type B(A it) implements num {} +'''); + } + + test_supertype_interfaceType() async { + await assertNoErrorsInCode(''' +extension type A(int it) implements num {} +'''); + } +} diff --git a/pkg/analyzer/test/src/diagnostics/extension_type_inherited_member_conflict_test.dart b/pkg/analyzer/test/src/diagnostics/extension_type_inherited_member_conflict_test.dart index e9f918b7fe2..d169db16763 100644 --- a/pkg/analyzer/test/src/diagnostics/extension_type_inherited_member_conflict_test.dart +++ b/pkg/analyzer/test/src/diagnostics/extension_type_inherited_member_conflict_test.dart @@ -129,22 +129,26 @@ class ExtensionTypeInheritedMemberConflictTest_notExtension test_conflict() async { await assertErrorsInCode(''' class A { - int foo() => 0; + void foo(int a) {} } class B { - String foo() => '0'; + void foo(String a) {} } -extension type C(Object it) implements A, B {} +class C implements A, B { + void foo(Object a) {} +} + +extension type D(C it) implements A, B {} ''', [ error( CompileTimeErrorCode.EXTENSION_TYPE_INHERITED_MEMBER_CONFLICT, - 82, + 139, 1, contextMessages: [ - message('/home/test/lib/test.dart', 16, 3), - message('/home/test/lib/test.dart', 50, 3), + message('/home/test/lib/test.dart', 17, 3), + message('/home/test/lib/test.dart', 51, 3) ], ), ]); @@ -153,14 +157,18 @@ extension type C(Object it) implements A, B {} test_noConflict_redeclared() async { await assertNoErrorsInCode(''' class A { - int foo() => 0; + void foo(int a) {} } class B { - String foo() => '0'; + void foo(String a) {} } -extension type C(Object it) implements A, B { +class C implements A, B { + void foo(Object a) {} +} + +extension type D(C it) implements A, B { void foo() {} } '''); diff --git a/pkg/analyzer/test/src/diagnostics/test_all.dart b/pkg/analyzer/test/src/diagnostics/test_all.dart index c42a882ac9a..af8cb261be4 100644 --- a/pkg/analyzer/test/src/diagnostics/test_all.dart +++ b/pkg/analyzer/test/src/diagnostics/test_all.dart @@ -250,6 +250,8 @@ import 'extension_type_implements_disallowed_type_test.dart' as extension_type_implements_disallowed_type; import 'extension_type_implements_itself_test.dart' as extension_type_implements_itself; +import 'extension_type_implements_not_supertype_of_erasure_test.dart' + as extension_type_implements_not_supertype_of_erasure; import 'extension_type_inherited_member_conflict_test.dart' as extension_type_inherited_member_conflict; import 'extension_type_representation_depends_on_itself_test.dart' @@ -1058,6 +1060,7 @@ main() { extension_type_declares_member_of_object.main(); extension_type_implements_disallowed_type.main(); extension_type_implements_itself.main(); + extension_type_implements_not_supertype_of_erasure.main(); extension_type_inherited_member_conflict.main(); extension_type_representation_depends_on_itself.main(); external_field_constructor_initializer.main();