[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