mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 11:42:11 +00:00
[analyzer] Report error when subtyping a sealed class outside of its library.
Change-Id: Iee6c9120d096ed49592ca3d155c81c876ceb5c34 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/273832 Reviewed-by: Konstantin Shcheglov <scheglov@google.com> Commit-Queue: Kallen Tu <kallentu@google.com> Reviewed-by: Johnni Winther <johnniwinther@google.com>
This commit is contained in:
parent
df3df89fd9
commit
7ff20a67ca
|
@ -10706,8 +10706,8 @@ const Template<
|
|||
const Code<Message Function(String name)>
|
||||
codeSealedClassSubtypeOutsideOfLibrary =
|
||||
const Code<Message Function(String name)>(
|
||||
"SealedClassSubtypeOutsideOfLibrary",
|
||||
);
|
||||
"SealedClassSubtypeOutsideOfLibrary",
|
||||
analyzerCodes: <String>["SEALED_CLASS_SUBTYPE_OUTSIDE_OF_LIBRARY"]);
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
Message _withArgumentsSealedClassSubtypeOutsideOfLibrary(String name) {
|
||||
|
|
|
@ -970,6 +970,8 @@ CompileTimeErrorCode.RETURN_OF_INVALID_TYPE_FROM_METHOD:
|
|||
status: hasFix
|
||||
CompileTimeErrorCode.RETURN_WITHOUT_VALUE:
|
||||
status: needsEvaluation
|
||||
CompileTimeErrorCode.SEALED_CLASS_SUBTYPE_OUTSIDE_OF_LIBRARY:
|
||||
status: needsEvaluation
|
||||
CompileTimeErrorCode.SET_ELEMENT_FROM_DEFERRED_LIBRARY:
|
||||
status: needsEvaluation
|
||||
CompileTimeErrorCode.SET_ELEMENT_TYPE_NOT_ASSIGNABLE:
|
||||
|
|
|
@ -4127,6 +4127,15 @@ class CompileTimeErrorCode extends AnalyzerErrorCode {
|
|||
hasPublishedDocs: true,
|
||||
);
|
||||
|
||||
/// Parameters:
|
||||
/// 0: the name of the supertype being extended, implemented, or mixed in
|
||||
static const CompileTimeErrorCode SEALED_CLASS_SUBTYPE_OUTSIDE_OF_LIBRARY =
|
||||
CompileTimeErrorCode(
|
||||
'SEALED_CLASS_SUBTYPE_OUTSIDE_OF_LIBRARY',
|
||||
"The sealed class '{0}' can't be extended, implemented, or mixed in "
|
||||
"outside of its library.",
|
||||
);
|
||||
|
||||
/// No parameters.
|
||||
static const CompileTimeErrorCode SET_ELEMENT_FROM_DEFERRED_LIBRARY =
|
||||
CompileTimeErrorCode(
|
||||
|
|
|
@ -414,6 +414,7 @@ const List<ErrorCode> errorCodeValues = [
|
|||
CompileTimeErrorCode.RETURN_OF_INVALID_TYPE_FROM_FUNCTION,
|
||||
CompileTimeErrorCode.RETURN_OF_INVALID_TYPE_FROM_METHOD,
|
||||
CompileTimeErrorCode.RETURN_WITHOUT_VALUE,
|
||||
CompileTimeErrorCode.SEALED_CLASS_SUBTYPE_OUTSIDE_OF_LIBRARY,
|
||||
CompileTimeErrorCode.SET_ELEMENT_FROM_DEFERRED_LIBRARY,
|
||||
CompileTimeErrorCode.SET_ELEMENT_TYPE_NOT_ASSIGNABLE,
|
||||
CompileTimeErrorCode.SHARED_DEFERRED_PREFIX,
|
||||
|
|
|
@ -1405,6 +1405,8 @@ class ErrorVerifier extends RecursiveAstVisitor<void>
|
|||
_checkMixinsSuperClass(withClause);
|
||||
_checkForMixinWithConflictingPrivateMember(withClause, superclass);
|
||||
_checkForConflictingGenerics(node);
|
||||
_checkForSealedSupertypeOutsideOfLibrary(
|
||||
node, superclass, withClause, implementsClause);
|
||||
if (node is ClassDeclaration) {
|
||||
_checkForNoDefaultSuperConstructorImplicit(node);
|
||||
}
|
||||
|
@ -4204,6 +4206,49 @@ class ErrorVerifier extends RecursiveAstVisitor<void>
|
|||
CompileTimeErrorCode.RETURN_IN_GENERATIVE_CONSTRUCTOR, body);
|
||||
}
|
||||
|
||||
/// Check that if a direct supertype of a node is sealed, then it must be in
|
||||
/// the same library.
|
||||
///
|
||||
/// See [CompileTimeErrorCode.SEALED_CLASS_SUBTYPE_OUTSIDE_OF_LIBRARY].
|
||||
void _checkForSealedSupertypeOutsideOfLibrary(
|
||||
NamedCompilationUnitMember node,
|
||||
NamedType? superclass,
|
||||
WithClause? withClause,
|
||||
ImplementsClause? implementsClause) {
|
||||
// TODO (kallentu): Distinguish between mixins and classes and make a
|
||||
// separate error for both.
|
||||
void reportSealedClassOutsideOfLibraryError(ClassElementImpl superclass) {
|
||||
if (superclass.isSealed && superclass.library != _currentLibrary) {
|
||||
errorReporter.reportErrorForNode(
|
||||
CompileTimeErrorCode.SEALED_CLASS_SUBTYPE_OUTSIDE_OF_LIBRARY,
|
||||
node,
|
||||
[superclass.name]);
|
||||
}
|
||||
}
|
||||
|
||||
if (superclass != null) {
|
||||
final superclassType = superclass.type;
|
||||
if (superclassType is InterfaceType) {
|
||||
final superclassElement = superclassType.element;
|
||||
if (superclassElement is ClassElementImpl) {
|
||||
reportSealedClassOutsideOfLibraryError(superclassElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO (kallentu): Report errors for the [withClause].
|
||||
if (implementsClause != null) {
|
||||
for (NamedType interface in implementsClause.interfaces) {
|
||||
final interfaceType = interface.type;
|
||||
if (interfaceType is InterfaceType) {
|
||||
final interfaceElement = interfaceType.element;
|
||||
if (interfaceElement is ClassElementImpl) {
|
||||
reportSealedClassOutsideOfLibraryError(interfaceElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Verify that the elements in the given set [literal] are subtypes of the
|
||||
/// set's static type.
|
||||
///
|
||||
|
@ -4980,6 +5025,8 @@ class ErrorVerifier extends RecursiveAstVisitor<void>
|
|||
CompileTimeErrorCode.IMPLEMENTS_REPEATED,
|
||||
);
|
||||
_checkForConflictingGenerics(node);
|
||||
_checkForSealedSupertypeOutsideOfLibrary(
|
||||
node, null, null, implementsClause);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -91,7 +91,10 @@ class ElementBuilder extends ThrowingAstVisitor<void> {
|
|||
var element = ClassElementImpl(name, nameToken.offset);
|
||||
element.isAbstract = node.abstractKeyword != null;
|
||||
element.isMacro = node.macroKeyword != null;
|
||||
element.isSealed = node.sealedKeyword != null;
|
||||
if (node.sealedKeyword != null) {
|
||||
element.isSealed = true;
|
||||
element.isAbstract = true;
|
||||
}
|
||||
element.metadata = _buildAnnotations(node.metadata);
|
||||
_setCodeRange(element, node);
|
||||
_setDocumentation(element, node);
|
||||
|
@ -125,7 +128,10 @@ class ElementBuilder extends ThrowingAstVisitor<void> {
|
|||
var element = ClassElementImpl(name, nameToken.offset);
|
||||
element.isAbstract = node.abstractKeyword != null;
|
||||
element.isMacro = node.macroKeyword != null;
|
||||
element.isSealed = node.sealedKeyword != null;
|
||||
if (node.sealedKeyword != null) {
|
||||
element.isSealed = true;
|
||||
element.isAbstract = true;
|
||||
}
|
||||
element.isMixinApplication = true;
|
||||
element.metadata = _buildAnnotations(node.metadata);
|
||||
_setCodeRange(element, node);
|
||||
|
|
|
@ -12557,6 +12557,11 @@ CompileTimeErrorCode:
|
|||
return 0;
|
||||
}
|
||||
```
|
||||
SEALED_CLASS_SUBTYPE_OUTSIDE_OF_LIBRARY:
|
||||
problemMessage: "The sealed class '{0}' can't be extended, implemented, or mixed in outside of its library."
|
||||
comment: |-
|
||||
Parameters:
|
||||
0: the name of the supertype being extended, implemented, or mixed in
|
||||
SET_ELEMENT_TYPE_NOT_ASSIGNABLE:
|
||||
problemMessage: "The element type '{0}' can't be assigned to the set type '{1}'."
|
||||
hasPublishedDocs: true
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
// 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(SealedClassSubtypeOutsideOfLibraryTest);
|
||||
});
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class SealedClassSubtypeOutsideOfLibraryTest extends PubPackageResolutionTest {
|
||||
test_class_extends_sealed_inside() async {
|
||||
await assertNoErrorsInCode(r'''
|
||||
sealed class Foo {}
|
||||
class Bar extends Foo {}
|
||||
''');
|
||||
}
|
||||
|
||||
test_class_extends_sealed_outside() async {
|
||||
newFile('$testPackageLibPath/foo.dart', r'''
|
||||
sealed class Foo {}
|
||||
''');
|
||||
|
||||
await assertErrorsInCode(r'''
|
||||
import 'foo.dart';
|
||||
class Bar extends Foo {}
|
||||
''', [
|
||||
error(
|
||||
CompileTimeErrorCode.SEALED_CLASS_SUBTYPE_OUTSIDE_OF_LIBRARY, 19, 24),
|
||||
]);
|
||||
}
|
||||
|
||||
test_class_extends_sealed_outside_viaTypedef_inside() async {
|
||||
newFile('$testPackageLibPath/foo.dart', r'''
|
||||
sealed class Foo {}
|
||||
typedef FooTypedef = Foo;
|
||||
''');
|
||||
|
||||
await assertErrorsInCode(r'''
|
||||
import 'foo.dart';
|
||||
class Bar extends FooTypedef {}
|
||||
''', [
|
||||
error(
|
||||
CompileTimeErrorCode.SEALED_CLASS_SUBTYPE_OUTSIDE_OF_LIBRARY, 19, 31),
|
||||
]);
|
||||
}
|
||||
|
||||
test_class_extends_sealed_outside_viaTypedef_outside() async {
|
||||
newFile('$testPackageLibPath/foo.dart', r'''
|
||||
sealed class Foo {}
|
||||
''');
|
||||
|
||||
await assertErrorsInCode(r'''
|
||||
import 'foo.dart';
|
||||
typedef FooTypedef = Foo;
|
||||
class Bar extends FooTypedef {}
|
||||
''', [
|
||||
error(
|
||||
CompileTimeErrorCode.SEALED_CLASS_SUBTYPE_OUTSIDE_OF_LIBRARY, 45, 31),
|
||||
]);
|
||||
}
|
||||
|
||||
test_class_extends_subtypeOfSealed_outside() async {
|
||||
newFile('$testPackageLibPath/foo.dart', r'''
|
||||
sealed class Foo {}
|
||||
class Bar extends Foo {}
|
||||
''');
|
||||
|
||||
await assertNoErrorsInCode(r'''
|
||||
import 'foo.dart';
|
||||
class Bar2 extends Bar {}
|
||||
''');
|
||||
}
|
||||
|
||||
test_class_implements_sealed_inside() async {
|
||||
await assertNoErrorsInCode(r'''
|
||||
sealed class Foo {}
|
||||
class Bar implements Foo {}
|
||||
''');
|
||||
}
|
||||
|
||||
test_class_implements_sealed_outside() async {
|
||||
newFile('$testPackageLibPath/foo.dart', r'''
|
||||
sealed class Foo {}
|
||||
''');
|
||||
|
||||
await assertErrorsInCode(r'''
|
||||
import 'foo.dart';
|
||||
class Bar implements Foo {}
|
||||
''', [
|
||||
error(
|
||||
CompileTimeErrorCode.SEALED_CLASS_SUBTYPE_OUTSIDE_OF_LIBRARY, 19, 27),
|
||||
]);
|
||||
}
|
||||
|
||||
test_class_implements_sealed_outside_viaTypedef_inside() async {
|
||||
newFile('$testPackageLibPath/foo.dart', r'''
|
||||
sealed class Foo {}
|
||||
typedef FooTypedef = Foo;
|
||||
''');
|
||||
|
||||
await assertErrorsInCode(r'''
|
||||
import 'foo.dart';
|
||||
class Bar implements FooTypedef {}
|
||||
''', [
|
||||
error(
|
||||
CompileTimeErrorCode.SEALED_CLASS_SUBTYPE_OUTSIDE_OF_LIBRARY, 19, 34),
|
||||
]);
|
||||
}
|
||||
|
||||
test_class_implements_sealed_outside_viaTypedef_outside() async {
|
||||
newFile('$testPackageLibPath/foo.dart', r'''
|
||||
sealed class Foo {}
|
||||
''');
|
||||
|
||||
await assertErrorsInCode(r'''
|
||||
import 'foo.dart';
|
||||
typedef FooTypedef = Foo;
|
||||
class Bar implements FooTypedef {}
|
||||
''', [
|
||||
error(
|
||||
CompileTimeErrorCode.SEALED_CLASS_SUBTYPE_OUTSIDE_OF_LIBRARY, 45, 34),
|
||||
]);
|
||||
}
|
||||
|
||||
test_class_implements_subtypeOfSealed_outside() async {
|
||||
newFile('$testPackageLibPath/foo.dart', r'''
|
||||
sealed class Foo {}
|
||||
class Bar implements Foo {}
|
||||
''');
|
||||
|
||||
await assertNoErrorsInCode(r'''
|
||||
import 'foo.dart';
|
||||
class Bar2 implements Bar {}
|
||||
''');
|
||||
}
|
||||
|
||||
test_mixin_implements_sealed_outside() async {
|
||||
newFile('$testPackageLibPath/foo.dart', r'''
|
||||
sealed class Foo {}
|
||||
''');
|
||||
|
||||
await assertErrorsInCode(r'''
|
||||
import 'foo.dart';
|
||||
mixin Bar implements Foo {}
|
||||
''', [
|
||||
error(
|
||||
CompileTimeErrorCode.SEALED_CLASS_SUBTYPE_OUTSIDE_OF_LIBRARY, 19, 27),
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -687,6 +687,8 @@ import 'sdk_version_set_literal_test.dart' as sdk_version_set_literal;
|
|||
import 'sdk_version_ui_as_code_in_const_context_test.dart'
|
||||
as sdk_version_ui_as_code_in_const_context;
|
||||
import 'sdk_version_ui_as_code_test.dart' as sdk_version_ui_as_code;
|
||||
import 'sealed_class_subtype_outside_of_library_test.dart'
|
||||
as sealed_class_subtype_outside_of_library;
|
||||
import 'set_element_from_deferred_library_test.dart'
|
||||
as set_element_from_deferred_library;
|
||||
import 'set_element_type_not_assignable_test.dart'
|
||||
|
@ -1268,6 +1270,7 @@ main() {
|
|||
sdk_version_set_literal.main();
|
||||
sdk_version_ui_as_code.main();
|
||||
sdk_version_ui_as_code_in_const_context.main();
|
||||
sealed_class_subtype_outside_of_library.main();
|
||||
set_element_type_not_assignable.main();
|
||||
shared_deferred_prefix.main();
|
||||
size_annotation_dimensions.main();
|
||||
|
|
|
@ -7576,7 +7576,7 @@ library
|
|||
library
|
||||
definingUnit
|
||||
classes
|
||||
sealed class C @13
|
||||
abstract sealed class C @13
|
||||
constructors
|
||||
synthetic @-1
|
||||
''');
|
||||
|
@ -9052,7 +9052,7 @@ mixin M {}
|
|||
library
|
||||
definingUnit
|
||||
classes
|
||||
sealed class alias C @13
|
||||
abstract sealed class alias C @13
|
||||
supertype: Object
|
||||
mixins
|
||||
M
|
||||
|
|
|
@ -864,7 +864,6 @@ SdkSpecificationNotFound/analyzerCode: Fail
|
|||
SdkSpecificationNotFound/example: Fail
|
||||
SdkSummaryNotFound/analyzerCode: Fail
|
||||
SdkSummaryNotFound/example: Fail
|
||||
SealedClassSubtypeOutsideOfLibrary/analyzerCode: Fail
|
||||
SealedClassSubtypeOutsideOfLibrary/part_wrapped_script: Fail # Uses imports
|
||||
SealedMixinSubtypeOutsideOfLibrary/analyzerCode: Fail
|
||||
SealedMixinSubtypeOutsideOfLibrary/part_wrapped_script: Fail # Uses imports
|
||||
|
|
|
@ -6004,6 +6004,7 @@ InheritedRestrictedMemberOfEnumImplementer:
|
|||
|
||||
SealedClassSubtypeOutsideOfLibrary:
|
||||
problemMessage: "Sealed class '#name' can't be extended, implemented, or mixed in outside of its library."
|
||||
analyzerCode: SEALED_CLASS_SUBTYPE_OUTSIDE_OF_LIBRARY
|
||||
experiments: sealed-class
|
||||
script:
|
||||
main.dart:
|
||||
|
|
|
@ -16,13 +16,15 @@ sealed class NotConstructableWithMixin = Object with M;
|
|||
|
||||
main() {
|
||||
var error = NotConstructable();
|
||||
// ^
|
||||
// ^^^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.INSTANTIATE_ABSTRACT_CLASS
|
||||
// [cfe] The class 'NotConstructable' is abstract and can't be instantiated.
|
||||
var error2 = AlsoNotConstructable();
|
||||
// ^^^^^^^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.MIXIN_INSTANTIATE
|
||||
// [cfe] Couldn't find constructor 'AlsoNotConstructable'.
|
||||
var error3 = NotConstructableWithMixin();
|
||||
// ^
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.INSTANTIATE_ABSTRACT_CLASS
|
||||
// [cfe] The class 'NotConstructableWithMixin' is abstract and can't be instantiated.
|
||||
}
|
||||
|
|
|
@ -9,13 +9,15 @@
|
|||
import 'sealed_class_extend_lib.dart';
|
||||
|
||||
abstract class OutsideA extends SealedClass {}
|
||||
// [error column 1, length 46]
|
||||
// [analyzer] COMPILE_TIME_ERROR.SEALED_CLASS_SUBTYPE_OUTSIDE_OF_LIBRARY
|
||||
// ^
|
||||
// [analyzer] unspecified
|
||||
// [cfe] Sealed class 'SealedClass' can't be extended, implemented, or mixed in outside of its library.
|
||||
|
||||
class OutsideB extends SealedClass {
|
||||
// [error column 1, length 326]
|
||||
// [analyzer] COMPILE_TIME_ERROR.SEALED_CLASS_SUBTYPE_OUTSIDE_OF_LIBRARY
|
||||
// ^
|
||||
// [analyzer] unspecified
|
||||
// [cfe] Sealed class 'SealedClass' can't be extended, implemented, or mixed in outside of its library.
|
||||
@override
|
||||
int foo = 2;
|
||||
|
|
|
@ -9,13 +9,15 @@
|
|||
import "sealed_class_implement_lib.dart";
|
||||
|
||||
abstract class OutsideA implements SealedClass {}
|
||||
// [error column 1, length 49]
|
||||
// [analyzer] COMPILE_TIME_ERROR.SEALED_CLASS_SUBTYPE_OUTSIDE_OF_LIBRARY
|
||||
// ^
|
||||
// [analyzer] unspecified
|
||||
// [cfe] Sealed class 'SealedClass' can't be extended, implemented, or mixed in outside of its library.
|
||||
|
||||
class OutsideB implements SealedClass {
|
||||
// [error column 1, length 344]
|
||||
// [analyzer] COMPILE_TIME_ERROR.SEALED_CLASS_SUBTYPE_OUTSIDE_OF_LIBRARY
|
||||
// ^
|
||||
// [analyzer] unspecified
|
||||
// [cfe] Sealed class 'SealedClass' can't be extended, implemented, or mixed in outside of its library.
|
||||
@override
|
||||
int nonAbstractFoo = 2;
|
||||
|
@ -31,6 +33,7 @@ class OutsideB implements SealedClass {
|
|||
}
|
||||
|
||||
mixin OutsideMixin implements SealedClass {}
|
||||
// [error column 1, length 44]
|
||||
// [analyzer] COMPILE_TIME_ERROR.SEALED_CLASS_SUBTYPE_OUTSIDE_OF_LIBRARY
|
||||
// ^
|
||||
// [analyzer] unspecified
|
||||
// [cfe] Sealed class 'SealedClass' can't be extended, implemented, or mixed in outside of its library.
|
||||
|
|
|
@ -10,13 +10,15 @@
|
|||
import 'sealed_class_typedef_lib.dart';
|
||||
|
||||
class ATypeDef extends SealedClassTypeDef {}
|
||||
// [error column 1, length 44]
|
||||
// [analyzer] COMPILE_TIME_ERROR.SEALED_CLASS_SUBTYPE_OUTSIDE_OF_LIBRARY
|
||||
// ^
|
||||
// [analyzer] unspecified
|
||||
// [cfe] Sealed class 'SealedClass' can't be extended, implemented, or mixed in outside of its library.
|
||||
|
||||
class BTypeDef implements SealedClassTypeDef {
|
||||
// [error column 1, length 213]
|
||||
// [analyzer] COMPILE_TIME_ERROR.SEALED_CLASS_SUBTYPE_OUTSIDE_OF_LIBRARY
|
||||
// ^
|
||||
// [analyzer] unspecified
|
||||
// [cfe] Sealed class 'SealedClass' can't be extended, implemented, or mixed in outside of its library.
|
||||
@override
|
||||
int foo = 1;
|
||||
|
|
|
@ -13,13 +13,15 @@ import 'sealed_class_typedef_used_outside_lib.dart';
|
|||
typedef ATypeDef = SealedClass;
|
||||
|
||||
class A extends ATypeDef {}
|
||||
// [error column 1, length 27]
|
||||
// [analyzer] COMPILE_TIME_ERROR.SEALED_CLASS_SUBTYPE_OUTSIDE_OF_LIBRARY
|
||||
// ^
|
||||
// [analyzer] unspecified
|
||||
// [cfe] Sealed class 'SealedClass' can't be extended, implemented, or mixed in outside of its library.
|
||||
|
||||
class B implements ATypeDef {
|
||||
// [error column 1, length 184]
|
||||
// [analyzer] COMPILE_TIME_ERROR.SEALED_CLASS_SUBTYPE_OUTSIDE_OF_LIBRARY
|
||||
// ^
|
||||
// [analyzer] unspecified
|
||||
// [cfe] Sealed class 'SealedClass' can't be extended, implemented, or mixed in outside of its library.
|
||||
int foo = 1;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue