blob: a9cf0eb57c3f3698176aeb565db5eb7bb7ed4282 [file] [log] [blame]
// Copyright (c) 2019, 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/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/least_upper_bound.dart';
import 'package:analyzer/src/generated/testing/element_factory.dart';
import 'package:analyzer/src/generated/testing/test_type_provider.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../../../generated/type_system_base.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(PathToObjectTest);
defineReflectiveTests(SuperinterfaceSetTest);
});
}
@reflectiveTest
class PathToObjectTest extends AbstractTypeSystemTest {
@override
final TestTypeProvider typeProvider = TestTypeProvider();
void test_class_mixins1() {
var M1 = mixin_(name: 'M1');
expect(_longestPathToObject(M1), 1);
var A = class_(name: 'A');
expect(_longestPathToObject(A), 1);
// class _X&A&M1 extends A implements M1 {}
// length: 2
// class X extends _X&A&M1 {}
// length: 3
var X = class_(
name: 'X',
superType: interfaceTypeNone(A),
mixins: [
interfaceTypeNone(M1),
],
);
expect(_longestPathToObject(X), 3);
}
void test_class_mixins2() {
var M1 = mixin_(name: 'M1');
var M2 = mixin_(name: 'M2');
expect(_longestPathToObject(M1), 1);
expect(_longestPathToObject(M2), 1);
var A = class_(name: 'A');
expect(_longestPathToObject(A), 1);
// class _X&A&M1 extends A implements M1 {}
// length: 2
// class _X&A&M1&M2 extends _X&A&M1 implements M2 {}
// length: 3
// class X extends _X&A&M1&M2 {}
// length: 4
var X = class_(
name: 'X',
superType: interfaceTypeNone(A),
mixins: [
interfaceTypeNone(M1),
interfaceTypeNone(M2),
],
);
expect(_longestPathToObject(X), 4);
}
void test_class_mixins_longerViaSecondMixin() {
var I1 = class_(name: 'I1');
var I2 = class_(name: 'I2', superType: interfaceTypeNone(I1));
var I3 = class_(name: 'I3', superType: interfaceTypeNone(I2));
expect(_longestPathToObject(I1), 1);
expect(_longestPathToObject(I2), 2);
expect(_longestPathToObject(I3), 3);
var M1 = mixin_(name: 'M1');
var M2 = mixin_(
name: 'M2',
interfaces: [interfaceTypeNone(I3)],
);
expect(_longestPathToObject(M1), 1);
expect(_longestPathToObject(M2), 4);
var A = class_(name: 'A'); // length: 1
expect(_longestPathToObject(A), 1);
// class _X&A&M1 extends A implements M1 {}
// length: 2
// class _X&A&M1&M2 extends _X&A&M1 implements M2 {}
// length: 5 = max(1 + _X&A&M1, 1 + M2)
// class X extends _X&A&M1&M2 {}
// length: 6
var X = class_(
name: 'X',
superType: interfaceTypeNone(A),
mixins: [
interfaceTypeNone(M1),
interfaceTypeNone(M2),
],
);
expect(_longestPathToObject(X), 6);
}
void test_class_multipleInterfacePaths() {
//
// Object
// |
// A
// / \
// B C
// | |
// | D
// \ /
// E
//
ClassElementImpl classA = class_(name: "A");
ClassElementImpl classB = class_(name: "B");
ClassElementImpl classC = class_(name: "C");
ClassElementImpl classD = class_(name: "D");
ClassElementImpl classE = class_(name: "E");
classB.interfaces = <InterfaceType>[interfaceTypeStar(classA)];
classC.interfaces = <InterfaceType>[interfaceTypeStar(classA)];
classD.interfaces = <InterfaceType>[interfaceTypeStar(classC)];
classE.interfaces = <InterfaceType>[
interfaceTypeStar(classB),
interfaceTypeStar(classD)
];
// assertion: even though the longest path to Object for typeB is 2, and
// typeE implements typeB, the longest path for typeE is 4 since it also
// implements typeD
expect(_longestPathToObject(classB), 2);
expect(_longestPathToObject(classE), 4);
}
void test_class_multipleSuperclassPaths() {
//
// Object
// |
// A
// / \
// B C
// | |
// | D
// \ /
// E
//
ClassElement classA = class_(name: "A");
ClassElement classB =
class_(name: "B", superType: interfaceTypeStar(classA));
ClassElement classC =
class_(name: "C", superType: interfaceTypeStar(classA));
ClassElement classD =
class_(name: "D", superType: interfaceTypeStar(classC));
ClassElementImpl classE =
class_(name: "E", superType: interfaceTypeStar(classB));
classE.interfaces = <InterfaceType>[interfaceTypeStar(classD)];
// assertion: even though the longest path to Object for typeB is 2, and
// typeE extends typeB, the longest path for typeE is 4 since it also
// implements typeD
expect(_longestPathToObject(classB), 2);
expect(_longestPathToObject(classE), 4);
}
void test_class_object() {
expect(_longestPathToObject(typeProvider.objectType.element2), 0);
}
void test_class_recursion() {
ClassElementImpl classA = class_(name: "A");
ClassElementImpl classB =
class_(name: "B", superType: interfaceTypeStar(classA));
classA.supertype = interfaceTypeStar(classB);
expect(_longestPathToObject(classA), 2);
}
void test_class_singleInterfacePath() {
//
// Object
// |
// A
// |
// B
// |
// C
//
ClassElementImpl classA = class_(name: "A");
ClassElementImpl classB = class_(name: "B");
ClassElementImpl classC = class_(name: "C");
classB.interfaces = <InterfaceType>[interfaceTypeStar(classA)];
classC.interfaces = <InterfaceType>[interfaceTypeStar(classB)];
expect(_longestPathToObject(classA), 1);
expect(_longestPathToObject(classB), 2);
expect(_longestPathToObject(classC), 3);
}
void test_class_singleSuperclassPath() {
//
// Object
// |
// A
// |
// B
// |
// C
//
ClassElement classA = class_(name: "A");
ClassElement classB =
class_(name: "B", superType: interfaceTypeStar(classA));
ClassElement classC =
class_(name: "C", superType: interfaceTypeStar(classB));
expect(_longestPathToObject(classA), 1);
expect(_longestPathToObject(classB), 2);
expect(_longestPathToObject(classC), 3);
}
void test_mixin_constraints_interfaces_allSame() {
var A = class_(name: 'A');
var B = class_(name: 'B');
var I = class_(name: 'I');
var J = class_(name: 'J');
expect(_longestPathToObject(A), 1);
expect(_longestPathToObject(B), 1);
expect(_longestPathToObject(I), 1);
expect(_longestPathToObject(J), 1);
// The interface of M is:
// class _M&A&A implements A, B, I, J {}
var M = mixin_(
name: 'M',
constraints: [
interfaceTypeNone(A),
interfaceTypeNone(B),
],
interfaces: [
interfaceTypeNone(I),
interfaceTypeNone(J),
],
);
expect(_longestPathToObject(M), 2);
}
void test_mixin_longerConstraint_1() {
var A1 = class_(name: 'A1');
var A = class_(
name: 'A',
superType: interfaceTypeNone(A1),
);
var B = class_(name: 'B');
var I = class_(name: 'I');
var J = class_(name: 'J');
expect(_longestPathToObject(A), 2);
expect(_longestPathToObject(B), 1);
expect(_longestPathToObject(I), 1);
expect(_longestPathToObject(J), 1);
// The interface of M is:
// class _M&A&A implements A, B, I, J {}
var M = mixin_(
name: 'M',
constraints: [
interfaceTypeNone(A),
interfaceTypeNone(B),
],
interfaces: [
interfaceTypeNone(I),
interfaceTypeNone(J),
],
);
expect(_longestPathToObject(M), 3);
}
void test_mixin_longerConstraint_2() {
var A = class_(name: 'A');
var B1 = class_(name: 'B1');
var B = class_(
name: 'B',
interfaces: [
interfaceTypeNone(B1),
],
);
var I = class_(name: 'I');
var J = class_(name: 'J');
expect(_longestPathToObject(A), 1);
expect(_longestPathToObject(B), 2);
expect(_longestPathToObject(I), 1);
expect(_longestPathToObject(J), 1);
// The interface of M is:
// class _M&A&A implements A, B, I, J {}
var M = mixin_(
name: 'M',
constraints: [
interfaceTypeNone(A),
interfaceTypeNone(B),
],
interfaces: [
interfaceTypeNone(I),
interfaceTypeNone(J),
],
);
expect(_longestPathToObject(M), 3);
}
void test_mixin_longerInterface_1() {
var A = class_(name: 'A');
var B = class_(name: 'B');
var I1 = class_(name: 'I1');
var I = class_(
name: 'I',
interfaces: [
interfaceTypeNone(I1),
],
);
var J = class_(name: 'J');
expect(_longestPathToObject(A), 1);
expect(_longestPathToObject(B), 1);
expect(_longestPathToObject(I), 2);
expect(_longestPathToObject(J), 1);
// The interface of M is:
// class _M&A&A implements A, B, I, J {}
var M = mixin_(
name: 'M',
constraints: [
interfaceTypeNone(A),
interfaceTypeNone(B),
],
interfaces: [
interfaceTypeNone(I),
interfaceTypeNone(J),
],
);
expect(_longestPathToObject(M), 3);
}
int _longestPathToObject(InterfaceElement element) {
return InterfaceLeastUpperBoundHelper.computeLongestInheritancePathToObject(
element);
}
}
@reflectiveTest
class SuperinterfaceSetTest extends AbstractTypeSystemTest {
@override
final TestTypeProvider typeProvider = TestTypeProvider();
void test_genericInterfacePath() {
//
// A
// | implements
// B<T>
// | implements
// C<T>
//
// D
//
var instObject = InstantiatedClass.of(typeProvider.objectType);
ClassElementImpl classA = class_(name: 'A');
var instA = InstantiatedClass(classA, const []);
var BT = typeParameter('T');
var classB = class_(
name: 'B',
typeParameters: [BT],
interfaces: [instA.withNullabilitySuffixNone],
);
var CT = typeParameter('T');
var classC = class_(
name: 'C',
typeParameters: [CT],
interfaces: [
InstantiatedClass(
classB,
[typeParameterTypeStar(CT)],
).withNullabilitySuffixNone,
],
);
var classD = class_(name: 'D');
// A
expect(
_superInterfaces(instA),
unorderedEquals([instObject]),
);
// B<D>
expect(
_superInterfaces(
InstantiatedClass(classB, [interfaceTypeStar(classD)]),
),
unorderedEquals([instObject, instA]),
);
// C<D>
expect(
_superInterfaces(
InstantiatedClass(classC, [interfaceTypeStar(classD)]),
),
unorderedEquals([
instObject,
instA,
InstantiatedClass(classB, [interfaceTypeStar(classD)]),
]),
);
}
void test_genericSuperclassPath() {
//
// A
// |
// B<T>
// |
// C<T>
//
// D
//
var instObject = InstantiatedClass.of(typeProvider.objectType);
ClassElementImpl classA = class_(name: 'A');
var instA = InstantiatedClass(classA, const []);
var classB = class_(
name: 'B',
typeParameters: [typeParameter('T')],
superType: instA.withNullabilitySuffixNone,
);
var typeParametersC = ElementFactory.typeParameters(['T']);
var classC = class_(
name: 'B',
typeParameters: typeParametersC,
superType: InstantiatedClass(
classB,
[typeParameterTypeStar(typeParametersC[0])],
).withNullabilitySuffixNone,
);
var classD = class_(name: 'D');
// A
expect(
_superInterfaces(instA),
unorderedEquals([instObject]),
);
// B<D>
expect(
_superInterfaces(
InstantiatedClass(classB, [interfaceTypeStar(classD)]),
),
unorderedEquals([instObject, instA]),
);
// C<D>
expect(
_superInterfaces(
InstantiatedClass(classC, [interfaceTypeStar(classD)]),
),
unorderedEquals([
instObject,
instA,
InstantiatedClass(classB, [interfaceTypeStar(classD)]),
]),
);
}
void test_mixin_constraints() {
var instObject = InstantiatedClass.of(typeProvider.objectType);
var classA = class_(name: 'A');
var instA = InstantiatedClass(classA, const []);
var classB = class_(
name: 'B',
interfaces: [instA.withNullabilitySuffixNone],
);
var instB = InstantiatedClass(classB, const []);
var classC = class_(name: 'C');
var instC = InstantiatedClass(classC, const []);
var mixinM = mixin_(
name: 'M',
constraints: [
instB.withNullabilitySuffixNone,
instC.withNullabilitySuffixNone,
],
);
var instM = InstantiatedClass(mixinM, const []);
expect(
_superInterfaces(instM),
unorderedEquals([instObject, instA, instB, instC]),
);
}
void test_mixin_constraints_object() {
var instObject = InstantiatedClass.of(typeProvider.objectType);
var mixinM = mixin_(name: 'M');
var instM = InstantiatedClass(mixinM, const []);
expect(
_superInterfaces(instM),
unorderedEquals([instObject]),
);
}
void test_mixin_interfaces() {
var instObject = InstantiatedClass.of(typeProvider.objectType);
var classA = class_(name: 'A');
var instA = InstantiatedClass(classA, const []);
var classB = class_(
name: 'B',
interfaces: [instA.withNullabilitySuffixNone],
);
var instB = InstantiatedClass(classB, const []);
var classC = class_(name: 'C');
var instC = InstantiatedClass(classC, const []);
var mixinM = mixin_(
name: 'M',
interfaces: [
instB.withNullabilitySuffixNone,
instC.withNullabilitySuffixNone,
],
);
var instM = InstantiatedClass(mixinM, const []);
expect(
_superInterfaces(instM),
unorderedEquals([instObject, instA, instB, instC]),
);
}
void test_multipleInterfacePaths() {
var instObject = InstantiatedClass.of(typeProvider.objectType);
var classA = class_(name: 'A');
var instA = InstantiatedClass(classA, const []);
var classB = class_(
name: 'B',
interfaces: [instA.withNullabilitySuffixNone],
);
var instB = InstantiatedClass(classB, const []);
var classC = class_(
name: 'C',
interfaces: [instA.withNullabilitySuffixNone],
);
var instC = InstantiatedClass(classC, const []);
var classD = class_(
name: 'D',
interfaces: [instC.withNullabilitySuffixNone],
);
var instD = InstantiatedClass(classD, const []);
var classE = class_(
name: 'E',
interfaces: [
instB.withNullabilitySuffixNone,
instD.withNullabilitySuffixNone,
],
);
var instE = InstantiatedClass(classE, const []);
// D
expect(
_superInterfaces(instD),
unorderedEquals([instObject, instA, instC]),
);
// E
expect(
_superInterfaces(instE),
unorderedEquals([instObject, instA, instB, instC, instD]),
);
}
void test_multipleSuperclassPaths() {
var instObject = InstantiatedClass.of(typeProvider.objectType);
var classA = class_(name: 'A');
var instA = InstantiatedClass(classA, const []);
var classB = class_(
name: 'B',
superType: instA.withNullabilitySuffixNone,
);
var instB = InstantiatedClass(classB, const []);
var classC = class_(
name: 'C',
superType: instA.withNullabilitySuffixNone,
);
var instC = InstantiatedClass(classC, const []);
var classD = class_(
name: 'D',
superType: instC.withNullabilitySuffixNone,
);
var instD = InstantiatedClass(classD, const []);
var classE = class_(
name: 'E',
superType: instB.withNullabilitySuffixNone,
interfaces: [
instD.withNullabilitySuffixNone,
],
);
var instE = InstantiatedClass(classE, const []);
// D
expect(
_superInterfaces(instD),
unorderedEquals([instObject, instA, instC]),
);
// E
expect(
_superInterfaces(instE),
unorderedEquals([instObject, instA, instB, instC, instD]),
);
}
void test_recursion() {
var classA = class_(name: 'A');
var instA = InstantiatedClass(classA, const []);
var classB = class_(
name: 'B',
superType: instA.withNullabilitySuffixNone,
);
var instB = InstantiatedClass(classB, const []);
classA.supertype = instB.withNullabilitySuffixNone;
expect(
_superInterfaces(instB),
unorderedEquals([instA, instB]),
);
expect(
_superInterfaces(instA),
unorderedEquals([instA, instB]),
);
}
void test_singleInterfacePath() {
var instObject = InstantiatedClass.of(typeProvider.objectType);
var classA = class_(name: 'A');
var instA = InstantiatedClass(classA, const []);
var classB = class_(
name: 'B',
interfaces: [instA.withNullabilitySuffixNone],
);
var instB = InstantiatedClass(classB, const []);
var classC = class_(
name: 'C',
interfaces: [instB.withNullabilitySuffixNone],
);
var instC = InstantiatedClass(classC, const []);
// A
expect(
_superInterfaces(instA),
unorderedEquals([instObject]),
);
// B
expect(
_superInterfaces(instB),
unorderedEquals([instObject, instA]),
);
// C
expect(
_superInterfaces(instC),
unorderedEquals([instObject, instA, instB]),
);
}
void test_singleSuperclassPath() {
//
// A
// |
// B
// |
// C
//
var instObject = InstantiatedClass.of(typeProvider.objectType);
var classA = class_(name: 'A');
var instA = InstantiatedClass(classA, const []);
var classB = class_(
name: 'B',
superType: instA.withNullabilitySuffixNone,
);
var instB = InstantiatedClass(classB, const []);
var classC = class_(
name: 'C',
superType: instB.withNullabilitySuffixNone,
);
var instC = InstantiatedClass(classC, const []);
// A
expect(
_superInterfaces(instA),
unorderedEquals([instObject]),
);
// B
expect(
_superInterfaces(instB),
unorderedEquals([instObject, instA]),
);
// C
expect(
_superInterfaces(instC),
unorderedEquals([instObject, instA, instB]),
);
}
Set<InstantiatedClass> _superInterfaces(InstantiatedClass type) {
var helper = InterfaceLeastUpperBoundHelper(typeSystem);
return helper.computeSuperinterfaceSet(type);
}
}