Skip to content

Commit 6de1afd

Browse files
kallentuCommit Queue
authored and
Commit Queue
committed
[analyzer] Disallow implementing a legacy library subclass of a final class.
Checks an interface's supertypes for final classes and produces an error if the implementing bypasses a legacy library. This behaviour should only happen when a post-feature library implements a pre-feature library declaration that has a final class as a super declaration. Similar error to https://dart-review.googlesource.com/c/sdk/+/298320 Bug: #52078 Change-Id: Ie16edb2b231957dad7502fdab3d5faba93bc6773 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/300861 Commit-Queue: Kallen Tu <[email protected]> Reviewed-by: Konstantin Shcheglov <[email protected]>
1 parent 5a046ce commit 6de1afd

File tree

6 files changed

+67
-60
lines changed

6 files changed

+67
-60
lines changed

pkg/analyzer/lib/src/generated/error_verifier.dart

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2893,52 +2893,51 @@ class ErrorVerifier extends RecursiveAstVisitor<void>
28932893
ImplementsClause? implementsClause,
28942894
OnClause? onClause,
28952895
) {
2896-
List<Element> elementsToCheck(InterfaceType type) {
2897-
final element = type.element;
2898-
if (element.library.featureSet.isEnabled(Feature.class_modifiers)) {
2899-
return [element];
2900-
} else {
2901-
return [
2902-
element,
2903-
...element.allSupertypes.map((e) => e.element),
2904-
];
2905-
}
2906-
}
2907-
29082896
if (superclass != null) {
29092897
final type = superclass.type;
29102898
if (type is InterfaceType) {
2911-
final elements = elementsToCheck(type);
2912-
for (final element in elements) {
2913-
if (element is ClassElementImpl &&
2914-
element.isFinal &&
2915-
!element.isSealed &&
2916-
element.library != _currentLibrary &&
2917-
!_mayIgnoreClassModifiers(element.library)) {
2918-
errorReporter.reportErrorForNode(
2919-
CompileTimeErrorCode.FINAL_CLASS_EXTENDED_OUTSIDE_OF_LIBRARY,
2920-
superclass,
2921-
[element.name]);
2922-
}
2899+
final element = type.element;
2900+
if (element is ClassElementImpl &&
2901+
element.isFinal &&
2902+
!element.isSealed &&
2903+
element.library != _currentLibrary &&
2904+
!_mayIgnoreClassModifiers(element.library)) {
2905+
errorReporter.reportErrorForNode(
2906+
CompileTimeErrorCode.FINAL_CLASS_EXTENDED_OUTSIDE_OF_LIBRARY,
2907+
superclass,
2908+
[element.name]);
29232909
}
29242910
}
29252911
}
29262912
if (implementsClause != null) {
29272913
for (NamedType namedType in implementsClause.interfaces) {
29282914
final type = namedType.type;
29292915
if (type is InterfaceType) {
2930-
final elements = elementsToCheck(type);
2931-
for (final element in elements) {
2916+
final implementedInterfaces = [
2917+
type,
2918+
...type.element.allSupertypes,
2919+
].map((e) => e.element).toList();
2920+
for (final element in implementedInterfaces) {
29322921
if (element is ClassElement &&
29332922
element.isFinal &&
29342923
!element.isSealed &&
29352924
element.library != _currentLibrary &&
29362925
!_mayIgnoreClassModifiers(element.library)) {
2926+
// If the final interface is an indirect interface and is in a
2927+
// different library that has class modifiers enabled, there is a
2928+
// nearer declaration that would emit an error, if any.
2929+
if (element != type.element &&
2930+
type.element.library.featureSet
2931+
.isEnabled(Feature.class_modifiers)) {
2932+
continue;
2933+
}
2934+
29372935
errorReporter.reportErrorForNode(
29382936
CompileTimeErrorCode
29392937
.FINAL_CLASS_IMPLEMENTED_OUTSIDE_OF_LIBRARY,
29402938
namedType,
29412939
[element.name]);
2940+
break;
29422941
}
29432942
}
29442943
}

pkg/analyzer/lib/src/test_utilities/mock_sdk.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -539,7 +539,7 @@ final class Null extends Object {
539539
}
540540
}
541541
542-
class MapEntry<K, V> {
542+
final class MapEntry<K, V> {
543543
final K key;
544544
final V value;
545545
const factory MapEntry(K key, V value) = MapEntry<K, V>._;

pkg/analyzer/test/src/diagnostics/final_class_extended_outside_of_library_test.dart

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,24 +36,28 @@ final class Bar extends Foo {}
3636
]);
3737
}
3838

39-
test_outside_viaLanguage219() async {
40-
newFile('$testPackageLibPath/a.dart', r'''
41-
final class A {}
39+
test_outside_viaLanguage219AndCore() async {
40+
// There is no error when extending a pre-feature class that subtypes a
41+
// class in the core libraries.
42+
final a = newFile('$testPackageLibPath/a.dart', r'''
43+
// @dart=2.19
44+
import 'dart:core';
45+
class A implements MapEntry<int, int> {
46+
int get key => 0;
47+
int get value => 1;
48+
}
4249
''');
4350

44-
newFile('$testPackageLibPath/b.dart', r'''
45-
// @dart = 2.19
51+
await resolveFile2(a.path);
52+
assertNoErrorsInResult();
53+
54+
await assertNoErrorsInCode(r'''
4655
import 'a.dart';
47-
class B extends A {}
56+
final class B extends A {
57+
int get key => 0;
58+
int get value => 1;
59+
}
4860
''');
49-
50-
await assertErrorsInCode(r'''
51-
import 'b.dart';
52-
final class C extends B {}
53-
''', [
54-
error(
55-
CompileTimeErrorCode.FINAL_CLASS_EXTENDED_OUTSIDE_OF_LIBRARY, 39, 1),
56-
]);
5761
}
5862

5963
test_outside_viaTypedef_inside() async {

pkg/analyzer/test/src/diagnostics/final_class_implemented_outside_of_library_test.dart

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,21 +37,25 @@ final class Bar implements Foo {}
3737
]);
3838
}
3939

40-
test_class_outside_viaLanguage219() async {
41-
int;
42-
newFile('$testPackageLibPath/a.dart', r'''
43-
final class A {}
40+
test_class_outside_viaLanguage219AndCore() async {
41+
final a = newFile('$testPackageLibPath/a.dart', r'''
42+
// @dart=2.19
43+
import 'dart:core';
44+
class A implements MapEntry<int, int> {
45+
int get key => 0;
46+
int get value => 1;
47+
}
4448
''');
4549

46-
newFile('$testPackageLibPath/b.dart', r'''
47-
// @dart = 2.19
48-
import 'a.dart';
49-
class B implements A {}
50-
''');
50+
await resolveFile2(a.path);
51+
assertNoErrorsInResult();
5152

5253
await assertErrorsInCode(r'''
53-
import 'b.dart';
54-
final class C implements B {}
54+
import 'a.dart';
55+
final class B implements A {
56+
int get key => 0;
57+
int get value => 1;
58+
}
5559
''', [
5660
error(CompileTimeErrorCode.FINAL_CLASS_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 42,
5761
1),

tests/language/class_modifiers/base_transitivity/final_class_different_library_error_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ base mixin BaseMixinImplement implements FinalClass {}
189189

190190
class LegacyImplement implements LegacyImplementFinalCore {
191191
// ^^^^^^^^^^^^^^^^^^^^^^^^
192-
// [analyzer] unspecified
192+
// [analyzer] COMPILE_TIME_ERROR.INVALID_USE_OF_TYPE_OUTSIDE_LIBRARY
193193
// [cfe] unspecified
194194
int get key => 0;
195195
int get value => 1;

tests/language/class_modifiers/trans_legacy/legacy_superdeclaration_error_test.dart

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,33 +12,33 @@ import "legacy_lib.dart";
1212
abstract base class ImplementsLegacyImplementsFinal
1313
implements LegacyImplementsFinal {
1414
// ^^^^^^^^^^^^^^^^^^^^^
15+
// [analyzer] COMPILE_TIME_ERROR.INVALID_USE_OF_TYPE_OUTSIDE_LIBRARY
1516
// [cfe] unspecified
16-
// [analyzer] unspecified
1717
}
1818

1919
abstract base class ImplementsLegacyExtendsFinal implements LegacyExtendsFinal {
2020
// ^^^^^^^^^^^^^^^^^^
21+
// [analyzer] COMPILE_TIME_ERROR.INVALID_USE_OF_TYPE_OUTSIDE_LIBRARY
2122
// [cfe] unspecified
22-
// [analyzer] unspecified
2323
}
2424

2525
abstract class ImplementsLegacyMixesInFinal implements LegacyMixesInFinal {
2626
// ^^^^^^^^^^^^^^^^^^
27+
// [analyzer] COMPILE_TIME_ERROR.INVALID_USE_OF_TYPE_OUTSIDE_LIBRARY
2728
// [cfe] unspecified
28-
// [analyzer] unspecified
2929
}
3030

3131
abstract base class ImplementsLegacyImplementsBase
3232
implements LegacyImplementsBase {
33-
// ^^^^^^^^^^^^^^^^^^^^^
33+
// ^^^^^^^^^^^^^^^^^^^^
34+
// [analyzer] COMPILE_TIME_ERROR.INVALID_USE_OF_TYPE_OUTSIDE_LIBRARY
3435
// [cfe] unspecified
35-
// [analyzer] unspecified
3636
}
3737

3838
abstract base class ImplementsLegacyMixinOnFinal implements LegacyMixinOnFinal {
3939
// ^^^^^^^^^^^^^^^^^^
40+
// [analyzer] COMPILE_TIME_ERROR.INVALID_USE_OF_TYPE_OUTSIDE_LIBRARY
4041
// [cfe] unspecified
41-
// [analyzer] unspecified
4242
}
4343

4444
// Not allowed to omit base on classes with base/final superclasses.
@@ -175,4 +175,4 @@ abstract class MixesInMixinImplementsBase2 = Object
175175
// Helpers.
176176
mixin _AnyMixin {}
177177

178-
void main() {}
178+
void main() {}

0 commit comments

Comments
 (0)