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