[analyzer] Report an error when extending an interface class or mixing in an interface mixin.
Change-Id: I868b8c89fa52edbcd05d7f06e3246196bbf3d952
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/279910
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Kallen Tu <kallentu@google.com>
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 83478e6..2dfd1fe 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
@@ -623,6 +623,10 @@
status: hasFix
CompileTimeErrorCode.INTEGER_LITERAL_OUT_OF_RANGE:
status: needsEvaluation
+CompileTimeErrorCode.INTERFACE_CLASS_EXTENDED_OUTSIDE_OF_LIBRARY:
+ status: needsEvaluation
+CompileTimeErrorCode.INTERFACE_MIXIN_MIXED_IN_OUTSIDE_OF_LIBRARY:
+ status: needsEvaluation
CompileTimeErrorCode.INVALID_ANNOTATION:
status: hasFix
CompileTimeErrorCode.INVALID_ANNOTATION_CONSTANT_VALUE_FROM_DEFERRED_LIBRARY:
diff --git a/pkg/analyzer/lib/src/error/codes.g.dart b/pkg/analyzer/lib/src/error/codes.g.dart
index 2fd5bbb..baae8df 100644
--- a/pkg/analyzer/lib/src/error/codes.g.dart
+++ b/pkg/analyzer/lib/src/error/codes.g.dart
@@ -2246,6 +2246,26 @@
hasPublishedDocs: true,
);
+ /// Parameters:
+ /// 0: the name of the interface class being extended.
+ static const CompileTimeErrorCode
+ INTERFACE_CLASS_EXTENDED_OUTSIDE_OF_LIBRARY = CompileTimeErrorCode(
+ 'INVALID_USE_OF_TYPE_OUTSIDE_LIBRARY',
+ "The class '{0}' can't be extended outside of its library because it's an "
+ "interface class.",
+ uniqueName: 'INTERFACE_CLASS_EXTENDED_OUTSIDE_OF_LIBRARY',
+ );
+
+ /// Parameters:
+ /// 0: the name of the interface mixin being mixed in.
+ static const CompileTimeErrorCode
+ INTERFACE_MIXIN_MIXED_IN_OUTSIDE_OF_LIBRARY = CompileTimeErrorCode(
+ 'INVALID_USE_OF_TYPE_OUTSIDE_LIBRARY',
+ "The mixin '{0}' can't be mixed-in outside of its library because it's an "
+ "interface mixin.",
+ uniqueName: 'INTERFACE_MIXIN_MIXED_IN_OUTSIDE_OF_LIBRARY',
+ );
+
/// No parameters.
static const CompileTimeErrorCode INVALID_ANNOTATION = CompileTimeErrorCode(
'INVALID_ANNOTATION',
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 21b06346a..9b25033 100644
--- a/pkg/analyzer/lib/src/error/error_code_values.g.dart
+++ b/pkg/analyzer/lib/src/error/error_code_values.g.dart
@@ -244,6 +244,8 @@
CompileTimeErrorCode.INSTANTIATE_TYPE_ALIAS_EXPANDS_TO_TYPE_PARAMETER,
CompileTimeErrorCode.INTEGER_LITERAL_IMPRECISE_AS_DOUBLE,
CompileTimeErrorCode.INTEGER_LITERAL_OUT_OF_RANGE,
+ CompileTimeErrorCode.INTERFACE_CLASS_EXTENDED_OUTSIDE_OF_LIBRARY,
+ CompileTimeErrorCode.INTERFACE_MIXIN_MIXED_IN_OUTSIDE_OF_LIBRARY,
CompileTimeErrorCode.INVALID_ANNOTATION,
CompileTimeErrorCode.INVALID_ANNOTATION_CONSTANT_VALUE_FROM_DEFERRED_LIBRARY,
CompileTimeErrorCode.INVALID_ANNOTATION_FROM_DEFERRED_LIBRARY,
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index 3b484a2..608db1f 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -1395,6 +1395,8 @@
_checkForMixinWithConflictingPrivateMember(withClause, superclass);
_checkForConflictingGenerics(node);
_checkForBaseClassOrMixinImplementedOutsideOfLibrary(implementsClause);
+ _checkForInterfaceClassOrMixinSuperclassOutsideOfLibrary(
+ superclass, withClause);
_checkForClassUsedAsMixin(withClause);
_checkForSealedSupertypeOutsideOfLibrary(
superclass, withClause, implementsClause);
@@ -3031,6 +3033,48 @@
}
}
+ /// Verify that if a class is extending an interface class or mixing in an
+ /// interface mixin, it must be within the same library as that class or
+ /// mixin.
+ ///
+ /// See
+ /// [CompileTimeErrorCode.INTERFACE_CLASS_EXTENDED_OUTSIDE_OF_LIBRARY],
+ /// [CompileTimeErrorCode.INTERFACE_MIXIN_MIXED_IN_OUTSIDE_OF_LIBRARY].
+ void _checkForInterfaceClassOrMixinSuperclassOutsideOfLibrary(
+ NamedType? superclass, WithClause? withClause) {
+ if (superclass != null) {
+ final superclassType = superclass.type;
+ if (superclassType is InterfaceType) {
+ final superclassElement = superclassType.element;
+ if (superclassElement is ClassElementImpl &&
+ superclassElement.isInterface &&
+ superclassElement.library != _currentLibrary) {
+ errorReporter.reportErrorForNode(
+ CompileTimeErrorCode.INTERFACE_CLASS_EXTENDED_OUTSIDE_OF_LIBRARY,
+ superclass,
+ [superclassElement.name]);
+ }
+ }
+ }
+ if (withClause != null) {
+ for (NamedType withMixin in withClause.mixinTypes) {
+ final withType = withMixin.type;
+ if (withType is InterfaceType) {
+ final withElement = withType.element;
+ if (withElement is MixinElementImpl &&
+ withElement.isInterface &&
+ withElement.library != _currentLibrary) {
+ errorReporter.reportErrorForNode(
+ CompileTimeErrorCode
+ .INTERFACE_MIXIN_MIXED_IN_OUTSIDE_OF_LIBRARY,
+ withMixin,
+ [withElement.name]);
+ }
+ }
+ }
+ }
+ }
+
/// Verify that an 'int' can be assigned to the parameter corresponding to the
/// given [argument]. This is used for prefix and postfix expressions where
/// the argument value is implicit.
diff --git a/pkg/analyzer/messages.yaml b/pkg/analyzer/messages.yaml
index 9943b34..13bae56 100644
--- a/pkg/analyzer/messages.yaml
+++ b/pkg/analyzer/messages.yaml
@@ -6712,6 +6712,18 @@
```dart
var x = BigInt.parse('9223372036854775810');
```
+ INTERFACE_CLASS_EXTENDED_OUTSIDE_OF_LIBRARY:
+ sharedName: INVALID_USE_OF_TYPE_OUTSIDE_LIBRARY
+ problemMessage: "The class '{0}' can't be extended outside of its library because it's an interface class."
+ comment: |-
+ Parameters:
+ 0: the name of the interface class being extended.
+ INTERFACE_MIXIN_MIXED_IN_OUTSIDE_OF_LIBRARY:
+ sharedName: INVALID_USE_OF_TYPE_OUTSIDE_LIBRARY
+ problemMessage: "The mixin '{0}' can't be mixed-in outside of its library because it's an interface mixin."
+ comment: |-
+ Parameters:
+ 0: the name of the interface mixin being mixed in.
INVALID_ANNOTATION:
problemMessage: Annotation must be either a const variable reference or const constructor invocation.
hasPublishedDocs: true
diff --git a/pkg/analyzer/test/src/diagnostics/interface_class_extended_outside_of_library_test.dart b/pkg/analyzer/test/src/diagnostics/interface_class_extended_outside_of_library_test.dart
new file mode 100644
index 0000000..86c3470
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/interface_class_extended_outside_of_library_test.dart
@@ -0,0 +1,81 @@
+// 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(InterfaceClassExtendedOutsideOfLibraryTest);
+ });
+}
+
+@reflectiveTest
+class InterfaceClassExtendedOutsideOfLibraryTest
+ extends PubPackageResolutionTest {
+ test_inside() async {
+ await assertNoErrorsInCode(r'''
+interface class Foo {}
+class Bar extends Foo {}
+''');
+ }
+
+ test_outside() async {
+ newFile('$testPackageLibPath/foo.dart', r'''
+interface class Foo {}
+''');
+
+ await assertErrorsInCode(r'''
+import 'foo.dart';
+class Bar extends Foo {}
+''', [
+ error(CompileTimeErrorCode.INTERFACE_CLASS_EXTENDED_OUTSIDE_OF_LIBRARY,
+ 37, 3),
+ ]);
+ }
+
+ test_outside_viaTypedef_inside() async {
+ newFile('$testPackageLibPath/foo.dart', r'''
+interface class Foo {}
+typedef FooTypedef = Foo;
+''');
+
+ await assertErrorsInCode(r'''
+import 'foo.dart';
+class Bar extends FooTypedef {}
+''', [
+ error(CompileTimeErrorCode.INTERFACE_CLASS_EXTENDED_OUTSIDE_OF_LIBRARY,
+ 37, 10),
+ ]);
+ }
+
+ test_outside_viaTypedef_outside() async {
+ newFile('$testPackageLibPath/foo.dart', r'''
+interface class Foo {}
+''');
+
+ await assertErrorsInCode(r'''
+import 'foo.dart';
+typedef FooTypedef = Foo;
+class Bar extends FooTypedef {}
+''', [
+ error(CompileTimeErrorCode.INTERFACE_CLASS_EXTENDED_OUTSIDE_OF_LIBRARY,
+ 63, 10),
+ ]);
+ }
+
+ test_subtypeOfBase_outside() async {
+ newFile('$testPackageLibPath/foo.dart', r'''
+interface class Foo {}
+class Bar extends Foo {}
+''');
+
+ await assertNoErrorsInCode(r'''
+import 'foo.dart';
+class Bar2 extends Bar {}
+''');
+ }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/interface_mixin_mixed_in_outside_of_library_test.dart b/pkg/analyzer/test/src/diagnostics/interface_mixin_mixed_in_outside_of_library_test.dart
new file mode 100644
index 0000000..7fbe31f
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/interface_mixin_mixed_in_outside_of_library_test.dart
@@ -0,0 +1,132 @@
+// 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(InterfaceMixinMixedInOutsideOfLibraryTest);
+ });
+}
+
+@reflectiveTest
+class InterfaceMixinMixedInOutsideOfLibraryTest
+ extends PubPackageResolutionTest {
+ test_class_inside() async {
+ await assertNoErrorsInCode(r'''
+interface mixin Foo {}
+class Bar with Foo {}
+''');
+ }
+
+ test_class_outside() async {
+ newFile('$testPackageLibPath/foo.dart', r'''
+interface mixin Foo {}
+''');
+
+ await assertErrorsInCode(r'''
+import 'foo.dart';
+class Bar with Foo {}
+''', [
+ error(CompileTimeErrorCode.INTERFACE_MIXIN_MIXED_IN_OUTSIDE_OF_LIBRARY,
+ 34, 3),
+ ]);
+ }
+
+ test_class_outside_viaTypedef_inside() async {
+ newFile('$testPackageLibPath/foo.dart', r'''
+interface mixin Foo {}
+typedef FooTypedef = Foo;
+''');
+
+ await assertErrorsInCode(r'''
+import 'foo.dart';
+class Bar with FooTypedef {}
+''', [
+ error(CompileTimeErrorCode.INTERFACE_MIXIN_MIXED_IN_OUTSIDE_OF_LIBRARY,
+ 34, 10),
+ ]);
+ }
+
+ test_class_outside_viaTypedef_outside() async {
+ newFile('$testPackageLibPath/foo.dart', r'''
+interface mixin Foo {}
+''');
+
+ await assertErrorsInCode(r'''
+import 'foo.dart';
+typedef FooTypedef = Foo;
+class Bar with FooTypedef {}
+''', [
+ error(CompileTimeErrorCode.INTERFACE_MIXIN_MIXED_IN_OUTSIDE_OF_LIBRARY,
+ 60, 10),
+ ]);
+ }
+
+ test_class_subtypeOfBase_outside() async {
+ newFile('$testPackageLibPath/foo.dart', r'''
+interface mixin Foo {}
+class Bar with Foo {}
+''');
+
+ await assertNoErrorsInCode(r'''
+import 'foo.dart';
+class Bar2 extends Bar {}
+''');
+ }
+
+ test_enum_inside() async {
+ await assertNoErrorsInCode(r'''
+interface mixin Foo {}
+enum Bar with Foo { bar }
+''');
+ }
+
+ test_enum_outside() async {
+ newFile('$testPackageLibPath/foo.dart', r'''
+interface mixin Foo {}
+''');
+
+ await assertErrorsInCode(r'''
+import 'foo.dart';
+enum Bar with Foo { bar }
+''', [
+ error(CompileTimeErrorCode.INTERFACE_MIXIN_MIXED_IN_OUTSIDE_OF_LIBRARY,
+ 33, 3),
+ ]);
+ }
+
+ test_enum_outside_viaTypedef_inside() async {
+ newFile('$testPackageLibPath/foo.dart', r'''
+interface mixin Foo {}
+typedef FooTypedef = Foo;
+''');
+
+ await assertErrorsInCode(r'''
+import 'foo.dart';
+enum Bar with FooTypedef { bar }
+''', [
+ error(CompileTimeErrorCode.INTERFACE_MIXIN_MIXED_IN_OUTSIDE_OF_LIBRARY,
+ 33, 10),
+ ]);
+ }
+
+ test_enum_outside_viaTypedef_outside() async {
+ newFile('$testPackageLibPath/foo.dart', r'''
+interface mixin Foo {}
+''');
+
+ await assertErrorsInCode(r'''
+import 'foo.dart';
+typedef FooTypedef = Foo;
+enum Bar with FooTypedef { bar }
+''', [
+ error(CompileTimeErrorCode.INTERFACE_MIXIN_MIXED_IN_OUTSIDE_OF_LIBRARY,
+ 59, 10),
+ ]);
+ }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/test_all.dart b/pkg/analyzer/test/src/diagnostics/test_all.dart
index 1df5085..3436aee 100644
--- a/pkg/analyzer/test/src/diagnostics/test_all.dart
+++ b/pkg/analyzer/test/src/diagnostics/test_all.dart
@@ -359,6 +359,10 @@
import 'integer_literal_imprecise_as_double_test.dart'
as integer_literal_imprecise_as_double;
import 'integer_literal_out_of_range_test.dart' as integer_literal_out_of_range;
+import 'interface_class_extended_outside_of_library_test.dart'
+ as interface_class_extended_outside_of_library;
+import 'interface_mixin_mixed_in_outside_of_library_test.dart'
+ as interface_mixin_mixed_in_outside_of_library;
import 'invalid_annotation_from_deferred_library_test.dart'
as invalid_annotation_from_deferred_library;
import 'invalid_annotation_target_test.dart' as invalid_annotation_target;
@@ -1085,6 +1089,8 @@
instantiate_type_alias_expands_to_type_parameter.main();
integer_literal_imprecise_as_double.main();
integer_literal_out_of_range.main();
+ interface_class_extended_outside_of_library.main();
+ interface_mixin_mixed_in_outside_of_library.main();
invalid_annotation.main();
invalid_annotation_from_deferred_library.main();
invalid_annotation_target.main();
diff --git a/tests/language/class_modifiers/interface/interface_class_extend_outside_error_test.dart b/tests/language/class_modifiers/interface/interface_class_extend_outside_error_test.dart
index e324a46..7360471 100644
--- a/tests/language/class_modifiers/interface/interface_class_extend_outside_error_test.dart
+++ b/tests/language/class_modifiers/interface/interface_class_extend_outside_error_test.dart
@@ -9,12 +9,12 @@
import 'interface_class_extend_lib.dart';
abstract class AOutside extends InterfaceClass {}
-// ^
-// [analyzer] unspecified
+// ^^^^^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.INVALID_USE_OF_TYPE_OUTSIDE_LIBRARY
// [cfe] unspecified
class BOutside extends InterfaceClass {
-// ^
-// [analyzer] unspecified
+// ^^^^^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.INVALID_USE_OF_TYPE_OUTSIDE_LIBRARY
// [cfe] unspecified
}
diff --git a/tests/language/class_modifiers/interface/interface_class_typedef_extend_error_test.dart b/tests/language/class_modifiers/interface/interface_class_typedef_extend_error_test.dart
index 6993363e7..d567d3f 100644
--- a/tests/language/class_modifiers/interface/interface_class_typedef_extend_error_test.dart
+++ b/tests/language/class_modifiers/interface/interface_class_typedef_extend_error_test.dart
@@ -10,6 +10,6 @@
import 'interface_class_typedef_lib.dart';
class ATypeDef extends InterfaceClassTypeDef {}
-// ^
-// [analyzer] unspecified
+// ^^^^^^^^^^^^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.INVALID_USE_OF_TYPE_OUTSIDE_LIBRARY
// [cfe] unspecified
diff --git a/tests/language/class_modifiers/interface/interface_class_typedef_used_outside_error_test.dart b/tests/language/class_modifiers/interface/interface_class_typedef_used_outside_error_test.dart
index e6dadc2..fef1603 100644
--- a/tests/language/class_modifiers/interface/interface_class_typedef_used_outside_error_test.dart
+++ b/tests/language/class_modifiers/interface/interface_class_typedef_used_outside_error_test.dart
@@ -13,6 +13,6 @@
typedef ATypeDef = InterfaceClass;
class A extends ATypeDef {}
-// ^
-// [analyzer] unspecified
+// ^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.INVALID_USE_OF_TYPE_OUTSIDE_LIBRARY
// [cfe] unspecified
diff --git a/tests/language/class_modifiers/interface/interface_mixin_typedef_with_outside_error_test.dart b/tests/language/class_modifiers/interface/interface_mixin_typedef_with_outside_error_test.dart
index 0a01de7..a095423 100644
--- a/tests/language/class_modifiers/interface/interface_mixin_typedef_with_outside_error_test.dart
+++ b/tests/language/class_modifiers/interface/interface_mixin_typedef_with_outside_error_test.dart
@@ -9,11 +9,11 @@
import 'interface_mixin_typedef_with_lib.dart';
abstract class AOutside with InterfaceMixinTypeDef {}
-// ^
-// [analyzer] unspecified
+// ^^^^^^^^^^^^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.INVALID_USE_OF_TYPE_OUTSIDE_LIBRARY
// [cfe] unspecified
class BOutside with InterfaceMixinTypeDef {}
-// ^
-// [analyzer] unspecified
+// ^^^^^^^^^^^^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.INVALID_USE_OF_TYPE_OUTSIDE_LIBRARY
// [cfe] unspecified
diff --git a/tests/language/class_modifiers/interface/interface_mixin_with_outside_error_test.dart b/tests/language/class_modifiers/interface/interface_mixin_with_outside_error_test.dart
index 1b3863e..e60f0aa 100644
--- a/tests/language/class_modifiers/interface/interface_mixin_with_outside_error_test.dart
+++ b/tests/language/class_modifiers/interface/interface_mixin_with_outside_error_test.dart
@@ -9,11 +9,11 @@
import 'interface_mixin_with_lib.dart';
abstract class AOutside with InterfaceMixin {}
-// ^
-// [analyzer] unspecified
+// ^^^^^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.INVALID_USE_OF_TYPE_OUTSIDE_LIBRARY
// [cfe] unspecified
class BOutside with InterfaceMixin {}
-// ^
-// [analyzer] unspecified
+// ^^^^^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.INVALID_USE_OF_TYPE_OUTSIDE_LIBRARY
// [cfe] unspecified