| // 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_test.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.element), 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(ClassElement 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); |
| } |
| } |