Ensure that Object doesn't have supertypes

Change-Id: Ife632785bdac0d26fb6db1463636fd9a1fec51e1
Reviewed-on: https://dart-review.googlesource.com/c/86201
Commit-Queue: Peter von der Ahé <ahe@google.com>
Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
diff --git a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
index ff290e0..0cb1431 100644
--- a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
+++ b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
@@ -6698,6 +6698,28 @@
 }
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Null> codeObjectExtends = messageObjectExtends;
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const MessageCode messageObjectExtends = const MessageCode("ObjectExtends",
+    message: r"""The class 'Object' can't have a superclass.""");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Null> codeObjectImplements = messageObjectImplements;
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const MessageCode messageObjectImplements = const MessageCode(
+    "ObjectImplements",
+    message: r"""The class 'Object' can't implement anything.""");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Null> codeObjectMixesIn = messageObjectMixesIn;
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const MessageCode messageObjectMixesIn = const MessageCode("ObjectMixesIn",
+    message: r"""The class 'Object' can't use mixins.""");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Code<Null> codeOnlyTry = messageOnlyTry;
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_class_builder.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_class_builder.dart
index 873aa85..bc49b59 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_class_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_class_builder.dart
@@ -646,9 +646,9 @@
           isInterfaceCheck: true);
     }
 
-    bool hasNoSuchMethod =
-        hierarchy.getDispatchTarget(cls, noSuchMethodName).enclosingClass !=
-            coreTypes.objectClass;
+    Member noSuchMethod = hierarchy.getDispatchTarget(cls, noSuchMethodName);
+    bool hasNoSuchMethod = noSuchMethod != null &&
+        noSuchMethod.enclosingClass != coreTypes.objectClass;
 
     void findMissingImplementations({bool setters}) {
       List<Member> dispatchTargets =
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
index cea63eb..ad36388 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
@@ -271,7 +271,7 @@
       loader.resolveTypes();
       loader.computeDefaultTypes(dynamicType, bottomType, objectClassBuilder);
       List<SourceClassBuilder> myClasses = collectMyClasses();
-      loader.checkSemantics(myClasses);
+      loader.checkSemantics(myClasses, objectClassBuilder);
       loader.finishTypeVariables(objectClassBuilder, dynamicType);
       loader.buildComponent();
       installDefaultSupertypes();
diff --git a/pkg/front_end/lib/src/fasta/source/source_loader.dart b/pkg/front_end/lib/src/fasta/source/source_loader.dart
index a330137..5edde47 100644
--- a/pkg/front_end/lib/src/fasta/source/source_loader.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_loader.dart
@@ -47,6 +47,9 @@
         Message,
         SummaryTemplate,
         Template,
+        messageObjectExtends,
+        messageObjectImplements,
+        messageObjectMixesIn,
         messagePartOrphan,
         noLength,
         templateAmbiguousSupertypes,
@@ -552,7 +555,32 @@
     return output;
   }
 
