mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 15:42:20 +00:00
[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: https://github.com/dart-lang/sdk/issues/52078 Change-Id: Ie16edb2b231957dad7502fdab3d5faba93bc6773 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/300861 Commit-Queue: Kallen Tu <kallentu@google.com> Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
parent
5a046ceedd
commit
6de1afdb3a
|
@ -2893,33 +2893,19 @@ class ErrorVerifier extends RecursiveAstVisitor<void>
|
|||
ImplementsClause? implementsClause,
|
||||
OnClause? onClause,
|
||||
) {
|
||||
List<Element> elementsToCheck(InterfaceType type) {
|
||||
final element = type.element;
|
||||
if (element.library.featureSet.isEnabled(Feature.class_modifiers)) {
|
||||
return [element];
|
||||
} else {
|
||||
return [
|
||||
element,
|
||||
...element.allSupertypes.map((e) => e.element),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (superclass != null) {
|
||||
final type = superclass.type;
|
||||
if (type is InterfaceType) {
|
||||
final elements = elementsToCheck(type);
|
||||
for (final element in elements) {
|
||||
if (element is ClassElementImpl &&
|
||||
element.isFinal &&
|
||||
!element.isSealed &&
|
||||
element.library != _currentLibrary &&
|
||||
!_mayIgnoreClassModifiers(element.library)) {
|
||||
errorReporter.reportErrorForNode(
|
||||
CompileTimeErrorCode.FINAL_CLASS_EXTENDED_OUTSIDE_OF_LIBRARY,
|
||||
superclass,
|
||||
[element.name]);
|
||||
}
|
||||
final element = type.element;
|
||||
if (element is ClassElementImpl &&
|
||||
element.isFinal &&
|
||||
!element.isSealed &&
|
||||
element.library != _currentLibrary &&
|
||||
!_mayIgnoreClassModifiers(element.library)) {
|
||||
errorReporter.reportErrorForNode(
|
||||
CompileTimeErrorCode.FINAL_CLASS_EXTENDED_OUTSIDE_OF_LIBRARY,
|
||||
superclass,
|
||||
[element.name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2927,18 +2913,31 @@ class ErrorVerifier extends RecursiveAstVisitor<void>
|
|||
for (NamedType namedType in implementsClause.interfaces) {
|
||||
final type = namedType.type;
|
||||
if (type is InterfaceType) {
|
||||
final elements = elementsToCheck(type);
|
||||
for (final element in elements) {
|
||||
final implementedInterfaces = [
|
||||
type,
|
||||
...type.element.allSupertypes,
|
||||
].map((e) => e.element).toList();
|
||||
for (final element in implementedInterfaces) {
|
||||
if (element is ClassElement &&
|
||||
element.isFinal &&
|
||||
!element.isSealed &&
|
||||
element.library != _currentLibrary &&
|
||||
!_mayIgnoreClassModifiers(element.library)) {
|
||||
// If the final interface is an indirect interface and is in a
|
||||
// different library that has class modifiers enabled, there is a
|
||||
// nearer declaration that would emit an error, if any.
|
||||
if (element != type.element &&
|
||||
type.element.library.featureSet
|
||||
.isEnabled(Feature.class_modifiers)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
errorReporter.reportErrorForNode(
|
||||
CompileTimeErrorCode
|
||||
.FINAL_CLASS_IMPLEMENTED_OUTSIDE_OF_LIBRARY,
|
||||
namedType,
|
||||
[element.name]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -539,7 +539,7 @@ final class Null extends Object {
|
|||
}
|
||||
}
|
||||
|
||||
class MapEntry<K, V> {
|
||||
final class MapEntry<K, V> {
|
||||
final K key;
|
||||
final V value;
|
||||
const factory MapEntry(K key, V value) = MapEntry<K, V>._;
|
||||
|
|
|
@ -36,24 +36,28 @@ final class Bar extends Foo {}
|
|||
]);
|
||||
}
|
||||
|
||||
test_outside_viaLanguage219() async {
|
||||
newFile('$testPackageLibPath/a.dart', r'''
|
||||
final class A {}
|
||||
test_outside_viaLanguage219AndCore() async {
|
||||
// There is no error when extending a pre-feature class that subtypes a
|
||||
// class in the core libraries.
|
||||
final a = newFile('$testPackageLibPath/a.dart', r'''
|
||||
// @dart=2.19
|
||||
import 'dart:core';
|
||||
class A implements MapEntry<int, int> {
|
||||
int get key => 0;
|
||||
int get value => 1;
|
||||
}
|
||||
''');
|
||||
|
||||
newFile('$testPackageLibPath/b.dart', r'''
|
||||
// @dart = 2.19
|
||||
await resolveFile2(a.path);
|
||||
assertNoErrorsInResult();
|
||||
|
||||
await assertNoErrorsInCode(r'''
|
||||
import 'a.dart';
|
||||
class B extends A {}
|
||||
final class B extends A {
|
||||
int get key => 0;
|
||||
int get value => 1;
|
||||
}
|
||||
''');
|
||||
|
||||
await assertErrorsInCode(r'''
|
||||
import 'b.dart';
|
||||
final class C extends B {}
|
||||
''', [
|
||||
error(
|
||||
CompileTimeErrorCode.FINAL_CLASS_EXTENDED_OUTSIDE_OF_LIBRARY, 39, 1),
|
||||
]);
|
||||
}
|
||||
|
||||
test_outside_viaTypedef_inside() async {
|
||||
|
|
|
@ -37,21 +37,25 @@ final class Bar implements Foo {}
|
|||
]);
|
||||
}
|
||||
|
||||
test_class_outside_viaLanguage219() async {
|
||||
int;
|
||||
newFile('$testPackageLibPath/a.dart', r'''
|
||||
final class A {}
|
||||
test_class_outside_viaLanguage219AndCore() async {
|
||||
final a = newFile('$testPackageLibPath/a.dart', r'''
|
||||
// @dart=2.19
|
||||
import 'dart:core';
|
||||
class A implements MapEntry<int, int> {
|
||||
int get key => 0;
|
||||
int get value => 1;
|
||||
}
|
||||
''');
|
||||
|
||||
newFile('$testPackageLibPath/b.dart', r'''
|
||||
// @dart = 2.19
|
||||
import 'a.dart';
|
||||
class B implements A {}
|
||||
''');
|
||||
await resolveFile2(a.path);
|
||||
assertNoErrorsInResult();
|
||||
|
||||
await assertErrorsInCode(r'''
|
||||
import 'b.dart';
|
||||
final class C implements B {}
|
||||
import 'a.dart';
|
||||
final class B implements A {
|
||||
int get key => 0;
|
||||
int get value => 1;
|
||||
}
|
||||
''', [
|
||||
error(CompileTimeErrorCode.FINAL_CLASS_IMPLEMENTED_OUTSIDE_OF_LIBRARY, 42,
|
||||
1),
|
||||
|
|
|
@ -189,7 +189,7 @@ base mixin BaseMixinImplement implements FinalClass {}
|
|||
|
||||
class LegacyImplement implements LegacyImplementFinalCore {
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
// [analyzer] unspecified
|
||||
// [analyzer] COMPILE_TIME_ERROR.INVALID_USE_OF_TYPE_OUTSIDE_LIBRARY
|
||||
// [cfe] unspecified
|
||||
int get key => 0;
|
||||
int get value => 1;
|
||||
|
|
|
@ -12,33 +12,33 @@ import "legacy_lib.dart";
|
|||
abstract base class ImplementsLegacyImplementsFinal
|
||||
implements LegacyImplementsFinal {
|
||||
// ^^^^^^^^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.INVALID_USE_OF_TYPE_OUTSIDE_LIBRARY
|
||||
// [cfe] unspecified
|
||||
// [analyzer] unspecified
|
||||
}
|
||||
|
||||
abstract base class ImplementsLegacyExtendsFinal implements LegacyExtendsFinal {
|
||||
// ^^^^^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.INVALID_USE_OF_TYPE_OUTSIDE_LIBRARY
|
||||
// [cfe] unspecified
|
||||
// [analyzer] unspecified
|
||||
}
|
||||
|
||||
abstract class ImplementsLegacyMixesInFinal implements LegacyMixesInFinal {
|
||||
// ^^^^^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.INVALID_USE_OF_TYPE_OUTSIDE_LIBRARY
|
||||
// [cfe] unspecified
|
||||
// [analyzer] unspecified
|
||||
}
|
||||
|
||||
abstract base class ImplementsLegacyImplementsBase
|
||||
implements LegacyImplementsBase {
|
||||
// ^^^^^^^^^^^^^^^^^^^^^
|
||||
// ^^^^^^^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.INVALID_USE_OF_TYPE_OUTSIDE_LIBRARY
|
||||
// [cfe] unspecified
|
||||
// [analyzer] unspecified
|
||||
}
|
||||
|
||||
abstract base class ImplementsLegacyMixinOnFinal implements LegacyMixinOnFinal {
|
||||
// ^^^^^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.INVALID_USE_OF_TYPE_OUTSIDE_LIBRARY
|
||||
// [cfe] unspecified
|
||||
// [analyzer] unspecified
|
||||
}
|
||||
|
||||
// Not allowed to omit base on classes with base/final superclasses.
|
||||
|
@ -175,4 +175,4 @@ abstract class MixesInMixinImplementsBase2 = Object
|
|||
// Helpers.
|
||||
mixin _AnyMixin {}
|
||||
|
||||
void main() {}
|
||||
void main() {}
|
||||
|
|
Loading…
Reference in a new issue