[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:
Kallen Tu 2022-12-07 22:00:28 +00:00 committed by Commit Queue
parent df3df89fd9
commit 7ff20a67ca
17 changed files with 259 additions and 18 deletions

View file

@ -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) {

View file

@ -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:

View file

@ -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(

View file

@ -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,

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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

View file

@ -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),
]);
}
}

View file

@ -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();

View file

@ -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

View file

@ -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

View file

@ -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:

View file

@ -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.
}

View file

@ -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;

View file

@ -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.

View file

@ -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;

View file

@ -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;
}