-  void checkSemantics(List<SourceClassBuilder> classes) {
+  /// Check that [objectClass] has no supertypes. Recover by removing any
+  /// found.
+  void checkObjectClassHierarchy(ClassBuilder objectClass) {
+    if (objectClass is SourceClassBuilder &&
+        objectClass.library.loader == this) {
+      if (objectClass.supertype != null) {
+        objectClass.supertype = null;
+        objectClass.addProblem(
+            messageObjectExtends, objectClass.charOffset, noLength);
+      }
+      if (objectClass.interfaces != null) {
+        objectClass.addProblem(
+            messageObjectImplements, objectClass.charOffset, noLength);
+        objectClass.interfaces = null;
+      }
+      if (objectClass.mixedInType != null) {
+        objectClass.addProblem(
+            messageObjectMixesIn, objectClass.charOffset, noLength);
+        objectClass.mixedInType = null;
+      }
+    }
+  }
+
+  void checkSemantics(
+      List<SourceClassBuilder> classes, ClassBuilder objectClass) {
+    checkObjectClassHierarchy(objectClass);
     Iterable<ClassBuilder> candidates = cyclicCandidates(classes);
     if (candidates.isNotEmpty) {
       Map<ClassBuilder, Set<ClassBuilder>> realCycles =
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index 00f7e31..86aad34 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -3364,3 +3364,18 @@
   template: "'#name' is from '#uri' ('#uri2')."
   frontendInternal: true
   external: test/type_labeler_test.dart
+
+ObjectExtends:
+  template: "The class 'Object' can't have a superclass."
+  frontendInternal: true
+  external: test/fasta/object_supertype_test.dart
+
+ObjectImplements:
+  template: "The class 'Object' can't implement anything."
+  frontendInternal: true
+  external: test/fasta/object_supertype_test.dart
+
+ObjectMixesIn:
+  template: "The class 'Object' can't use mixins."
+  frontendInternal: true
+  external: test/fasta/object_supertype_test.dart
diff --git a/pkg/front_end/test/fasta/object_supertype_test.dart b/pkg/front_end/test/fasta/object_supertype_test.dart
new file mode 100644
index 0000000..fd00300
--- /dev/null
+++ b/pkg/front_end/test/fasta/object_supertype_test.dart
@@ -0,0 +1,101 @@
+// Copyright (c) 2018, 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 "dart:convert" show json;
+
+import "package:async_helper/async_helper.dart" show asyncTest;
+
+import "package:expect/expect.dart" show Expect;
+
+import "package:front_end/src/api_prototype/compiler_options.dart"
+    show CompilerOptions;
+
+import "package:front_end/src/api_prototype/diagnostic_message.dart"
+    show DiagnosticMessage, getMessageCodeObject;
+
+import "package:front_end/src/api_prototype/memory_file_system.dart"
+    show MemoryFileSystem;
+
+import "package:front_end/src/base/processed_options.dart"
+    show ProcessedOptions;
+
+import "package:front_end/src/fasta/compiler_context.dart" show CompilerContext;
+
+import "package:front_end/src/fasta/messages.dart"
+    show Code, codeObjectExtends, codeObjectImplements, codeObjectMixesIn;
+
+import "package:front_end/src/fasta/source/source_loader.dart"
+    show defaultDartCoreSource;
+
+import "package:front_end/src/fasta/ticker.dart" show Ticker;
+
+import "../../tool/_fasta/entry_points.dart" show CompileTask;
+
+Future<List<DiagnosticMessage>> outline(String objectHeader) async {
+  final Ticker ticker = new Ticker(isVerbose: false);
+  final Uri base = Uri.parse("org-dartlang-test:///");
+
+  final MemoryFileSystem fs = new MemoryFileSystem(base);
+
+  final Uri librariesSpecificationUri = base.resolve("sdk/libraries.json");
+
+  fs.entityForUri(librariesSpecificationUri).writeAsStringSync(json.encode({
+        "none": {
+          "libraries": {
+            "core": {
+              "uri": "lib/core/core.dart",
+            },
+          },
+        },
+      }));
+
+  fs.entityForUri(base.resolve("sdk/lib/core/core.dart")).writeAsStringSync(
+      defaultDartCoreSource.replaceAll("class Object {", "$objectHeader"));
+
+  final List<DiagnosticMessage> messages = <DiagnosticMessage>[];
+
+  CompilerContext context = new CompilerContext(new ProcessedOptions(
+      options: new CompilerOptions()
+        ..onDiagnostic = messages.add
+        ..sdkRoot = base.resolve("sdk/")
+        ..fileSystem = fs
+        ..compileSdk = true
+        ..librariesSpecificationUri = librariesSpecificationUri,
+      inputs: [Uri.parse("dart:core")]));
+
+  await context.runInContext<void>((_) async {
+    CompileTask task = new CompileTask(context, ticker);
+    await task.buildOutline();
+  });
+  return messages;
+}
+
+test() async {
+  Set<String> normalErrors = (await outline("class Object {"))
+      .map((DiagnosticMessage message) => getMessageCodeObject(message).name)
+      .toSet();
+
+  check(String objectHeader, List<Code> expectedCodes) async {
+    List<DiagnosticMessage> messages = (await outline(objectHeader))
+        .where((DiagnosticMessage message) =>
+            !normalErrors.contains(getMessageCodeObject(message).name))
+        .toList();
+    Expect.setEquals(
+        expectedCodes,
+        messages.map((DiagnosticMessage m) => getMessageCodeObject(m)),
+        objectHeader);
+  }
+
+  await check("class Object extends String {", <Code>[codeObjectExtends]);
+
+  await check(
+      "class Object implements String, bool {", <Code>[codeObjectImplements]);
+
+  await check("class Object = Object with bool ; class Blah {",
+      <Code>[codeObjectExtends, codeObjectMixesIn]);
+}
+
+main() {
+  asyncTest(test);
+}