d7f200b97d
(Part of https://github.com/dart-lang/sdk/issues/63288) This change migrates the kernel package to use the new constructor declaration syntax, described in https://github.com/dart-lang/language/blob/main/accepted/future-releases/primary-constructors/feature-specification.md#abbreviations-of-in-body-constructor-declarations. This change was performed in an automated fashion, by (a) bumping the package's SDK constraint to `3.13.0-0`, (b) enabling the lints `unnecessary_type_name_in_constructor` and `unnecessary_const_in_enum_constructor`, (c) fixing the resulting lint failures using `dart fix`, and then (d) reformatting the affected files. To ease code review, I've reverted unrelated formatting changes. Change-Id: I8d32a0b26450a44dfa2ebd9fe2dff9df6a6a6964 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/508426 Reviewed-by: Konstantin Shcheglov <scheglov@google.com> Commit-Queue: Paul Berry <paulberry@google.com>
1807 lines
51 KiB
Dart
1807 lines
51 KiB
Dart
// Copyright (c) 2017, 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:expect/legacy/minitest.dart"; // ignore: deprecated_member_use
|
|
|
|
import "package:kernel/ast.dart";
|
|
import "package:kernel/class_hierarchy.dart";
|
|
import "package:kernel/core_types.dart";
|
|
import "package:kernel/testing/mock_sdk_component.dart";
|
|
import "package:kernel/text/ast_to_text.dart";
|
|
import "package:kernel/src/text_util.dart";
|
|
|
|
void main() {
|
|
new ClosedWorldClassHierarchyTest().test_applyTreeChanges();
|
|
|
|
new ClosedWorldClassHierarchyTest().test_applyMemberChanges();
|
|
|
|
new ClosedWorldClassHierarchyTest()
|
|
.test_getSingleTargetForInterfaceInvocation();
|
|
|
|
new ClosedWorldClassHierarchyTest().test_getSubtypesOf();
|
|
|
|
new ClosedWorldClassHierarchyTest()
|
|
.test_forEachOverridePair_supertypeOverridesInterface();
|
|
|
|
new ClosedWorldClassHierarchyTest()
|
|
.test_forEachOverridePair_supertypeOverridesThis();
|
|
|
|
new ClosedWorldClassHierarchyTest()
|
|
.test_forEachOverridePair_supertypeOverridesThisAbstract();
|
|
|
|
new ClosedWorldClassHierarchyTest()
|
|
.test_forEachOverridePair_thisOverridesSupertype();
|
|
|
|
new ClosedWorldClassHierarchyTest()
|
|
.test_forEachOverridePair_thisOverridesSupertype_setter();
|
|
|
|
new ClosedWorldClassHierarchyTest()
|
|
.test_getClassAsInstanceOf_generic_extends();
|
|
|
|
new ClosedWorldClassHierarchyTest()
|
|
.test_getClassAsInstanceOf_generic_implements();
|
|
|
|
new ClosedWorldClassHierarchyTest().test_getClassAsInstanceOf_generic_with();
|
|
|
|
new ClosedWorldClassHierarchyTest()
|
|
.test_getClassAsInstanceOf_notGeneric_extends();
|
|
|
|
new ClosedWorldClassHierarchyTest()
|
|
.test_getClassAsInstanceOf_notGeneric_implements();
|
|
|
|
new ClosedWorldClassHierarchyTest()
|
|
.test_getClassAsInstanceOf_notGeneric_with();
|
|
|
|
new ClosedWorldClassHierarchyTest().test_getDeclaredMembers();
|
|
|
|
new ClosedWorldClassHierarchyTest().test_getDispatchTarget();
|
|
|
|
new ClosedWorldClassHierarchyTest().test_getDispatchTarget_abstract();
|
|
|
|
new ClosedWorldClassHierarchyTest().test_getInterfaceMember_extends();
|
|
|
|
new ClosedWorldClassHierarchyTest().test_getInterfaceMember_implements();
|
|
|
|
new ClosedWorldClassHierarchyTest().test_getInterfaceMembers_in_class();
|
|
|
|
new ClosedWorldClassHierarchyTest()
|
|
.test_getInterfaceMembers_inherited_or_mixed_in();
|
|
|
|
new ClosedWorldClassHierarchyTest().test_getInterfaceMembers_multiple();
|
|
|
|
new ClosedWorldClassHierarchyTest().test_getInterfaceMembers_shadowed();
|
|
|
|
new ClosedWorldClassHierarchyTest().test_getOrderedClasses();
|
|
|
|
new ClosedWorldClassHierarchyTest()
|
|
.test_getTypeAsInstanceOf_generic_extends();
|
|
}
|
|
|
|
class ClosedWorldClassHierarchyTest {
|
|
final Component component = createMockSdkComponent();
|
|
late CoreTypes coreTypes;
|
|
|
|
late Library library;
|
|
|
|
ClassHierarchy? _hierarchy;
|
|
|
|
new() {
|
|
coreTypes = new CoreTypes(component);
|
|
Uri uri = Uri.parse('org-dartlang:///test.dart');
|
|
library = new Library(uri, fileUri: uri, name: 'test');
|
|
library.parent = component;
|
|
component.libraries.add(library);
|
|
}
|
|
|
|
ClassHierarchy createClassHierarchy(Component component) {
|
|
return new ClassHierarchy(component, coreTypes);
|
|
}
|
|
|
|
void test_applyTreeChanges() {
|
|
Class a = addClass(
|
|
new Class(name: 'A', supertype: objectSuper, fileUri: library.fileUri),
|
|
);
|
|
_assertLibraryText(library, '''
|
|
class A {}
|
|
''');
|
|
|
|
Uri uriB = Uri.parse('org-dartlang:///test_b.dart');
|
|
Class b = new Class(
|
|
name: 'B',
|
|
supertype: a.asThisSupertype,
|
|
fileUri: library.fileUri,
|
|
);
|
|
Library libWithB = new Library(uriB, fileUri: uriB, name: 'test_b');
|
|
libWithB.parent = component;
|
|
component.libraries.add(libWithB);
|
|
libWithB.addClass(b);
|
|
_assertLibraryText(libWithB, '''
|
|
library test_b;
|
|
import self as self;
|
|
import "test.dart" as test;
|
|
|
|
class B extends test::A { // from org-dartlang:///test.dart
|
|
}
|
|
''');
|
|
|
|
// No updated classes, the same hierarchy.
|
|
expect(hierarchy.applyTreeChanges([], [], []), same(hierarchy));
|
|
|
|
// Has updated classes, still the same hierarchy (instance). Can answer
|
|
// queries about the new classes.
|
|
var c = new Class(
|
|
name: 'C',
|
|
supertype: a.asThisSupertype,
|
|
fileUri: library.fileUri,
|
|
);
|
|
Uri uriC = Uri.parse('org-dartlang:///test2.dart');
|
|
Library libWithC = new Library(uriC, fileUri: uriC, name: 'test2');
|
|
libWithC.parent = component;
|
|
component.libraries.add(libWithC);
|
|
libWithC.addClass(c);
|
|
|
|
expect(
|
|
hierarchy.applyTreeChanges([libWithB], [libWithC], []),
|
|
same(hierarchy),
|
|
);
|
|
expect(hierarchy.isSubclassOf(a, c), false);
|
|
expect(hierarchy.isSubclassOf(c, a), true);
|
|
|
|
// Remove C - after this C should be unknown.
|
|
expect(hierarchy.applyTreeChanges([libWithC], [], []), same(hierarchy));
|
|
expectThrows(() => hierarchy.isSubclassOf(a, c));
|
|
expectThrows(() => hierarchy.isSubclassOf(c, a));
|
|
}
|
|
|
|
void expectThrows(Function() f) {
|
|
bool hasThrown = false;
|
|
try {
|
|
f();
|
|
} catch (e) {
|
|
hasThrown = true;
|
|
}
|
|
if (!hasThrown) {
|
|
throw "Expected the function to throw an exception.";
|
|
}
|
|
}
|
|
|
|
void test_applyMemberChanges() {
|
|
var methodA1 = newEmptyMethod('memberA1');
|
|
var methodA2 = newEmptyMethod('memberA2');
|
|
var methodA3 = newEmptyMethod('memberA3');
|
|
var methodB1 = newEmptyMethod('memberB1');
|
|
|
|
var a = addClass(
|
|
new Class(
|
|
name: 'A',
|
|
supertype: objectSuper,
|
|
procedures: [methodA1, methodA2],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
var b = addClass(
|
|
new Class(
|
|
name: 'B',
|
|
supertype: a.asThisSupertype,
|
|
procedures: [methodB1],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
|
|
_assertTestLibraryText('''
|
|
class A {
|
|
method memberA1() → void {}
|
|
method memberA2() → void {}
|
|
}
|
|
class B extends self::A {
|
|
method memberB1() → void {}
|
|
}
|
|
''');
|
|
|
|
// No changes: B has memberA1, memberA2 and memberB1;
|
|
// A has memberA1 and memberA2
|
|
expect(
|
|
hierarchy.getDispatchTargets(b),
|
|
unorderedEquals([methodA1, methodA2, methodB1]),
|
|
);
|
|
expect(
|
|
hierarchy.getDispatchTargets(a),
|
|
unorderedEquals([methodA1, methodA2]),
|
|
);
|
|
|
|
// Add a member to A, but only update A.
|
|
a.addProcedure(methodA3);
|
|
hierarchy.applyMemberChanges([a]);
|
|
expect(
|
|
hierarchy.getDispatchTargets(b),
|
|
unorderedEquals([methodA1, methodA2, methodB1]),
|
|
);
|
|
expect(
|
|
hierarchy.getDispatchTargets(a),
|
|
unorderedEquals([methodA1, methodA2, methodA3]),
|
|
);
|
|
|
|
// Apply member changes again, this time telling the hierarchy to find
|
|
// descendants.
|
|
hierarchy.applyMemberChanges([a], findDescendants: true);
|
|
expect(
|
|
hierarchy.getDispatchTargets(b),
|
|
unorderedEquals([methodA1, methodA2, methodA3, methodB1]),
|
|
);
|
|
expect(
|
|
hierarchy.getDispatchTargets(a),
|
|
unorderedEquals([methodA1, methodA2, methodA3]),
|
|
);
|
|
}
|
|
|
|
void test_getSingleTargetForInterfaceInvocation() {
|
|
var methodInA = newEmptyMethod('foo', isAbstract: true);
|
|
var methodInB = newEmptyMethod('foo');
|
|
var methodInD = newEmptyMethod('foo');
|
|
var methodInE = newEmptyMethod('foo');
|
|
|
|
var a = addClass(
|
|
new Class(
|
|
name: 'A',
|
|
supertype: objectSuper,
|
|
procedures: [methodInA],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
var b = addClass(
|
|
new Class(
|
|
name: 'B',
|
|
isAbstract: true,
|
|
supertype: objectSuper,
|
|
procedures: [methodInB],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
var c = addClass(
|
|
new Class(
|
|
name: 'C',
|
|
supertype: b.asThisSupertype,
|
|
implementedTypes: [a.asThisSupertype],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
addClass(
|
|
new Class(
|
|
name: 'D',
|
|
supertype: b.asThisSupertype,
|
|
procedures: [methodInD],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
addClass(
|
|
new Class(
|
|
name: 'E',
|
|
isAbstract: true,
|
|
supertype: objectSuper,
|
|
implementedTypes: [c.asThisSupertype],
|
|
procedures: [methodInE],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
|
|
_assertTestLibraryText('''
|
|
class A {
|
|
abstract method foo() → void;
|
|
}
|
|
abstract class B {
|
|
method foo() → void {}
|
|
}
|
|
class C extends self::B implements self::A {}
|
|
class D extends self::B {
|
|
method foo() → void {}
|
|
}
|
|
abstract class E implements self::C {
|
|
method foo() → void {}
|
|
}
|
|
''');
|
|
|
|
ClosedWorldClassHierarchy cwch = hierarchy as ClosedWorldClassHierarchy;
|
|
ClassHierarchySubtypes cwchst = cwch.computeSubtypesInformation();
|
|
|
|
expect(cwchst.getSingleTargetForInterfaceInvocation(methodInA), methodInB);
|
|
expect(
|
|
cwchst.getSingleTargetForInterfaceInvocation(methodInB),
|
|
null,
|
|
); // B::foo and D::foo
|
|
expect(cwchst.getSingleTargetForInterfaceInvocation(methodInD), methodInD);
|
|
expect(
|
|
cwchst.getSingleTargetForInterfaceInvocation(methodInE),
|
|
null,
|
|
); // no concrete subtypes
|
|
}
|
|
|
|
void test_getSubtypesOf() {
|
|
var a = addClass(
|
|
new Class(name: 'A', supertype: objectSuper, fileUri: library.fileUri),
|
|
);
|
|
var b = addClass(
|
|
new Class(name: 'B', supertype: objectSuper, fileUri: library.fileUri),
|
|
);
|
|
var c = addClass(
|
|
new Class(name: 'C', supertype: objectSuper, fileUri: library.fileUri),
|
|
);
|
|
|
|
var d = addClass(
|
|
new Class(
|
|
name: 'D',
|
|
supertype: a.asThisSupertype,
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
|
|
var e = addClass(
|
|
new Class(
|
|
name: 'E',
|
|
supertype: b.asThisSupertype,
|
|
implementedTypes: [c.asThisSupertype],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
|
|
var f = addClass(
|
|
new Class(
|
|
name: 'F',
|
|
supertype: e.asThisSupertype,
|
|
implementedTypes: [a.asThisSupertype],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
|
|
var g = addClass(
|
|
new Class(name: 'G', supertype: objectSuper, fileUri: library.fileUri),
|
|
);
|
|
|
|
var h = addClass(
|
|
new Class(
|
|
name: 'H',
|
|
supertype: g.asThisSupertype,
|
|
implementedTypes: [c.asThisSupertype, a.asThisSupertype],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
|
|
_assertTestLibraryText('''
|
|
class A {}
|
|
class B {}
|
|
class C {}
|
|
class D extends self::A {}
|
|
class E extends self::B implements self::C {}
|
|
class F extends self::E implements self::A {}
|
|
class G {}
|
|
class H extends self::G implements self::C, self::A {}
|
|
''');
|
|
|
|
ClosedWorldClassHierarchy cwch = hierarchy as ClosedWorldClassHierarchy;
|
|
ClassHierarchySubtypes cwchst = cwch.computeSubtypesInformation();
|
|
|
|
expect(cwchst.getSubtypesOf(a), unorderedEquals([a, d, f, h]));
|
|
expect(cwchst.getSubtypesOf(b), unorderedEquals([b, e, f]));
|
|
expect(cwchst.getSubtypesOf(c), unorderedEquals([c, e, f, h]));
|
|
expect(cwchst.getSubtypesOf(d), unorderedEquals([d]));
|
|
expect(cwchst.getSubtypesOf(e), unorderedEquals([e, f]));
|
|
expect(cwchst.getSubtypesOf(f), unorderedEquals([f]));
|
|
expect(cwchst.getSubtypesOf(g), unorderedEquals([g, h]));
|
|
expect(cwchst.getSubtypesOf(h), unorderedEquals([h]));
|
|
|
|
// Update hierarchy.
|
|
expect(
|
|
hierarchy.applyTreeChanges([library], [library], []),
|
|
same(hierarchy),
|
|
);
|
|
|
|
// The existing ClassHierarchySubtypes should be invalidated.
|
|
expect((cwchst as dynamic).invalidated, true);
|
|
|
|
// Asking for it again gives one that works.
|
|
cwchst = cwch.computeSubtypesInformation();
|
|
expect(cwchst.getSubtypesOf(a), unorderedEquals([a, d, f, h]));
|
|
expect(cwchst.getSubtypesOf(b), unorderedEquals([b, e, f]));
|
|
expect(cwchst.getSubtypesOf(c), unorderedEquals([c, e, f, h]));
|
|
expect(cwchst.getSubtypesOf(d), unorderedEquals([d]));
|
|
expect(cwchst.getSubtypesOf(e), unorderedEquals([e, f]));
|
|
expect(cwchst.getSubtypesOf(f), unorderedEquals([f]));
|
|
expect(cwchst.getSubtypesOf(g), unorderedEquals([g, h]));
|
|
expect(cwchst.getSubtypesOf(h), unorderedEquals([h]));
|
|
}
|
|
|
|
/// Return the new or existing instance of [ClassHierarchy].
|
|
ClassHierarchy get hierarchy {
|
|
return _hierarchy ??= createClassHierarchy(component);
|
|
}
|
|
|
|
Class get objectClass => coreTypes.objectClass;
|
|
|
|
Supertype get objectSuper => coreTypes.objectClass.asThisSupertype;
|
|
|
|
Class addClass(Class c) {
|
|
if (_hierarchy != null) {
|
|
fail('The class hierarchy has already been created.');
|
|
}
|
|
library.addClass(c);
|
|
return c;
|
|
}
|
|
|
|
/// Add a new generic class with the given [name] and [typeParameterNames].
|
|
/// The [TypeParameterType]s corresponding to [typeParameterNames] are
|
|
/// passed to optional [extends_] and [implements_] callbacks.
|
|
Class addGenericClass(
|
|
String name,
|
|
List<String> typeParameterNames, {
|
|
Supertype Function(List<DartType> typeParameterTypes)? extends_,
|
|
List<Supertype> Function(List<DartType> typeParameterTypes)? implements_,
|
|
}) {
|
|
var typeParameters = typeParameterNames
|
|
.map(
|
|
(name) => new TypeParameter(
|
|
name,
|
|
coreTypes.objectNullableRawType,
|
|
const DynamicType(),
|
|
),
|
|
)
|
|
.toList();
|
|
var typeParameterTypes = typeParameters
|
|
.map(
|
|
(parameter) =>
|
|
new TypeParameterType(parameter, Nullability.undetermined),
|
|
)
|
|
.toList();
|
|
var supertype = extends_ != null
|
|
? extends_(typeParameterTypes)
|
|
: objectSuper;
|
|
var implementedTypes = implements_ != null
|
|
? implements_(typeParameterTypes)
|
|
: <Supertype>[];
|
|
return addClass(
|
|
new Class(
|
|
name: name,
|
|
typeParameters: typeParameters,
|
|
supertype: supertype,
|
|
implementedTypes: implementedTypes,
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
}
|
|
|
|
Procedure newEmptyGetter(
|
|
String name, {
|
|
DartType returnType = const DynamicType(),
|
|
bool isAbstract = false,
|
|
}) {
|
|
var body = isAbstract
|
|
? null
|
|
: new Block([new ReturnStatement(new NullLiteral())]);
|
|
return new Procedure(
|
|
new Name(name),
|
|
ProcedureKind.Getter,
|
|
new FunctionNode(body, returnType: returnType),
|
|
fileUri: library.fileUri,
|
|
);
|
|
}
|
|
|
|
Procedure newEmptyMethod(String name, {bool isAbstract = false}) {
|
|
var body = isAbstract ? null : new Block([]);
|
|
return new Procedure(
|
|
new Name(name),
|
|
ProcedureKind.Method,
|
|
new FunctionNode(body, returnType: const VoidType()),
|
|
isAbstract: isAbstract,
|
|
fileUri: library.fileUri,
|
|
);
|
|
}
|
|
|
|
Procedure newEmptySetter(
|
|
String name, {
|
|
bool isAbstract = false,
|
|
DartType type = const DynamicType(),
|
|
}) {
|
|
var body = isAbstract ? null : new Block([]);
|
|
return new Procedure(
|
|
new Name(name),
|
|
ProcedureKind.Setter,
|
|
new FunctionNode(
|
|
body,
|
|
returnType: const VoidType(),
|
|
positionalParameters: [new Variable('_', type: type)],
|
|
),
|
|
fileUri: library.fileUri,
|
|
);
|
|
}
|
|
|
|
/// 2. A non-abstract member is inherited from a superclass, and in the
|
|
/// context of this class, it overrides an abstract member inheritable through
|
|
/// one of its superinterfaces.
|
|
void test_forEachOverridePair_supertypeOverridesInterface() {
|
|
var a = addClass(
|
|
new Class(
|
|
name: 'A',
|
|
supertype: objectSuper,
|
|
procedures: [newEmptyMethod('foo'), newEmptyMethod('bar')],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
var b = addClass(
|
|
new Class(
|
|
name: 'B',
|
|
supertype: a.asThisSupertype,
|
|
procedures: [newEmptyMethod('foo', isAbstract: true)],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
var c = addClass(
|
|
new Class(
|
|
name: 'C',
|
|
supertype: a.asThisSupertype,
|
|
implementedTypes: [b.asThisSupertype],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
var d = addClass(
|
|
new Class(name: 'D', supertype: objectSuper, fileUri: library.fileUri),
|
|
);
|
|
var e = addClass(
|
|
new Class(
|
|
name: 'E',
|
|
supertype: d.asThisSupertype,
|
|
mixedInType: a.asThisSupertype,
|
|
implementedTypes: [b.asThisSupertype],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
|
|
_assertTestLibraryText('''
|
|
class A {
|
|
method foo() → void {}
|
|
method bar() → void {}
|
|
}
|
|
class B extends self::A {
|
|
abstract method foo() → void;
|
|
}
|
|
class C extends self::A implements self::B {}
|
|
class D {}
|
|
class E = self::D with self::A implements self::B {}
|
|
''');
|
|
|
|
_assertOverridePairs(c, []);
|
|
_assertOverridePairs(e, ['test::A.foo overrides test::B.foo']);
|
|
}
|
|
|
|
/// An abstract member declared in the class is overridden by a member in
|
|
/// one of the interfaces.
|
|
void test_forEachOverridePair_supertypeOverridesThis() {
|
|
var a = addClass(
|
|
new Class(
|
|
name: 'A',
|
|
supertype: objectSuper,
|
|
procedures: [newEmptyMethod('foo')],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
var b = addClass(
|
|
new Class(
|
|
name: 'B',
|
|
supertype: a.asThisSupertype,
|
|
procedures: [newEmptyMethod('foo', isAbstract: true)],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
var c = addClass(
|
|
new Class(
|
|
name: 'C',
|
|
supertype: a.asThisSupertype,
|
|
procedures: [newEmptyMethod('foo', isAbstract: true)],
|
|
isAbstract: true,
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
var d = addClass(
|
|
new Class(
|
|
name: 'D',
|
|
supertype: b.asThisSupertype,
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
var e = addClass(
|
|
new Class(
|
|
name: 'E',
|
|
supertype: c.asThisSupertype,
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
|
|
_assertTestLibraryText('''
|
|
class A {
|
|
method foo() → void {}
|
|
}
|
|
class B extends self::A {
|
|
abstract method foo() → void;
|
|
}
|
|
abstract class C extends self::A {
|
|
abstract method foo() → void;
|
|
}
|
|
class D extends self::B {}
|
|
class E extends self::C {}
|
|
''');
|
|
|
|
_assertOverridePairs(b, ['test::B.foo overrides test::A.foo']);
|
|
_assertOverridePairs(c, ['test::C.foo overrides test::A.foo']);
|
|
_assertOverridePairs(d, []);
|
|
_assertOverridePairs(e, []);
|
|
}
|
|
|
|
/// 3. A non-abstract member is inherited from a superclass, and it overrides
|
|
/// an abstract member declared in this class.
|
|
void test_forEachOverridePair_supertypeOverridesThisAbstract() {
|
|
var a = addClass(
|
|
new Class(
|
|
name: 'A',
|
|
supertype: objectSuper,
|
|
procedures: [newEmptyMethod('foo'), newEmptyMethod('bar')],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
var b = addClass(
|
|
new Class(
|
|
name: 'B',
|
|
supertype: a.asThisSupertype,
|
|
procedures: [newEmptyMethod('foo', isAbstract: true)],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
|
|
_assertTestLibraryText('''
|
|
class A {
|
|
method foo() → void {}
|
|
method bar() → void {}
|
|
}
|
|
class B extends self::A {
|
|
abstract method foo() → void;
|
|
}
|
|
''');
|
|
|
|
// The documentation says:
|
|
// It is possible for two methods to override one another in both
|
|
// directions.
|
|
_assertOverridePairs(b, ['test::B.foo overrides test::A.foo']);
|
|
}
|
|
|
|
/// 1. A member declared in the class overrides a member inheritable through
|
|
/// one of the supertypes of the class.
|
|
void test_forEachOverridePair_thisOverridesSupertype() {
|
|
var a = addClass(
|
|
new Class(
|
|
name: 'A',
|
|
supertype: objectSuper,
|
|
procedures: [newEmptyMethod('foo'), newEmptyMethod('bar')],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
var b = addClass(
|
|
new Class(
|
|
name: 'B',
|
|
supertype: a.asThisSupertype,
|
|
procedures: [newEmptyMethod('foo')],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
var c = addClass(
|
|
new Class(
|
|
name: 'C',
|
|
supertype: b.asThisSupertype,
|
|
procedures: [newEmptyMethod('bar')],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
|
|
_assertTestLibraryText('''
|
|
class A {
|
|
method foo() → void {}
|
|
method bar() → void {}
|
|
}
|
|
class B extends self::A {
|
|
method foo() → void {}
|
|
}
|
|
class C extends self::B {
|
|
method bar() → void {}
|
|
}
|
|
''');
|
|
|
|
_assertOverridePairs(b, ['test::B.foo overrides test::A.foo']);
|
|
_assertOverridePairs(c, ['test::C.bar overrides test::A.bar']);
|
|
}
|
|
|
|
/// 1. A member declared in the class overrides a member inheritable through
|
|
/// one of the supertypes of the class.
|
|
void test_forEachOverridePair_thisOverridesSupertype_setter() {
|
|
var a = addClass(
|
|
new Class(
|
|
name: 'A',
|
|
supertype: objectSuper,
|
|
procedures: [newEmptySetter('foo'), newEmptySetter('bar')],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
var b = addClass(
|
|
new Class(
|
|
name: 'B',
|
|
supertype: a.asThisSupertype,
|
|
procedures: [newEmptySetter('foo')],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
var c = addClass(
|
|
new Class(
|
|
name: 'C',
|
|
supertype: b.asThisSupertype,
|
|
procedures: [newEmptySetter('bar')],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
|
|
_assertTestLibraryText('''
|
|
class A {
|
|
set foo(dynamic _) → void {}
|
|
set bar(dynamic _) → void {}
|
|
}
|
|
class B extends self::A {
|
|
set foo(dynamic _) → void {}
|
|
}
|
|
class C extends self::B {
|
|
set bar(dynamic _) → void {}
|
|
}
|
|
''');
|
|
|
|
_assertOverridePairs(b, ['test::B.foo= overrides test::A.foo=']);
|
|
_assertOverridePairs(c, ['test::C.bar= overrides test::A.bar=']);
|
|
}
|
|
|
|
void test_getClassAsInstanceOf_generic_extends() {
|
|
var int = coreTypes.intNonNullableRawType;
|
|
var bool = coreTypes.boolNonNullableRawType;
|
|
|
|
var a = addGenericClass('A', ['T', 'U']);
|
|
|
|
var bT = new TypeParameter(
|
|
'T',
|
|
coreTypes.objectNullableRawType,
|
|
const DynamicType(),
|
|
);
|
|
var bTT = new TypeParameterType(bT, Nullability.undetermined);
|
|
var b = addClass(
|
|
new Class(
|
|
name: 'B',
|
|
typeParameters: [bT],
|
|
supertype: new Supertype(a, [bTT, bool]),
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
|
|
var c = addClass(
|
|
new Class(
|
|
name: 'C',
|
|
supertype: new Supertype(b, [int]),
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
|
|
_assertTestLibraryText('''
|
|
class A<T? = dynamic, U? = dynamic> {}
|
|
class B<T? = dynamic> extends self::A<self::B::T%, core::bool> {}
|
|
class C extends self::B<core::int> {}
|
|
''');
|
|
|
|
expect(hierarchy.getClassAsInstanceOf(a, objectClass), objectSuper);
|
|
expect(hierarchy.getClassAsInstanceOf(a, a), a.asThisSupertype);
|
|
expect(hierarchy.getClassAsInstanceOf(b, a), new Supertype(a, [bTT, bool]));
|
|
expect(hierarchy.getClassAsInstanceOf(c, b), new Supertype(b, [int]));
|
|
expect(hierarchy.getClassAsInstanceOf(c, a), new Supertype(a, [int, bool]));
|
|
}
|
|
|
|
void test_getClassAsInstanceOf_generic_implements() {
|
|
var int = coreTypes.intNonNullableRawType;
|
|
var bool = coreTypes.boolNonNullableRawType;
|
|
|
|
var a = addGenericClass('A', ['T', 'U']);
|
|
|
|
var bT = new TypeParameter(
|
|
'T',
|
|
coreTypes.objectNullableRawType,
|
|
const DynamicType(),
|
|
);
|
|
var bTT = new TypeParameterType(bT, Nullability.undetermined);
|
|
var b = addClass(
|
|
new Class(
|
|
name: 'B',
|
|
typeParameters: [bT],
|
|
supertype: objectSuper,
|
|
implementedTypes: [
|
|
new Supertype(a, [bTT, bool]),
|
|
],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
|
|
var c = addClass(
|
|
new Class(
|
|
name: 'C',
|
|
supertype: objectSuper,
|
|
implementedTypes: [
|
|
new Supertype(b, [int]),
|
|
],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
|
|
_assertTestLibraryText('''
|
|
class A<T? = dynamic, U? = dynamic> {}
|
|
class B<T? = dynamic> implements self::A<self::B::T%, core::bool> {}
|
|
class C implements self::B<core::int> {}
|
|
''');
|
|
|
|
expect(hierarchy.getClassAsInstanceOf(a, objectClass), objectSuper);
|
|
expect(hierarchy.getClassAsInstanceOf(a, a), a.asThisSupertype);
|
|
expect(hierarchy.getClassAsInstanceOf(b, a), new Supertype(a, [bTT, bool]));
|
|
expect(hierarchy.getClassAsInstanceOf(c, b), new Supertype(b, [int]));
|
|
expect(hierarchy.getClassAsInstanceOf(c, a), new Supertype(a, [int, bool]));
|
|
}
|
|
|
|
void test_getClassAsInstanceOf_generic_with() {
|
|
var int = coreTypes.intNonNullableRawType;
|
|
var bool = coreTypes.boolNonNullableRawType;
|
|
|
|
var a = addGenericClass('A', ['T', 'U']);
|
|
|
|
var bT = new TypeParameter(
|
|
'T',
|
|
coreTypes.objectNullableRawType,
|
|
const DynamicType(),
|
|
);
|
|
var bTT = new TypeParameterType(bT, Nullability.undetermined);
|
|
var b = addClass(
|
|
new Class(
|
|
name: 'B',
|
|
typeParameters: [bT],
|
|
supertype: objectSuper,
|
|
mixedInType: new Supertype(a, [bTT, bool]),
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
|
|
var c = addClass(
|
|
new Class(
|
|
name: 'C',
|
|
supertype: objectSuper,
|
|
mixedInType: new Supertype(b, [int]),
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
|
|
_assertTestLibraryText('''
|
|
class A<T? = dynamic, U? = dynamic> {}
|
|
class B<T? = dynamic> = core::Object with self::A<self::B::T%, core::bool> {}
|
|
class C = core::Object with self::B<core::int> {}
|
|
''');
|
|
|
|
expect(hierarchy.getClassAsInstanceOf(a, objectClass), objectSuper);
|
|
expect(hierarchy.getClassAsInstanceOf(a, a), a.asThisSupertype);
|
|
expect(hierarchy.getClassAsInstanceOf(b, a), new Supertype(a, [bTT, bool]));
|
|
expect(hierarchy.getClassAsInstanceOf(c, b), new Supertype(b, [int]));
|
|
expect(hierarchy.getClassAsInstanceOf(c, a), new Supertype(a, [int, bool]));
|
|
}
|
|
|
|
void test_getClassAsInstanceOf_notGeneric_extends() {
|
|
var a = addClass(
|
|
new Class(name: 'A', supertype: objectSuper, fileUri: library.fileUri),
|
|
);
|
|
var b = addClass(
|
|
new Class(
|
|
name: 'B',
|
|
supertype: a.asThisSupertype,
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
var c = addClass(
|
|
new Class(
|
|
name: 'C',
|
|
supertype: b.asThisSupertype,
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
var z = addClass(
|
|
new Class(name: 'Z', supertype: objectSuper, fileUri: library.fileUri),
|
|
);
|
|
|
|
_assertTestLibraryText('''
|
|
class A {}
|
|
class B extends self::A {}
|
|
class C extends self::B {}
|
|
class Z {}
|
|
''');
|
|
|
|
expect(hierarchy.getClassAsInstanceOf(a, objectClass), objectSuper);
|
|
expect(hierarchy.getClassAsInstanceOf(a, a), a.asThisSupertype);
|
|
expect(hierarchy.getClassAsInstanceOf(b, a), a.asThisSupertype);
|
|
expect(hierarchy.getClassAsInstanceOf(c, a), a.asThisSupertype);
|
|
expect(hierarchy.getClassAsInstanceOf(c, b), b.asThisSupertype);
|
|
expect(hierarchy.getClassAsInstanceOf(z, a), null);
|
|
expect(hierarchy.getClassAsInstanceOf(z, objectClass), objectSuper);
|
|
}
|
|
|
|
void test_getClassAsInstanceOf_notGeneric_implements() {
|
|
var a = addClass(
|
|
new Class(name: 'A', supertype: objectSuper, fileUri: library.fileUri),
|
|
);
|
|
var b = addClass(
|
|
new Class(name: 'B', supertype: objectSuper, fileUri: library.fileUri),
|
|
);
|
|
var c = addClass(
|
|
new Class(
|
|
name: 'C',
|
|
supertype: objectSuper,
|
|
implementedTypes: [a.asThisSupertype],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
var d = addClass(
|
|
new Class(
|
|
name: 'D',
|
|
supertype: objectSuper,
|
|
implementedTypes: [c.asThisSupertype],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
var e = addClass(
|
|
new Class(
|
|
name: 'D',
|
|
supertype: a.asThisSupertype,
|
|
implementedTypes: [b.asThisSupertype],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
var z = addClass(
|
|
new Class(name: 'Z', supertype: objectSuper, fileUri: library.fileUri),
|
|
);
|
|
|
|
_assertTestLibraryText('''
|
|
class A {}
|
|
class B {}
|
|
class C implements self::A {}
|
|
class D implements self::C {}
|
|
class D extends self::A implements self::B {}
|
|
class Z {}
|
|
''');
|
|
|
|
expect(hierarchy.getClassAsInstanceOf(c, a), a.asThisSupertype);
|
|
expect(hierarchy.getClassAsInstanceOf(d, a), a.asThisSupertype);
|
|
expect(hierarchy.getClassAsInstanceOf(d, c), c.asThisSupertype);
|
|
expect(hierarchy.getClassAsInstanceOf(e, a), a.asThisSupertype);
|
|
expect(hierarchy.getClassAsInstanceOf(e, b), b.asThisSupertype);
|
|
expect(hierarchy.getClassAsInstanceOf(z, a), null);
|
|
}
|
|
|
|
void test_getClassAsInstanceOf_notGeneric_with() {
|
|
var a = addClass(
|
|
new Class(name: 'A', supertype: objectSuper, fileUri: library.fileUri),
|
|
);
|
|
var b = addClass(
|
|
new Class(
|
|
name: 'B',
|
|
supertype: objectSuper,
|
|
mixedInType: a.asThisSupertype,
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
var z = addClass(
|
|
new Class(name: 'Z', supertype: objectSuper, fileUri: library.fileUri),
|
|
);
|
|
|
|
_assertTestLibraryText('''
|
|
class A {}
|
|
class B = core::Object with self::A {}
|
|
class Z {}
|
|
''');
|
|
|
|
expect(hierarchy.getClassAsInstanceOf(b, objectClass), objectSuper);
|
|
expect(hierarchy.getClassAsInstanceOf(b, a), a.asThisSupertype);
|
|
expect(hierarchy.getClassAsInstanceOf(z, a), null);
|
|
}
|
|
|
|
void test_getDeclaredMembers() {
|
|
var method = newEmptyMethod('method');
|
|
var getter = newEmptyGetter('getter');
|
|
var setter = newEmptySetter('setter');
|
|
var abstractMethod = newEmptyMethod('abstractMethod', isAbstract: true);
|
|
var abstractGetter = newEmptyGetter('abstractGetter', isAbstract: true);
|
|
var abstractSetter = newEmptySetter('abstractSetter', isAbstract: true);
|
|
var nonFinalField = new Field.mutable(
|
|
new Name('nonFinalField'),
|
|
fileUri: library.fileUri,
|
|
);
|
|
var finalField = new Field.immutable(
|
|
new Name('finalField'),
|
|
isFinal: true,
|
|
fileUri: library.fileUri,
|
|
);
|
|
var a = addClass(
|
|
new Class(
|
|
isAbstract: true,
|
|
name: 'A',
|
|
supertype: objectSuper,
|
|
fields: [nonFinalField, finalField],
|
|
procedures: [
|
|
method,
|
|
getter,
|
|
setter,
|
|
abstractMethod,
|
|
abstractGetter,
|
|
abstractSetter,
|
|
],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
var b = addClass(
|
|
new Class(
|
|
isAbstract: true,
|
|
name: 'B',
|
|
supertype: a.asThisSupertype,
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
|
|
_assertTestLibraryText('''
|
|
abstract class A {
|
|
field dynamic nonFinalField;
|
|
final field dynamic finalField;
|
|
method method() → void {}
|
|
get getter() → dynamic {
|
|
return null;
|
|
}
|
|
set setter(dynamic _) → void {}
|
|
abstract method abstractMethod() → void;
|
|
get abstractGetter() → dynamic;
|
|
set abstractSetter(dynamic _) → void;
|
|
}
|
|
abstract class B extends self::A {}
|
|
''');
|
|
|
|
expect(
|
|
hierarchy.getDeclaredMembers(a),
|
|
unorderedEquals([
|
|
method,
|
|
getter,
|
|
abstractMethod,
|
|
abstractGetter,
|
|
nonFinalField,
|
|
finalField,
|
|
]),
|
|
);
|
|
expect(
|
|
hierarchy.getDeclaredMembers(a, setters: true),
|
|
unorderedEquals([setter, abstractSetter, nonFinalField]),
|
|
);
|
|
expect(hierarchy.getDeclaredMembers(b).isEmpty, isTrue);
|
|
expect(hierarchy.getDeclaredMembers(b, setters: true).isEmpty, isTrue);
|
|
}
|
|
|
|
void test_getDispatchTarget() {
|
|
var aMethod = newEmptyMethod('aMethod');
|
|
var aSetter = newEmptySetter('aSetter');
|
|
var bMethod = newEmptyMethod('bMethod');
|
|
var bSetter = newEmptySetter('bSetter');
|
|
var a = addClass(
|
|
new Class(
|
|
name: 'A',
|
|
supertype: objectSuper,
|
|
procedures: [aMethod, aSetter],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
var b = addClass(
|
|
new Class(
|
|
name: 'B',
|
|
supertype: a.asThisSupertype,
|
|
procedures: [bMethod, bSetter],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
var c = addClass(
|
|
new Class(
|
|
name: 'C',
|
|
supertype: b.asThisSupertype,
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
|
|
_assertTestLibraryText('''
|
|
class A {
|
|
method aMethod() → void {}
|
|
set aSetter(dynamic _) → void {}
|
|
}
|
|
class B extends self::A {
|
|
method bMethod() → void {}
|
|
set bSetter(dynamic _) → void {}
|
|
}
|
|
class C extends self::B {}
|
|
''');
|
|
|
|
var aMethodName = new Name('aMethod');
|
|
var aSetterName = new Name('aSetter');
|
|
var bMethodName = new Name('bMethod');
|
|
var bSetterName = new Name('bSetter');
|
|
expect(hierarchy.getDispatchTarget(a, aMethodName), aMethod);
|
|
expect(hierarchy.getDispatchTarget(a, bMethodName), isNull);
|
|
expect(hierarchy.getDispatchTarget(a, aSetterName, setter: true), aSetter);
|
|
expect(hierarchy.getDispatchTarget(a, bSetterName, setter: true), isNull);
|
|
expect(hierarchy.getDispatchTarget(b, aMethodName), aMethod);
|
|
expect(hierarchy.getDispatchTarget(b, bMethodName), bMethod);
|
|
expect(hierarchy.getDispatchTarget(b, aSetterName, setter: true), aSetter);
|
|
expect(hierarchy.getDispatchTarget(b, bSetterName, setter: true), bSetter);
|
|
expect(hierarchy.getDispatchTarget(c, aMethodName), aMethod);
|
|
expect(hierarchy.getDispatchTarget(c, bMethodName), bMethod);
|
|
expect(hierarchy.getDispatchTarget(c, aSetterName, setter: true), aSetter);
|
|
expect(hierarchy.getDispatchTarget(c, bSetterName, setter: true), bSetter);
|
|
}
|
|
|
|
void test_getDispatchTarget_abstract() {
|
|
var aFoo = newEmptyMethod('foo', isAbstract: true);
|
|
var aBar = newEmptyMethod('bar');
|
|
var bFoo = newEmptyMethod('foo');
|
|
var bBar = newEmptyMethod('bar', isAbstract: true);
|
|
var a = addClass(
|
|
new Class(
|
|
isAbstract: true,
|
|
name: 'A',
|
|
supertype: objectSuper,
|
|
procedures: [aFoo, aBar],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
var b = addClass(
|
|
new Class(
|
|
isAbstract: true,
|
|
name: 'B',
|
|
supertype: a.asThisSupertype,
|
|
procedures: [bFoo, bBar],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
var c = addClass(
|
|
new Class(
|
|
name: 'C',
|
|
supertype: b.asThisSupertype,
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
|
|
_assertTestLibraryText('''
|
|
abstract class A {
|
|
abstract method foo() → void;
|
|
method bar() → void {}
|
|
}
|
|
abstract class B extends self::A {
|
|
method foo() → void {}
|
|
abstract method bar() → void;
|
|
}
|
|
class C extends self::B {}
|
|
''');
|
|
|
|
expect(hierarchy.getDispatchTarget(a, new Name('foo')), isNull);
|
|
expect(hierarchy.getDispatchTarget(b, new Name('foo')), bFoo);
|
|
expect(hierarchy.getDispatchTarget(c, new Name('foo')), bFoo);
|
|
|
|
expect(hierarchy.getDispatchTarget(a, new Name('bar')), aBar);
|
|
expect(hierarchy.getDispatchTarget(b, new Name('bar')), aBar);
|
|
expect(hierarchy.getDispatchTarget(c, new Name('bar')), aBar);
|
|
}
|
|
|
|
void test_getInterfaceMember_extends() {
|
|
var aMethod = newEmptyMethod('aMethod');
|
|
var aSetter = newEmptySetter('aSetter');
|
|
var bMethod = newEmptyMethod('bMethod');
|
|
var bSetter = newEmptySetter('bSetter');
|
|
var a = addClass(
|
|
new Class(
|
|
name: 'A',
|
|
supertype: objectSuper,
|
|
procedures: [aMethod, aSetter],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
var b = addClass(
|
|
new Class(
|
|
name: 'B',
|
|
supertype: a.asThisSupertype,
|
|
procedures: [bMethod, bSetter],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
var c = addClass(
|
|
new Class(
|
|
name: 'C',
|
|
supertype: b.asThisSupertype,
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
|
|
_assertTestLibraryText('''
|
|
class A {
|
|
method aMethod() → void {}
|
|
set aSetter(dynamic _) → void {}
|
|
}
|
|
class B extends self::A {
|
|
method bMethod() → void {}
|
|
set bSetter(dynamic _) → void {}
|
|
}
|
|
class C extends self::B {}
|
|
''');
|
|
|
|
var aMethodName = new Name('aMethod');
|
|
var aSetterName = new Name('aSetter');
|
|
var bMethodName = new Name('bMethod');
|
|
var bSetterName = new Name('bSetter');
|
|
expect(hierarchy.getInterfaceMember(a, aMethodName), aMethod);
|
|
expect(hierarchy.getInterfaceMember(a, bMethodName), isNull);
|
|
expect(hierarchy.getInterfaceMember(a, aSetterName, setter: true), aSetter);
|
|
expect(hierarchy.getInterfaceMember(a, bSetterName, setter: true), isNull);
|
|
expect(hierarchy.getInterfaceMember(b, aMethodName), aMethod);
|
|
expect(hierarchy.getInterfaceMember(b, bMethodName), bMethod);
|
|
expect(hierarchy.getInterfaceMember(b, aSetterName, setter: true), aSetter);
|
|
expect(hierarchy.getInterfaceMember(b, bSetterName, setter: true), bSetter);
|
|
expect(hierarchy.getInterfaceMember(c, aMethodName), aMethod);
|
|
expect(hierarchy.getInterfaceMember(c, bMethodName), bMethod);
|
|
expect(hierarchy.getInterfaceMember(c, aSetterName, setter: true), aSetter);
|
|
expect(hierarchy.getInterfaceMember(c, bSetterName, setter: true), bSetter);
|
|
}
|
|
|
|
void test_getInterfaceMember_implements() {
|
|
var aMethod = newEmptyMethod('aMethod');
|
|
var aSetter = newEmptySetter('aSetter');
|
|
var bMethod = newEmptyMethod('bMethod');
|
|
var bSetter = newEmptySetter('bSetter');
|
|
var a = addClass(
|
|
new Class(
|
|
name: 'A',
|
|
supertype: objectSuper,
|
|
procedures: [aMethod, aSetter],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
var b = addClass(
|
|
new Class(
|
|
name: 'B',
|
|
supertype: objectSuper,
|
|
implementedTypes: [a.asThisSupertype],
|
|
procedures: [bMethod, bSetter],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
var c = addClass(
|
|
new Class(
|
|
name: 'C',
|
|
supertype: objectSuper,
|
|
implementedTypes: [b.asThisSupertype],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
|
|
_assertTestLibraryText('''
|
|
class A {
|
|
method aMethod() → void {}
|
|
set aSetter(dynamic _) → void {}
|
|
}
|
|
class B implements self::A {
|
|
method bMethod() → void {}
|
|
set bSetter(dynamic _) → void {}
|
|
}
|
|
class C implements self::B {}
|
|
''');
|
|
|
|
var aMethodName = new Name('aMethod');
|
|
var aSetterName = new Name('aSetter');
|
|
var bMethodName = new Name('bMethod');
|
|
var bSetterName = new Name('bSetter');
|
|
expect(hierarchy.getInterfaceMember(a, aMethodName), aMethod);
|
|
expect(hierarchy.getInterfaceMember(a, bMethodName), isNull);
|
|
expect(hierarchy.getInterfaceMember(a, aSetterName, setter: true), aSetter);
|
|
expect(hierarchy.getInterfaceMember(a, bSetterName, setter: true), isNull);
|
|
expect(hierarchy.getInterfaceMember(b, aMethodName), aMethod);
|
|
expect(hierarchy.getInterfaceMember(b, bMethodName), bMethod);
|
|
expect(hierarchy.getInterfaceMember(b, aSetterName, setter: true), aSetter);
|
|
expect(hierarchy.getInterfaceMember(b, bSetterName, setter: true), bSetter);
|
|
expect(hierarchy.getInterfaceMember(c, aMethodName), aMethod);
|
|
expect(hierarchy.getInterfaceMember(c, bMethodName), bMethod);
|
|
expect(hierarchy.getInterfaceMember(c, aSetterName, setter: true), aSetter);
|
|
expect(hierarchy.getInterfaceMember(c, bSetterName, setter: true), bSetter);
|
|
}
|
|
|
|
void test_getInterfaceMembers_in_class() {
|
|
var method = newEmptyMethod('method');
|
|
var getter = newEmptyGetter('getter');
|
|
var setter = newEmptySetter('setter');
|
|
var abstractMethod = newEmptyMethod('abstractMethod', isAbstract: true);
|
|
var abstractGetter = newEmptyGetter('abstractGetter', isAbstract: true);
|
|
var abstractSetter = newEmptySetter('abstractSetter', isAbstract: true);
|
|
var nonFinalField = new Field.mutable(
|
|
new Name('nonFinalField'),
|
|
fileUri: library.fileUri,
|
|
);
|
|
var finalField = new Field.immutable(
|
|
new Name('finalField'),
|
|
isFinal: true,
|
|
fileUri: library.fileUri,
|
|
);
|
|
var a = addClass(
|
|
new Class(
|
|
isAbstract: true,
|
|
name: 'A',
|
|
supertype: objectSuper,
|
|
fields: [nonFinalField, finalField],
|
|
procedures: [
|
|
method,
|
|
getter,
|
|
setter,
|
|
abstractMethod,
|
|
abstractGetter,
|
|
abstractSetter,
|
|
],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
|
|
_assertTestLibraryText('''
|
|
abstract class A {
|
|
field dynamic nonFinalField;
|
|
final field dynamic finalField;
|
|
method method() → void {}
|
|
get getter() → dynamic {
|
|
return null;
|
|
}
|
|
set setter(dynamic _) → void {}
|
|
abstract method abstractMethod() → void;
|
|
get abstractGetter() → dynamic;
|
|
set abstractSetter(dynamic _) → void;
|
|
}
|
|
''');
|
|
|
|
expect(
|
|
hierarchy.getInterfaceMembers(a),
|
|
unorderedEquals([
|
|
method,
|
|
getter,
|
|
abstractMethod,
|
|
abstractGetter,
|
|
nonFinalField,
|
|
finalField,
|
|
]),
|
|
);
|
|
expect(
|
|
hierarchy.getInterfaceMembers(a, setters: true),
|
|
unorderedEquals([setter, abstractSetter, nonFinalField]),
|
|
);
|
|
}
|
|
|
|
void test_getInterfaceMembers_inherited_or_mixed_in() {
|
|
var method = newEmptyMethod('method');
|
|
var getter = newEmptyGetter('getter');
|
|
var setter = newEmptySetter('setter');
|
|
var abstractMethod = newEmptyMethod('abstractMethod', isAbstract: true);
|
|
var abstractGetter = newEmptyGetter('abstractGetter', isAbstract: true);
|
|
var abstractSetter = newEmptySetter('abstractSetter', isAbstract: true);
|
|
var nonFinalField = new Field.mutable(
|
|
new Name('nonFinalField'),
|
|
fileUri: library.fileUri,
|
|
);
|
|
var finalField = new Field.immutable(
|
|
new Name('finalField'),
|
|
isFinal: true,
|
|
fileUri: library.fileUri,
|
|
);
|
|
|
|
var a = addClass(
|
|
new Class(
|
|
name: 'A',
|
|
supertype: objectSuper,
|
|
fields: [nonFinalField, finalField],
|
|
procedures: [
|
|
method,
|
|
getter,
|
|
setter,
|
|
abstractMethod,
|
|
abstractGetter,
|
|
abstractSetter,
|
|
],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
var b = addClass(
|
|
new Class(
|
|
name: 'B',
|
|
supertype: a.asThisSupertype,
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
var c = addClass(
|
|
new Class(
|
|
isAbstract: true,
|
|
name: 'C',
|
|
supertype: objectSuper,
|
|
implementedTypes: [a.asThisSupertype],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
var d = addClass(
|
|
new Class(
|
|
name: 'D',
|
|
supertype: objectSuper,
|
|
mixedInType: a.asThisSupertype,
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
|
|
_assertTestLibraryText('''
|
|
class A {
|
|
field dynamic nonFinalField;
|
|
final field dynamic finalField;
|
|
method method() → void {}
|
|
get getter() → dynamic {
|
|
return null;
|
|
}
|
|
set setter(dynamic _) → void {}
|
|
abstract method abstractMethod() → void;
|
|
get abstractGetter() → dynamic;
|
|
set abstractSetter(dynamic _) → void;
|
|
}
|
|
class B extends self::A {}
|
|
abstract class C implements self::A {}
|
|
class D = core::Object with self::A {}
|
|
''');
|
|
|
|
var expectedGetters = [
|
|
method,
|
|
getter,
|
|
abstractMethod,
|
|
abstractGetter,
|
|
nonFinalField,
|
|
finalField,
|
|
];
|
|
expect(hierarchy.getInterfaceMembers(b), unorderedEquals(expectedGetters));
|
|
var expectedSetters = [setter, abstractSetter, nonFinalField];
|
|
expect(
|
|
hierarchy.getInterfaceMembers(b, setters: true),
|
|
unorderedEquals(expectedSetters),
|
|
);
|
|
expect(hierarchy.getInterfaceMembers(c), unorderedEquals(expectedGetters));
|
|
expect(
|
|
hierarchy.getInterfaceMembers(c, setters: true),
|
|
unorderedEquals(expectedSetters),
|
|
);
|
|
expect(hierarchy.getInterfaceMembers(d), unorderedEquals(expectedGetters));
|
|
expect(
|
|
hierarchy.getInterfaceMembers(d, setters: true),
|
|
unorderedEquals(expectedSetters),
|
|
);
|
|
}
|
|
|
|
void test_getInterfaceMembers_multiple() {
|
|
var method_a = newEmptyMethod('method');
|
|
var getter_a = newEmptyGetter('getter');
|
|
var setter_a = newEmptySetter('setter');
|
|
var nonFinalField_a = new Field.mutable(
|
|
new Name('nonFinalField'),
|
|
fileUri: library.fileUri,
|
|
);
|
|
var finalField_a = new Field.immutable(
|
|
new Name('finalField'),
|
|
isFinal: true,
|
|
fileUri: library.fileUri,
|
|
);
|
|
var method_b = newEmptyMethod('method');
|
|
var getter_b = newEmptyGetter('getter');
|
|
var setter_b = newEmptySetter('setter');
|
|
var nonFinalField_b = new Field.mutable(
|
|
new Name('nonFinalField'),
|
|
fileUri: library.fileUri,
|
|
);
|
|
var finalField_b = new Field.immutable(
|
|
new Name('finalField'),
|
|
isFinal: true,
|
|
fileUri: library.fileUri,
|
|
);
|
|
|
|
var a = addClass(
|
|
new Class(
|
|
name: 'A',
|
|
supertype: objectSuper,
|
|
fields: [nonFinalField_a, finalField_a],
|
|
procedures: [method_a, getter_a, setter_a],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
var b = addClass(
|
|
new Class(
|
|
name: 'B',
|
|
supertype: objectSuper,
|
|
fields: [nonFinalField_b, finalField_b],
|
|
procedures: [method_b, getter_b, setter_b],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
var c = addClass(
|
|
new Class(
|
|
isAbstract: true,
|
|
name: 'C',
|
|
supertype: objectSuper,
|
|
implementedTypes: [a.asThisSupertype, b.asThisSupertype],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
|
|
_assertTestLibraryText('''
|
|
class A {
|
|
field dynamic nonFinalField;
|
|
final field dynamic finalField;
|
|
method method() → void {}
|
|
get getter() → dynamic {
|
|
return null;
|
|
}
|
|
set setter(dynamic _) → void {}
|
|
}
|
|
class B {
|
|
field dynamic nonFinalField;
|
|
final field dynamic finalField;
|
|
method method() → void {}
|
|
get getter() → dynamic {
|
|
return null;
|
|
}
|
|
set setter(dynamic _) → void {}
|
|
}
|
|
abstract class C implements self::A, self::B {}
|
|
''');
|
|
|
|
expect(
|
|
hierarchy.getInterfaceMembers(c),
|
|
unorderedEquals([
|
|
method_a,
|
|
getter_a,
|
|
nonFinalField_a,
|
|
finalField_a,
|
|
method_b,
|
|
getter_b,
|
|
nonFinalField_b,
|
|
finalField_b,
|
|
]),
|
|
);
|
|
expect(
|
|
hierarchy.getInterfaceMembers(c, setters: true),
|
|
unorderedEquals([setter_a, nonFinalField_a, setter_b, nonFinalField_b]),
|
|
);
|
|
}
|
|
|
|
void test_getInterfaceMembers_shadowed() {
|
|
var method_a = newEmptyMethod('method');
|
|
var nonShadowedMethod_a = newEmptyMethod('nonShadowedMethod');
|
|
var getter_a = newEmptyGetter('getter');
|
|
var setter_a = newEmptySetter('setter');
|
|
var nonShadowedSetter_a = newEmptySetter('nonShadowedSetter');
|
|
var nonFinalField_a = new Field.mutable(
|
|
new Name('nonFinalField'),
|
|
fileUri: library.fileUri,
|
|
);
|
|
var finalField_a = new Field.immutable(
|
|
new Name('finalField'),
|
|
isFinal: true,
|
|
fileUri: library.fileUri,
|
|
);
|
|
var method_b = newEmptyMethod('method');
|
|
var getter_b = newEmptyGetter('getter');
|
|
var setter_b = newEmptySetter('setter');
|
|
var nonFinalField_b = new Field.mutable(
|
|
new Name('nonFinalField'),
|
|
fileUri: library.fileUri,
|
|
);
|
|
var finalField_b = new Field.immutable(
|
|
new Name('finalField'),
|
|
isFinal: true,
|
|
fileUri: library.fileUri,
|
|
);
|
|
|
|
var a = addClass(
|
|
new Class(
|
|
name: 'A',
|
|
supertype: objectSuper,
|
|
fields: [nonFinalField_a, finalField_a],
|
|
procedures: [
|
|
method_a,
|
|
nonShadowedMethod_a,
|
|
getter_a,
|
|
setter_a,
|
|
nonShadowedSetter_a,
|
|
],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
var b = addClass(
|
|
new Class(
|
|
name: 'B',
|
|
supertype: a.asThisSupertype,
|
|
fields: [nonFinalField_b, finalField_b],
|
|
procedures: [method_b, getter_b, setter_b],
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
|
|
_assertTestLibraryText('''
|
|
class A {
|
|
field dynamic nonFinalField;
|
|
final field dynamic finalField;
|
|
method method() → void {}
|
|
method nonShadowedMethod() → void {}
|
|
get getter() → dynamic {
|
|
return null;
|
|
}
|
|
set setter(dynamic _) → void {}
|
|
set nonShadowedSetter(dynamic _) → void {}
|
|
}
|
|
class B extends self::A {
|
|
field dynamic nonFinalField;
|
|
final field dynamic finalField;
|
|
method method() → void {}
|
|
get getter() → dynamic {
|
|
return null;
|
|
}
|
|
set setter(dynamic _) → void {}
|
|
}
|
|
''');
|
|
|
|
expect(
|
|
hierarchy.getInterfaceMembers(b),
|
|
unorderedEquals([
|
|
nonShadowedMethod_a,
|
|
method_b,
|
|
getter_b,
|
|
nonFinalField_b,
|
|
finalField_b,
|
|
]),
|
|
);
|
|
expect(
|
|
hierarchy.getInterfaceMembers(b, setters: true),
|
|
unorderedEquals([nonShadowedSetter_a, setter_b, nonFinalField_b]),
|
|
);
|
|
}
|
|
|
|
void test_getOrderedClasses() {
|
|
var a = addClass(
|
|
new Class(name: 'A', supertype: objectSuper, fileUri: library.fileUri),
|
|
);
|
|
var b = addClass(
|
|
new Class(
|
|
name: 'B',
|
|
supertype: a.asThisSupertype,
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
var c = addClass(
|
|
new Class(
|
|
name: 'C',
|
|
supertype: b.asThisSupertype,
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
|
|
void assertOrderOfClasses(List<Class> unordered, List<Class> expected) {
|
|
var ordered = hierarchy.getOrderedClasses(unordered);
|
|
expect(ordered, expected);
|
|
}
|
|
|
|
assertOrderOfClasses([a, b, c], [a, b, c]);
|
|
assertOrderOfClasses([b, a, c], [a, b, c]);
|
|
assertOrderOfClasses([a, c, b], [a, b, c]);
|
|
assertOrderOfClasses([b, c, a], [a, b, c]);
|
|
assertOrderOfClasses([c, a, b], [a, b, c]);
|
|
assertOrderOfClasses([c, b, a], [a, b, c]);
|
|
assertOrderOfClasses([c, b], [b, c]);
|
|
}
|
|
|
|
void test_getTypeAsInstanceOf_generic_extends() {
|
|
var int = coreTypes.intNonNullableRawType;
|
|
var bool = coreTypes.boolNonNullableRawType;
|
|
|
|
var a = addGenericClass('A', ['T', 'U']);
|
|
|
|
var bT = new TypeParameter(
|
|
'T',
|
|
coreTypes.objectNullableRawType,
|
|
const DynamicType(),
|
|
);
|
|
var bTT = new TypeParameterType(bT, Nullability.undetermined);
|
|
var b = addClass(
|
|
new Class(
|
|
name: 'B',
|
|
typeParameters: [bT],
|
|
supertype: new Supertype(a, [bTT, bool]),
|
|
fileUri: library.fileUri,
|
|
),
|
|
);
|
|
|
|
_assertTestLibraryText('''
|
|
class A<T? = dynamic, U? = dynamic> {}
|
|
class B<T? = dynamic> extends self::A<self::B::T%, core::bool> {}
|
|
''');
|
|
|
|
var b_int = new InterfaceType(b, Nullability.nonNullable, [int]);
|
|
expect(
|
|
hierarchy.getInterfaceTypeAsInstanceOfClass(b_int, a),
|
|
new InterfaceType(a, Nullability.nonNullable, [int, bool]),
|
|
);
|
|
expect(
|
|
hierarchy.getInterfaceTypeAsInstanceOfClass(b_int, objectClass),
|
|
new InterfaceType(objectClass, Nullability.nonNullable),
|
|
);
|
|
}
|
|
|
|
void _assertOverridePairs(Class class_, List<String> expected) {
|
|
List<String> overrideDescriptions = [];
|
|
void callback(
|
|
Member declaredMember,
|
|
Member interfaceMember,
|
|
bool isSetter,
|
|
) {
|
|
var suffix = isSetter ? '=' : '';
|
|
String declaredMemberName = qualifiedMemberNameToString(
|
|
declaredMember,
|
|
includeLibraryName: true,
|
|
);
|
|
String declaredName = '${declaredMemberName}$suffix';
|
|
String interfaceMemberName = qualifiedMemberNameToString(
|
|
interfaceMember,
|
|
includeLibraryName: true,
|
|
);
|
|
String interfaceName = '${interfaceMemberName}$suffix';
|
|
var desc = '$declaredName overrides $interfaceName';
|
|
overrideDescriptions.add(desc);
|
|
}
|
|
|
|
hierarchy.forEachOverridePair(class_, callback);
|
|
expect(overrideDescriptions, unorderedEquals(expected));
|
|
}
|
|
|
|
/// Assert that the test [library] has the [expectedText] presentation.
|
|
/// The presentation is close, but not identical to the normal Kernel one.
|
|
void _assertTestLibraryText(String expectedText) {
|
|
_assertLibraryText(library, expectedText);
|
|
}
|
|
|
|
void _assertLibraryText(Library lib, String expectedText) {
|
|
StringBuffer sb = new StringBuffer();
|
|
Printer printer = new Printer(sb);
|
|
printer.writeLibraryFile(lib);
|
|
|
|
String actualText = sb.toString();
|
|
|
|
// Clean up the text a bit.
|
|
const oftenUsedPrefix = '''
|
|
library test;
|
|
import self as self;
|
|
import "dart:core" as core;
|
|
|
|
''';
|
|
if (actualText.startsWith(oftenUsedPrefix)) {
|
|
actualText = actualText.substring(oftenUsedPrefix.length);
|
|
}
|
|
actualText = actualText.replaceAll('{\n}', '{}');
|
|
actualText = actualText.replaceAll(' extends core::Object', '');
|
|
|
|
if (actualText != expectedText) {
|
|
print('-------- Actual --------');
|
|
print(actualText + '------------------------');
|
|
}
|
|
|
|
expect(actualText, expectedText);
|
|
}
|
|
}
|