diff --git a/pkg/analyzer/lib/src/dart/element/type.dart b/pkg/analyzer/lib/src/dart/element/type.dart index 2252d1daf2ff8..18cc64a3e95a6 100644 --- a/pkg/analyzer/lib/src/dart/element/type.dart +++ b/pkg/analyzer/lib/src/dart/element/type.dart @@ -2415,80 +2415,6 @@ class InterfaceTypeImpl extends TypeImpl implements InterfaceType { return result; } - /** - * Compute the least upper bound of types [i] and [j], both of which are - * known to be interface types. - * - * In the event that the algorithm fails (which might occur due to a bug in - * the analyzer), `null` is returned. - */ - static InterfaceType computeLeastUpperBound(InterfaceType i, InterfaceType j, - {@deprecated bool strong = true}) { - // compute set of supertypes - Set si = computeSuperinterfaceSet(i); - Set sj = computeSuperinterfaceSet(j); - // union si with i and sj with j - si.add(i); - sj.add(j); - // compute intersection, reference as set 's' - List s = _intersection(si, sj); - return computeTypeAtMaxUniqueDepth(s); - } - - /** - * Return the length of the longest inheritance path from the given [type] to - * Object. - * - * See [computeLeastUpperBound]. - */ - static int computeLongestInheritancePathToObject(InterfaceType type) => - _computeLongestInheritancePathToObject( - type, 0, new HashSet()); - - /** - * Returns the set of all superinterfaces of the given [type]. - * - * See [computeLeastUpperBound]. - */ - static Set computeSuperinterfaceSet(InterfaceType type, - {@deprecated bool strong = true}) => - _computeSuperinterfaceSet(type, new HashSet(), true); - - /** - * Return the type from the [types] list that has the longest inheritance path - * to Object of unique length. - */ - static InterfaceType computeTypeAtMaxUniqueDepth(List types) { - // for each element in Set s, compute the largest inheritance path to Object - List depths = new List.filled(types.length, 0); - int maxDepth = 0; - for (int n = 0; n < types.length; n++) { - depths[n] = computeLongestInheritancePathToObject(types[n]); - if (depths[n] > maxDepth) { - maxDepth = depths[n]; - } - } - // ensure that the currently computed maxDepth is unique, - // otherwise, decrement and test for uniqueness again - for (; maxDepth >= 0; maxDepth--) { - int indexOfLeastUpperBound = -1; - int numberOfTypesAtMaxDepth = 0; - for (int m = 0; m < depths.length; m++) { - if (depths[m] == maxDepth) { - numberOfTypesAtMaxDepth++; - indexOfLeastUpperBound = m; - } - } - if (numberOfTypesAtMaxDepth == 1) { - return types[indexOfLeastUpperBound]; - } - } - // Should be impossible--there should always be exactly one type with the - // maximum depth. - assert(false); - return null; - } - /** * If there is a single type which is at least as specific as all of the * types in [types], return it. Otherwise return `null`. @@ -2565,105 +2491,6 @@ class InterfaceTypeImpl extends TypeImpl implements InterfaceType { return context.typeSystem.getLeastUpperBound(first, second); } - /** - * Return the length of the longest inheritance path from a subtype of the - * given [type] to Object, where the given [depth] is the length of the - * longest path from the subtype to this type. The set of [visitedTypes] is - * used to prevent infinite recursion in the case of a cyclic type structure. - * - * See [computeLongestInheritancePathToObject], and [computeLeastUpperBound]. - */ - static int _computeLongestInheritancePathToObject( - InterfaceType type, int depth, HashSet visitedTypes) { - ClassElement classElement = type.element; - // Object case - if (type.isObject || visitedTypes.contains(classElement)) { - return depth; - } - int longestPath = 1; - try { - visitedTypes.add(classElement); - int pathLength; - - // loop through each of the superinterfaces recursively calling this - // method and keeping track of the longest path to return - for (InterfaceType interface in classElement.superclassConstraints) { - pathLength = _computeLongestInheritancePathToObject( - interface, depth + 1, visitedTypes); - if (pathLength > longestPath) { - longestPath = pathLength; - } - } - - // loop through each of the superinterfaces recursively calling this - // method and keeping track of the longest path to return - for (InterfaceType interface in classElement.interfaces) { - pathLength = _computeLongestInheritancePathToObject( - interface, depth + 1, visitedTypes); - if (pathLength > longestPath) { - longestPath = pathLength; - } - } - - // finally, perform this same check on the super type - // TODO(brianwilkerson) Does this also need to add in the number of mixin - // classes? - InterfaceType supertype = classElement.supertype; - if (supertype != null) { - pathLength = _computeLongestInheritancePathToObject( - supertype, depth + 1, visitedTypes); - if (pathLength > longestPath) { - longestPath = pathLength; - } - } - } finally { - visitedTypes.remove(classElement); - } - return longestPath; - } - - /** - * Add all of the superinterfaces of the given [type] to the given [set]. - * Return the [set] as a convenience. - * - * If [strong] mode is enabled (Dart 2), then the `Function` interface is - * ignored and not treated as a superinterface. - * - * See [computeSuperinterfaceSet], and [computeLeastUpperBound]. - */ - static Set _computeSuperinterfaceSet( - InterfaceType type, HashSet set, bool _) { - Element element = type.element; - if (element != null) { - List superinterfaces = type.interfaces; - for (InterfaceType superinterface in superinterfaces) { - if (!superinterface.isDartCoreFunction) { - if (set.add(superinterface)) { - _computeSuperinterfaceSet(superinterface, set, true); - } - } - } - InterfaceType supertype = type.superclass; - if (supertype != null && !supertype.isDartCoreFunction) { - if (set.add(supertype)) { - _computeSuperinterfaceSet(supertype, set, true); - } - } - } - return set; - } - - /** - * Return the intersection of the [first] and [second] sets of types, where - * intersection is based on the equality of the types themselves. - */ - static List _intersection( - Set first, Set second) { - Set result = new HashSet.from(first); - result.retainAll(second); - return new List.from(result); - } - /** * Return the "least upper bound" of the given types under the assumption that * the types have the same element and differ only in terms of the type diff --git a/pkg/analyzer/lib/src/generated/testing/element_factory.dart b/pkg/analyzer/lib/src/generated/testing/element_factory.dart index 07d762bc007b5..90f283a1f5fc1 100644 --- a/pkg/analyzer/lib/src/generated/testing/element_factory.dart +++ b/pkg/analyzer/lib/src/generated/testing/element_factory.dart @@ -16,6 +16,7 @@ import 'package:analyzer/src/generated/resolver.dart'; import 'package:analyzer/src/generated/source.dart'; import 'package:analyzer/src/generated/testing/ast_test_factory.dart'; import 'package:analyzer/src/generated/utilities_dart.dart'; +import 'package:meta/meta.dart'; import 'package:path/path.dart'; /** @@ -54,6 +55,26 @@ class ElementFactory { [List parameterNames]) => classElement(typeName, objectType, parameterNames); + static ClassElementImpl classElement3({ + @required String name, + List typeParameters, + List typeParameterNames = const [], + InterfaceType supertype, + List mixins = const [], + List interfaces = const [], + }) { + typeParameters ??= ElementFactory.typeParameters(typeParameterNames); + supertype ??= objectType; + + var element = ClassElementImpl(name, 0); + element.typeParameters = typeParameters; + element.supertype = supertype; + element.mixins = mixins; + element.interfaces = interfaces; + element.constructors = const []; + return element; + } + static classTypeAlias(String typeName, InterfaceType superclassType, [List parameterNames]) { ClassElementImpl element = diff --git a/pkg/analyzer/lib/src/generated/type_system.dart b/pkg/analyzer/lib/src/generated/type_system.dart index 85f0991bb05ce..4b66e3a535548 100644 --- a/pkg/analyzer/lib/src/generated/type_system.dart +++ b/pkg/analyzer/lib/src/generated/type_system.dart @@ -16,6 +16,7 @@ import 'package:analyzer/src/dart/analysis/driver.dart'; import 'package:analyzer/src/dart/element/element.dart'; import 'package:analyzer/src/dart/element/member.dart' show TypeParameterMember; import 'package:analyzer/src/dart/element/type.dart'; +import 'package:analyzer/src/dart/element/type_algebra.dart'; import 'package:analyzer/src/error/codes.dart' show StrongModeCode; import 'package:analyzer/src/generated/engine.dart' show AnalysisContext, AnalysisOptionsImpl; @@ -956,51 +957,10 @@ class Dart2TypeSystem extends TypeSystem { DartType _functionParameterBound(DartType f, DartType g) => getGreatestLowerBound(f, g); - /** - * This currently does not implement a very complete least upper bound - * algorithm, but handles a couple of the very common cases that are - * causing pain in real code. The current algorithm is: - * 1. If either of the types is a supertype of the other, return it. - * This is in fact the best result in this case. - * 2. If the two types have the same class element, then take the - * pointwise least upper bound of the type arguments. This is again - * the best result, except that the recursive calls may not return - * the true least upper bounds. The result is guaranteed to be a - * well-formed type under the assumption that the input types were - * well-formed (and assuming that the recursive calls return - * well-formed types). - * 3. Otherwise return the spec-defined least upper bound. This will - * be an upper bound, might (or might not) be least, and might - * (or might not) be a well-formed type. - * - * TODO(leafp): Use matchTypes or something similar here to handle the - * case where one of the types is a superclass (but not supertype) of - * the other, e.g. LUB(Iterable, List) = Iterable - * TODO(leafp): Figure out the right final algorithm and implement it. - */ @override DartType _interfaceLeastUpperBound(InterfaceType type1, InterfaceType type2) { - if (isSubtypeOf(type1, type2)) { - return type2; - } - if (isSubtypeOf(type2, type1)) { - return type1; - } - if (type1.element == type2.element) { - List tArgs1 = type1.typeArguments; - List tArgs2 = type2.typeArguments; - - assert(tArgs1.length == tArgs2.length); - List tArgs = new List(tArgs1.length); - for (int i = 0; i < tArgs1.length; i++) { - tArgs[i] = getLeastUpperBound(tArgs1[i], tArgs2[i]); - } - InterfaceTypeImpl lub = new InterfaceTypeImpl(type1.element); - lub.typeArguments = tArgs; - return lub; - } - return InterfaceTypeImpl.computeLeastUpperBound(type1, type2) ?? - typeProvider.dynamicType; + var helper = InterfaceLeastUpperBoundHelper(this); + return helper.compute(type1, type2); } /// Check that [f1] is a subtype of [f2]. @@ -1893,6 +1853,353 @@ class GenericInferrer { } } +/// The instantiation of a [ClassElement] with type arguments. +/// +/// It is not a [DartType] itself, because it does not have nullability. +/// But it should be used where nullability does not make sense - to specify +/// superclasses, mixins, and implemented interfaces. +class InstantiatedClass { + final ClassElement element; + final List arguments; + + final Substitution _substitution; + + InstantiatedClass(this.element, this.arguments) + : _substitution = Substitution.fromPairs( + element.typeParameters, + arguments, + ); + + /// Return the [InstantiatedClass] that corresponds to the [type] - with the + /// same element and type arguments, ignoring its nullability suffix. + factory InstantiatedClass.of(InterfaceType type) { + return InstantiatedClass(type.element, type.typeArguments); + } + + @override + int get hashCode { + var hash = 0x3fffffff & element.hashCode; + for (var i = 0; i < arguments.length; i++) { + hash = 0x3fffffff & (hash * 31 + (hash ^ arguments[i].hashCode)); + } + return hash; + } + + /// Return the interfaces that are directly implemented by this class. + List get interfaces { + var interfaces = element.interfaces; + + var result = List(interfaces.length); + for (var i = 0; i < interfaces.length; i++) { + var interface = interfaces[i]; + var substituted = _substitution.substituteType(interface); + result[i] = InstantiatedClass.of(substituted); + } + + return result; + } + + /// Return `true` if this type represents the type 'Function' defined in the + /// dart:core library. + bool get isDartCoreFunction { + return element.name == 'Function' && element.library.isDartCore; + } + + /// Return the superclass of this type, or `null` if this type represents + /// the class 'Object'. + InstantiatedClass get superclass { + var supertype = element.supertype; + if (supertype == null) return null; + + supertype = _substitution.substituteType(supertype); + return InstantiatedClass.of(supertype); + } + + @visibleForTesting + InterfaceType get withNullabilitySuffixNone { + return withNullability(NullabilitySuffix.none); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + if (other is InstantiatedClass) { + if (element != other.element) return false; + if (arguments.length != other.arguments.length) return false; + for (var i = 0; i < arguments.length; i++) { + if (arguments[i] != other.arguments[i]) return false; + } + return true; + } + return false; + } + + @override + String toString() { + var buffer = StringBuffer(); + buffer.write(element.name); + if (arguments.isNotEmpty) { + buffer.write('<'); + buffer.write(arguments.join(', ')); + buffer.write('>'); + } + return buffer.toString(); + } + + InterfaceType withNullability(NullabilitySuffix nullability) { + return InterfaceTypeImpl.explicit( + element, + arguments, + nullabilitySuffix: nullability, + ); + } +} + +class InterfaceLeastUpperBoundHelper { + final TypeSystem typeSystem; + + InterfaceLeastUpperBoundHelper(this.typeSystem); + + /// This currently does not implement a very complete least upper bound + /// algorithm, but handles a couple of the very common cases that are + /// causing pain in real code. The current algorithm is: + /// 1. If either of the types is a supertype of the other, return it. + /// This is in fact the best result in this case. + /// 2. If the two types have the same class element, then take the + /// pointwise least upper bound of the type arguments. This is again + /// the best result, except that the recursive calls may not return + /// the true least upper bounds. The result is guaranteed to be a + /// well-formed type under the assumption that the input types were + /// well-formed (and assuming that the recursive calls return + /// well-formed types). + /// 3. Otherwise return the spec-defined least upper bound. This will + /// be an upper bound, might (or might not) be least, and might + /// (or might not) be a well-formed type. + /// + /// TODO(leafp): Use matchTypes or something similar here to handle the + /// case where one of the types is a superclass (but not supertype) of + /// the other, e.g. LUB(Iterable, List) = Iterable + /// TODO(leafp): Figure out the right final algorithm and implement it. + InterfaceTypeImpl compute(InterfaceTypeImpl type1, InterfaceTypeImpl type2) { + var nullability = _chooseNullability(type1, type2); + + // Strip off nullability. + type1 = type1.withNullability(NullabilitySuffix.none); + type2 = type2.withNullability(NullabilitySuffix.none); + + if (typeSystem.isSubtypeOf(type1, type2)) { + return type2.withNullability(nullability); + } + if (typeSystem.isSubtypeOf(type2, type1)) { + return type1.withNullability(nullability); + } + + if (type1.element == type2.element) { + var args1 = type1.typeArguments; + var args2 = type2.typeArguments; + + assert(args1.length == args2.length); + var args = List(args1.length); + for (int i = 0; i < args1.length; i++) { + args[i] = typeSystem.getLeastUpperBound(args1[i], args2[i]); + } + + return new InterfaceTypeImpl.explicit( + type1.element, + args, + nullabilitySuffix: nullability, + ); + } + + var result = _computeLeastUpperBound( + InstantiatedClass.of(type1), + InstantiatedClass.of(type2), + ); + return result.withNullability(nullability); + } + + /// Compute the least upper bound of types [i] and [j], both of which are + /// known to be interface types. + /// + /// In the event that the algorithm fails (which might occur due to a bug in + /// the analyzer), `null` is returned. + InstantiatedClass _computeLeastUpperBound( + InstantiatedClass i, + InstantiatedClass j, + ) { + // compute set of supertypes + var si = computeSuperinterfaceSet(i); + var sj = computeSuperinterfaceSet(j); + + // union si with i and sj with j + si.add(i); + sj.add(j); + + // compute intersection, reference as set 's' + var s = _intersection(si, sj); + return _computeTypeAtMaxUniqueDepth(s); + } + + /** + * Return the length of the longest inheritance path from the [element] to + * Object. + */ + @visibleForTesting + static int computeLongestInheritancePathToObject(ClassElement element) { + return _computeLongestInheritancePathToObject( + element, + 0, + Set(), + ); + } + + /// Return all of the superinterfaces of the given [type]. + @visibleForTesting + static Set computeSuperinterfaceSet( + InstantiatedClass type) { + var result = Set(); + _addSuperinterfaces(result, type); + return result; + } + + /// Add all of the superinterfaces of the given [type] to the given [set]. + static void _addSuperinterfaces( + Set set, InstantiatedClass type) { + for (var interface in type.interfaces) { + if (!interface.isDartCoreFunction) { + if (set.add(interface)) { + _addSuperinterfaces(set, interface); + } + } + } + + var supertype = type.superclass; + if (supertype != null && !supertype.isDartCoreFunction) { + if (set.add(supertype)) { + _addSuperinterfaces(set, supertype); + } + } + } + + static NullabilitySuffix _chooseNullability( + InterfaceTypeImpl type1, + InterfaceTypeImpl type2, + ) { + var nullability1 = type1.nullabilitySuffix; + var nullability2 = type2.nullabilitySuffix; + if (nullability1 == NullabilitySuffix.question || + nullability2 == NullabilitySuffix.question) { + return NullabilitySuffix.question; + } else if (nullability1 == NullabilitySuffix.star || + nullability2 == NullabilitySuffix.star) { + return NullabilitySuffix.star; + } + return NullabilitySuffix.none; + } + + /// Return the length of the longest inheritance path from a subtype of the + /// given [element] to Object, where the given [depth] is the length of the + /// longest path from the subtype to this type. The set of [visitedElements] + /// is used to prevent infinite recursion in the case of a cyclic type + /// structure. + static int _computeLongestInheritancePathToObject( + ClassElement element, int depth, Set visitedElements) { + // Object case + if (element.supertype == null || visitedElements.contains(element)) { + return depth; + } + int longestPath = 1; + try { + visitedElements.add(element); + int pathLength; + + // loop through each of the superinterfaces recursively calling this + // method and keeping track of the longest path to return + for (InterfaceType interface in element.superclassConstraints) { + pathLength = _computeLongestInheritancePathToObject( + interface.element, depth + 1, visitedElements); + if (pathLength > longestPath) { + longestPath = pathLength; + } + } + + // loop through each of the superinterfaces recursively calling this + // method and keeping track of the longest path to return + for (InterfaceType interface in element.interfaces) { + pathLength = _computeLongestInheritancePathToObject( + interface.element, depth + 1, visitedElements); + if (pathLength > longestPath) { + longestPath = pathLength; + } + } + + // finally, perform this same check on the super type + // TODO(brianwilkerson) Does this also need to add in the number of mixin + // classes? + InterfaceType supertype = element.supertype; + if (supertype != null) { + pathLength = _computeLongestInheritancePathToObject( + supertype.element, depth + 1, visitedElements); + if (pathLength > longestPath) { + longestPath = pathLength; + } + } + } finally { + visitedElements.remove(element); + } + return longestPath; + } + + /// Return the type from the [types] list that has the longest inheritance + /// path to Object of unique length. + static InstantiatedClass _computeTypeAtMaxUniqueDepth( + List types, + ) { + // for each element in Set s, compute the largest inheritance path to Object + List depths = new List.filled(types.length, 0); + int maxDepth = 0; + for (int i = 0; i < types.length; i++) { + depths[i] = computeLongestInheritancePathToObject(types[i].element); + if (depths[i] > maxDepth) { + maxDepth = depths[i]; + } + } + // ensure that the currently computed maxDepth is unique, + // otherwise, decrement and test for uniqueness again + for (; maxDepth >= 0; maxDepth--) { + int indexOfLeastUpperBound = -1; + int numberOfTypesAtMaxDepth = 0; + for (int m = 0; m < depths.length; m++) { + if (depths[m] == maxDepth) { + numberOfTypesAtMaxDepth++; + indexOfLeastUpperBound = m; + } + } + if (numberOfTypesAtMaxDepth == 1) { + return types[indexOfLeastUpperBound]; + } + } + // Should be impossible--there should always be exactly one type with the + // maximum depth. + assert(false); + return null; + } + + /** + * Return the intersection of the [first] and [second] sets of types, where + * intersection is based on the equality of the types themselves. + */ + static List _intersection( + Set first, + Set second, + ) { + var result = first.toSet(); + result.retainAll(second); + return result.toList(); + } +} + /// Used to check for infinite loops, if we repeat the same type comparison. class TypeComparison { final DartType lhs; diff --git a/pkg/analyzer/test/generated/static_type_analyzer_test.dart b/pkg/analyzer/test/generated/static_type_analyzer_test.dart index 36b6c24571116..c12fed7270803 100644 --- a/pkg/analyzer/test/generated/static_type_analyzer_test.dart +++ b/pkg/analyzer/test/generated/static_type_analyzer_test.dart @@ -490,7 +490,7 @@ class StaticTypeAnalyzerTest extends EngineTestCase with ResourceProviderMixin { _resolvedVariable(_typeProvider.doubleType, 'd'), TokenType.QUESTION_QUESTION_EQ, _resolvedInteger(0)); - expect(_analyze(node), same(_typeProvider.numType)); + expect(_analyze(node), _typeProvider.numType); _listener.assertNoErrors(); } @@ -549,7 +549,7 @@ class StaticTypeAnalyzerTest extends EngineTestCase with ResourceProviderMixin { // 1 ?? 1.5 Expression node = AstTestFactory.binaryExpression( _resolvedInteger(1), TokenType.QUESTION_QUESTION, _resolvedDouble(1.5)); - expect(_analyze(node), same(_typeProvider.numType)); + expect(_analyze(node), _typeProvider.numType); _listener.assertNoErrors(); } @@ -667,7 +667,7 @@ class StaticTypeAnalyzerTest extends EngineTestCase with ResourceProviderMixin { AstTestFactory.booleanLiteral(true), _resolvedDouble(1.0), _resolvedInteger(0)); - expect(_analyze(node), same(_typeProvider.numType)); + expect(_analyze(node), _typeProvider.numType); _listener.assertNoErrors(); } diff --git a/pkg/analyzer/test/generated/type_system_test.dart b/pkg/analyzer/test/generated/type_system_test.dart index 0c7074ba1b9ae..d544271334862 100644 --- a/pkg/analyzer/test/generated/type_system_test.dart +++ b/pkg/analyzer/test/generated/type_system_test.dart @@ -1740,6 +1740,82 @@ class LeastUpperBoundTest extends BoundTestBase { _checkLeastUpperBound(typeB, typeC, typeB); } + void test_directSuperclass_nullability() { + ClassElement _classElement(String name, InterfaceType supertype) { + return ElementFactory.classElement3(name: name, supertype: supertype); + } + + InterfaceTypeImpl _interfaceType( + ClassElement element, + NullabilitySuffix nullabilitySuffix, + ) { + return InterfaceTypeImpl.explicit( + element, + const [], + nullabilitySuffix: nullabilitySuffix, + ); + } + + var aElement = ElementFactory.classElement3(name: 'A'); + var aQuestion = _interfaceType(aElement, NullabilitySuffix.question); + var aStar = _interfaceType(aElement, NullabilitySuffix.star); + var aNone = _interfaceType(aElement, NullabilitySuffix.none); + + var bElementStar = _classElement('B', aStar); + var bElementNone = _classElement('B', aNone); + + InterfaceTypeImpl _bTypeStarElement(NullabilitySuffix nullability) { + return _interfaceType(bElementStar, nullability); + } + + InterfaceTypeImpl _bTypeNoneElement(NullabilitySuffix nullability) { + return _interfaceType(bElementNone, nullability); + } + + var bStarQuestion = _bTypeStarElement(NullabilitySuffix.question); + var bStarStar = _bTypeStarElement(NullabilitySuffix.star); + var bStarNone = _bTypeStarElement(NullabilitySuffix.none); + + var bNoneQuestion = _bTypeNoneElement(NullabilitySuffix.question); + var bNoneStar = _bTypeNoneElement(NullabilitySuffix.star); + var bNoneNone = _bTypeNoneElement(NullabilitySuffix.none); + + void assertLUB(DartType type1, DartType type2, DartType expected) { + _assertInterfaceType( + typeSystem.getLeastUpperBound(type1, type2), + expected, + ); + _assertInterfaceType( + typeSystem.getLeastUpperBound(type2, type1), + expected, + ); + } + + assertLUB(bStarQuestion, aQuestion, aQuestion); + assertLUB(bStarQuestion, aStar, aQuestion); + assertLUB(bStarQuestion, aNone, aQuestion); + + assertLUB(bStarStar, aQuestion, aQuestion); + assertLUB(bStarStar, aStar, aStar); + assertLUB(bStarStar, aNone, aStar); + + assertLUB(bStarNone, aQuestion, aQuestion); + assertLUB(bStarNone, aStar, aStar); + assertLUB(bStarNone, aNone, aNone); + + assertLUB(bNoneQuestion, aQuestion, aQuestion); + assertLUB(bNoneQuestion, aStar, aQuestion); + assertLUB(bNoneQuestion, aNone, aQuestion); + + assertLUB(bNoneStar, aQuestion, aQuestion); + assertLUB(bNoneStar, aStar, aStar); + assertLUB(bNoneStar, aNone, aStar); + + assertLUB(bNoneNone, aQuestion, aQuestion); + assertLUB(bNoneNone, aStar, aStar); + assertLUB(bNoneNone, aNone, aNone); + } + void test_dynamic_bottom() { _checkLeastUpperBound(dynamicType, bottomType, dynamicType); } @@ -1768,6 +1844,48 @@ class LeastUpperBoundTest extends BoundTestBase { _checkLeastUpperBound(interfaceType, simpleFunctionType, objectType); } + void test_interface_sameElement_nullability() { + var aElement = ElementFactory.classElement3(name: 'A'); + + InterfaceTypeImpl _interfaceType( + ClassElement element, + NullabilitySuffix nullabilitySuffix, + ) { + return InterfaceTypeImpl.explicit( + element, + const [], + nullabilitySuffix: nullabilitySuffix, + ); + } + + var aQuestion = _interfaceType(aElement, NullabilitySuffix.question); + var aStar = _interfaceType(aElement, NullabilitySuffix.star); + var aNone = _interfaceType(aElement, NullabilitySuffix.none); + + void assertLUB(DartType type1, DartType type2, DartType expected) { + _assertInterfaceType( + typeSystem.getLeastUpperBound(type1, type2), + expected, + ); + _assertInterfaceType( + typeSystem.getLeastUpperBound(type2, type1), + expected, + ); + } + + assertLUB(aQuestion, aQuestion, aQuestion); + assertLUB(aQuestion, aStar, aQuestion); + assertLUB(aQuestion, aNone, aQuestion); + + assertLUB(aStar, aQuestion, aQuestion); + assertLUB(aStar, aStar, aStar); + assertLUB(aStar, aNone, aStar); + + assertLUB(aNone, aQuestion, aQuestion); + assertLUB(aNone, aStar, aStar); + assertLUB(aNone, aNone, aNone); + } + void test_mixinCase() { // class A // class B extends A @@ -1869,6 +1987,120 @@ class LeastUpperBoundTest extends BoundTestBase { _checkLeastUpperBound(typeB, typeC, typeA); } + void test_sharedSuperclass1_nullability() { + ClassElementImpl _classElement(String name, InterfaceType supertype) { + return ElementFactory.classElement3(name: name, supertype: supertype); + } + + InterfaceTypeImpl _interfaceType( + ClassElement element, + NullabilitySuffix nullabilitySuffix, + ) { + return InterfaceTypeImpl.explicit( + element, + const [], + nullabilitySuffix: nullabilitySuffix, + ); + } + + var aElement = _classElement('A', null); + + var aQuestion = _interfaceType(aElement, NullabilitySuffix.question); + var aStar = _interfaceType(aElement, NullabilitySuffix.star); + var aNone = _interfaceType(aElement, NullabilitySuffix.none); + + var bElementNone = _classElement('B', aNone); + var bElementStar = _classElement('B', aStar); + + var cElementNone = _classElement('C', aNone); + var cElementStar = _classElement('C', aStar); + + InterfaceTypeImpl bTypeElementNone(NullabilitySuffix nullability) { + return _interfaceType(bElementNone, nullability); + } + + InterfaceTypeImpl bTypeElementStar(NullabilitySuffix nullability) { + return _interfaceType(bElementStar, nullability); + } + + var bNoneQuestion = bTypeElementNone(NullabilitySuffix.question); + var bNoneStar = bTypeElementNone(NullabilitySuffix.star); + var bNoneNone = bTypeElementNone(NullabilitySuffix.none); + + var bStarQuestion = bTypeElementStar(NullabilitySuffix.question); + var bStarStar = bTypeElementStar(NullabilitySuffix.star); + var bStarNone = bTypeElementStar(NullabilitySuffix.none); + + InterfaceTypeImpl cTypeElementNone(NullabilitySuffix nullability) { + return _interfaceType(cElementNone, nullability); + } + + InterfaceTypeImpl cTypeElementStar(NullabilitySuffix nullability) { + return _interfaceType(cElementStar, nullability); + } + + var cNoneQuestion = cTypeElementNone(NullabilitySuffix.question); + var cNoneStar = cTypeElementNone(NullabilitySuffix.star); + var cNoneNone = cTypeElementNone(NullabilitySuffix.none); + + var cStarQuestion = cTypeElementStar(NullabilitySuffix.question); + var cStarStar = cTypeElementStar(NullabilitySuffix.star); + var cStarNone = cTypeElementStar(NullabilitySuffix.none); + + void assertLUB(DartType type1, DartType type2, DartType expected) { + _assertInterfaceType( + typeSystem.getLeastUpperBound(type1, type2), + expected, + ); + _assertInterfaceType( + typeSystem.getLeastUpperBound(type2, type1), + expected, + ); + } + + assertLUB(bNoneQuestion, cNoneQuestion, aQuestion); + assertLUB(bNoneQuestion, cNoneStar, aQuestion); + assertLUB(bNoneQuestion, cNoneNone, aQuestion); + assertLUB(bNoneQuestion, cStarQuestion, aQuestion); + assertLUB(bNoneQuestion, cStarStar, aQuestion); + assertLUB(bNoneQuestion, cStarNone, aQuestion); + + assertLUB(bNoneStar, cNoneQuestion, aQuestion); + assertLUB(bNoneStar, cNoneStar, aStar); + assertLUB(bNoneStar, cNoneNone, aStar); + assertLUB(bNoneStar, cStarQuestion, aQuestion); + assertLUB(bNoneStar, cStarStar, aStar); + assertLUB(bNoneStar, cStarNone, aStar); + + assertLUB(bNoneNone, cNoneQuestion, aQuestion); + assertLUB(bNoneNone, cNoneStar, aStar); + assertLUB(bNoneNone, cNoneNone, aNone); + assertLUB(bNoneNone, cStarQuestion, aQuestion); + assertLUB(bNoneNone, cStarStar, aStar); + assertLUB(bNoneNone, cStarNone, aNone); + + assertLUB(bStarQuestion, cNoneQuestion, aQuestion); + assertLUB(bStarQuestion, cNoneStar, aQuestion); + assertLUB(bStarQuestion, cNoneNone, aQuestion); + assertLUB(bStarQuestion, cStarQuestion, aQuestion); + assertLUB(bStarQuestion, cStarStar, aQuestion); + assertLUB(bStarQuestion, cStarNone, aQuestion); + + assertLUB(bStarStar, cNoneQuestion, aQuestion); + assertLUB(bStarStar, cNoneStar, aStar); + assertLUB(bStarStar, cNoneNone, aStar); + assertLUB(bStarStar, cStarQuestion, aQuestion); + assertLUB(bStarStar, cStarStar, aStar); + assertLUB(bStarStar, cStarNone, aStar); + + assertLUB(bStarNone, cNoneQuestion, aQuestion); + assertLUB(bStarNone, cNoneStar, aStar); + assertLUB(bStarNone, cNoneNone, aNone); + assertLUB(bStarNone, cStarQuestion, aQuestion); + assertLUB(bStarNone, cStarStar, aStar); + assertLUB(bStarNone, cStarNone, aNone); + } + void test_sharedSuperclass2() { ClassElementImpl classA = ElementFactory.classElement2("A"); ClassElementImpl classB = ElementFactory.classElement("B", classA.type); @@ -2076,6 +2308,17 @@ class LeastUpperBoundTest extends BoundTestBase { ); } } + + /// TODO(scheglov) Replace with `DartType.==` when + /// https://github.com/dart-lang/sdk/issues/37587 is fixed. + void _assertInterfaceType(DartType type, InterfaceTypeImpl expectedType) { + if (type is InterfaceTypeImpl) { + expect(type, expectedType); + expect(type.nullabilitySuffix, expectedType.nullabilitySuffix); + } else { + fail('Expected interface: (${type.runtimeType}) $type'); + } + } } @reflectiveTest diff --git a/pkg/analyzer/test/src/dart/element/element_test.dart b/pkg/analyzer/test/src/dart/element/element_test.dart index 792b693968e1b..e724a66b134d8 100644 --- a/pkg/analyzer/test/src/dart/element/element_test.dart +++ b/pkg/analyzer/test/src/dart/element/element_test.dart @@ -2032,334 +2032,6 @@ class InterfaceTypeImplTest extends EngineTestCase { expect(result, classA.type.instantiate([classC.type])); } - void test_computeLongestInheritancePathToObject_multipleInterfacePaths() { - // - // Object - // | - // A - // / \ - // B C - // | | - // | D - // \ / - // E - // - ClassElementImpl classA = ElementFactory.classElement2("A"); - ClassElementImpl classB = ElementFactory.classElement2("B"); - ClassElementImpl classC = ElementFactory.classElement2("C"); - ClassElementImpl classD = ElementFactory.classElement2("D"); - ClassElementImpl classE = ElementFactory.classElement2("E"); - classB.interfaces = [classA.type]; - classC.interfaces = [classA.type]; - classD.interfaces = [classC.type]; - classE.interfaces = [classB.type, classD.type]; - // 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(InterfaceTypeImpl.computeLongestInheritancePathToObject(classB.type), - 2); - expect(InterfaceTypeImpl.computeLongestInheritancePathToObject(classE.type), - 4); - } - - void test_computeLongestInheritancePathToObject_multipleSuperclassPaths() { - // - // Object - // | - // A - // / \ - // B C - // | | - // | D - // \ / - // E - // - ClassElement classA = ElementFactory.classElement2("A"); - ClassElement classB = ElementFactory.classElement("B", classA.type); - ClassElement classC = ElementFactory.classElement("C", classA.type); - ClassElement classD = ElementFactory.classElement("D", classC.type); - ClassElementImpl classE = ElementFactory.classElement("E", classB.type); - classE.interfaces = [classD.type]; - // 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(InterfaceTypeImpl.computeLongestInheritancePathToObject(classB.type), - 2); - expect(InterfaceTypeImpl.computeLongestInheritancePathToObject(classE.type), - 4); - } - - void test_computeLongestInheritancePathToObject_object() { - // - // Object - // | - // A - // - ClassElement classA = ElementFactory.classElement2("A"); - InterfaceType object = classA.supertype; - expect(InterfaceTypeImpl.computeLongestInheritancePathToObject(object), 0); - } - - void test_computeLongestInheritancePathToObject_recursion() { - ClassElementImpl classA = ElementFactory.classElement2("A"); - ClassElementImpl classB = ElementFactory.classElement("B", classA.type); - classA.supertype = classB.type; - expect(InterfaceTypeImpl.computeLongestInheritancePathToObject(classA.type), - 2); - } - - void test_computeLongestInheritancePathToObject_singleInterfacePath() { - // - // Object - // | - // A - // | - // B - // | - // C - // - ClassElementImpl classA = ElementFactory.classElement2("A"); - ClassElementImpl classB = ElementFactory.classElement2("B"); - ClassElementImpl classC = ElementFactory.classElement2("C"); - classB.interfaces = [classA.type]; - classC.interfaces = [classB.type]; - expect(InterfaceTypeImpl.computeLongestInheritancePathToObject(classA.type), - 1); - expect(InterfaceTypeImpl.computeLongestInheritancePathToObject(classB.type), - 2); - expect(InterfaceTypeImpl.computeLongestInheritancePathToObject(classC.type), - 3); - } - - void test_computeLongestInheritancePathToObject_singleSuperclassPath() { - // - // Object - // | - // A - // | - // B - // | - // C - // - ClassElement classA = ElementFactory.classElement2("A"); - ClassElement classB = ElementFactory.classElement("B", classA.type); - ClassElement classC = ElementFactory.classElement("C", classB.type); - expect(InterfaceTypeImpl.computeLongestInheritancePathToObject(classA.type), - 1); - expect(InterfaceTypeImpl.computeLongestInheritancePathToObject(classB.type), - 2); - expect(InterfaceTypeImpl.computeLongestInheritancePathToObject(classC.type), - 3); - } - - void test_computeSuperinterfaceSet_genericInterfacePath() { - // - // A - // | implements - // B - // | implements - // C - // - // D - // - ClassElementImpl classA = ElementFactory.classElement2("A"); - ClassElementImpl classB = ElementFactory.classElement2("B", ["T"]); - ClassElementImpl classC = ElementFactory.classElement2("C", ["T"]); - ClassElement classD = ElementFactory.classElement2("D"); - InterfaceType typeA = classA.type; - classB.interfaces = [typeA]; - InterfaceTypeImpl typeBT = new InterfaceTypeImpl(classB); - DartType typeT = classC.type.typeArguments[0]; - typeBT.typeArguments = [typeT]; - classC.interfaces = [typeBT]; - // A - Set superinterfacesOfA = - InterfaceTypeImpl.computeSuperinterfaceSet(typeA); - expect(superinterfacesOfA, hasLength(1)); - InterfaceType typeObject = ElementFactory.object.type; - expect(superinterfacesOfA.contains(typeObject), isTrue); - // B - InterfaceTypeImpl typeBD = new InterfaceTypeImpl(classB); - typeBD.typeArguments = [classD.type]; - Set superinterfacesOfBD = - InterfaceTypeImpl.computeSuperinterfaceSet(typeBD); - expect(superinterfacesOfBD, hasLength(2)); - expect(superinterfacesOfBD.contains(typeObject), isTrue); - expect(superinterfacesOfBD.contains(typeA), isTrue); - // C - InterfaceTypeImpl typeCD = new InterfaceTypeImpl(classC); - typeCD.typeArguments = [classD.type]; - Set superinterfacesOfCD = - InterfaceTypeImpl.computeSuperinterfaceSet(typeCD); - expect(superinterfacesOfCD, hasLength(3)); - expect(superinterfacesOfCD.contains(typeObject), isTrue); - expect(superinterfacesOfCD.contains(typeA), isTrue); - expect(superinterfacesOfCD.contains(typeBD), isTrue); - } - - void test_computeSuperinterfaceSet_genericSuperclassPath() { - // - // A - // | - // B - // | - // C - // - // D - // - ClassElement classA = ElementFactory.classElement2("A"); - InterfaceType typeA = classA.type; - ClassElement classB = ElementFactory.classElement("B", typeA, ["T"]); - ClassElementImpl classC = ElementFactory.classElement2("C", ["T"]); - InterfaceTypeImpl typeBT = new InterfaceTypeImpl(classB); - DartType typeT = classC.type.typeArguments[0]; - typeBT.typeArguments = [typeT]; - classC.supertype = typeBT; - ClassElement classD = ElementFactory.classElement2("D"); - // A - Set superinterfacesOfA = - InterfaceTypeImpl.computeSuperinterfaceSet(typeA); - expect(superinterfacesOfA, hasLength(1)); - InterfaceType typeObject = ElementFactory.object.type; - expect(superinterfacesOfA.contains(typeObject), isTrue); - // B - InterfaceTypeImpl typeBD = new InterfaceTypeImpl(classB); - typeBD.typeArguments = [classD.type]; - Set superinterfacesOfBD = - InterfaceTypeImpl.computeSuperinterfaceSet(typeBD); - expect(superinterfacesOfBD, hasLength(2)); - expect(superinterfacesOfBD.contains(typeObject), isTrue); - expect(superinterfacesOfBD.contains(typeA), isTrue); - // C - InterfaceTypeImpl typeCD = new InterfaceTypeImpl(classC); - typeCD.typeArguments = [classD.type]; - Set superinterfacesOfCD = - InterfaceTypeImpl.computeSuperinterfaceSet(typeCD); - expect(superinterfacesOfCD, hasLength(3)); - expect(superinterfacesOfCD.contains(typeObject), isTrue); - expect(superinterfacesOfCD.contains(typeA), isTrue); - expect(superinterfacesOfCD.contains(typeBD), isTrue); - } - - void test_computeSuperinterfaceSet_multipleInterfacePaths() { - ClassElementImpl classA = ElementFactory.classElement2("A"); - ClassElementImpl classB = ElementFactory.classElement2("B"); - ClassElementImpl classC = ElementFactory.classElement2("C"); - ClassElementImpl classD = ElementFactory.classElement2("D"); - ClassElementImpl classE = ElementFactory.classElement2("E"); - classB.interfaces = [classA.type]; - classC.interfaces = [classA.type]; - classD.interfaces = [classC.type]; - classE.interfaces = [classB.type, classD.type]; - // D - Set superinterfacesOfD = - InterfaceTypeImpl.computeSuperinterfaceSet(classD.type); - expect(superinterfacesOfD, hasLength(3)); - expect(superinterfacesOfD.contains(ElementFactory.object.type), isTrue); - expect(superinterfacesOfD.contains(classA.type), isTrue); - expect(superinterfacesOfD.contains(classC.type), isTrue); - // E - Set superinterfacesOfE = - InterfaceTypeImpl.computeSuperinterfaceSet(classE.type); - expect(superinterfacesOfE, hasLength(5)); - expect(superinterfacesOfE.contains(ElementFactory.object.type), isTrue); - expect(superinterfacesOfE.contains(classA.type), isTrue); - expect(superinterfacesOfE.contains(classB.type), isTrue); - expect(superinterfacesOfE.contains(classC.type), isTrue); - expect(superinterfacesOfE.contains(classD.type), isTrue); - } - - void test_computeSuperinterfaceSet_multipleSuperclassPaths() { - ClassElement classA = ElementFactory.classElement2("A"); - ClassElement classB = ElementFactory.classElement("B", classA.type); - ClassElement classC = ElementFactory.classElement("C", classA.type); - ClassElement classD = ElementFactory.classElement("D", classC.type); - ClassElementImpl classE = ElementFactory.classElement("E", classB.type); - classE.interfaces = [classD.type]; - // D - Set superinterfacesOfD = - InterfaceTypeImpl.computeSuperinterfaceSet(classD.type); - expect(superinterfacesOfD, hasLength(3)); - expect(superinterfacesOfD.contains(ElementFactory.object.type), isTrue); - expect(superinterfacesOfD.contains(classA.type), isTrue); - expect(superinterfacesOfD.contains(classC.type), isTrue); - // E - Set superinterfacesOfE = - InterfaceTypeImpl.computeSuperinterfaceSet(classE.type); - expect(superinterfacesOfE, hasLength(5)); - expect(superinterfacesOfE.contains(ElementFactory.object.type), isTrue); - expect(superinterfacesOfE.contains(classA.type), isTrue); - expect(superinterfacesOfE.contains(classB.type), isTrue); - expect(superinterfacesOfE.contains(classC.type), isTrue); - expect(superinterfacesOfE.contains(classD.type), isTrue); - } - - void test_computeSuperinterfaceSet_recursion() { - ClassElementImpl classA = ElementFactory.classElement2("A"); - ClassElementImpl classB = ElementFactory.classElement("B", classA.type); - classA.supertype = classB.type; - Set superinterfacesOfB = - InterfaceTypeImpl.computeSuperinterfaceSet(classB.type); - expect(superinterfacesOfB, hasLength(2)); - } - - void test_computeSuperinterfaceSet_singleInterfacePath() { - ClassElementImpl classA = ElementFactory.classElement2("A"); - ClassElementImpl classB = ElementFactory.classElement2("B"); - ClassElementImpl classC = ElementFactory.classElement2("C"); - classB.interfaces = [classA.type]; - classC.interfaces = [classB.type]; - // A - Set superinterfacesOfA = - InterfaceTypeImpl.computeSuperinterfaceSet(classA.type); - expect(superinterfacesOfA, hasLength(1)); - expect(superinterfacesOfA.contains(ElementFactory.object.type), isTrue); - // B - Set superinterfacesOfB = - InterfaceTypeImpl.computeSuperinterfaceSet(classB.type); - expect(superinterfacesOfB, hasLength(2)); - expect(superinterfacesOfB.contains(ElementFactory.object.type), isTrue); - expect(superinterfacesOfB.contains(classA.type), isTrue); - // C - Set superinterfacesOfC = - InterfaceTypeImpl.computeSuperinterfaceSet(classC.type); - expect(superinterfacesOfC, hasLength(3)); - expect(superinterfacesOfC.contains(ElementFactory.object.type), isTrue); - expect(superinterfacesOfC.contains(classA.type), isTrue); - expect(superinterfacesOfC.contains(classB.type), isTrue); - } - - void test_computeSuperinterfaceSet_singleSuperclassPath() { - // - // A - // | - // B - // | - // C - // - ClassElement classA = ElementFactory.classElement2("A"); - ClassElement classB = ElementFactory.classElement("B", classA.type); - ClassElement classC = ElementFactory.classElement("C", classB.type); - // A - Set superinterfacesOfA = - InterfaceTypeImpl.computeSuperinterfaceSet(classA.type); - expect(superinterfacesOfA, hasLength(1)); - expect(superinterfacesOfA.contains(ElementFactory.object.type), isTrue); - // B - Set superinterfacesOfB = - InterfaceTypeImpl.computeSuperinterfaceSet(classB.type); - expect(superinterfacesOfB, hasLength(2)); - expect(superinterfacesOfB.contains(ElementFactory.object.type), isTrue); - expect(superinterfacesOfB.contains(classA.type), isTrue); - // C - Set superinterfacesOfC = - InterfaceTypeImpl.computeSuperinterfaceSet(classC.type); - expect(superinterfacesOfC, hasLength(3)); - expect(superinterfacesOfC.contains(ElementFactory.object.type), isTrue); - expect(superinterfacesOfC.contains(classA.type), isTrue); - expect(superinterfacesOfC.contains(classB.type), isTrue); - } - void test_creation() { expect(new InterfaceTypeImpl(ElementFactory.classElement2("A")), isNotNull); } diff --git a/pkg/analyzer/test/src/dart/element/least_upper_bound_helper_test.dart b/pkg/analyzer/test/src/dart/element/least_upper_bound_helper_test.dart new file mode 100644 index 0000000000000..e225632d2f47e --- /dev/null +++ b/pkg/analyzer/test/src/dart/element/least_upper_bound_helper_test.dart @@ -0,0 +1,471 @@ +// 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/type.dart'; +import 'package:analyzer/src/generated/testing/element_factory.dart'; +import 'package:analyzer/src/generated/testing/test_type_provider.dart'; +import 'package:analyzer/src/generated/type_system.dart'; +import 'package:test/test.dart'; +import 'package:test_reflective_loader/test_reflective_loader.dart'; + +import '../../../generated/test_support.dart'; + +main() { + defineReflectiveSuite(() { + defineReflectiveTests(InterfaceLeastUpperBoundHelperTest); + }); +} + +@reflectiveTest +class InterfaceLeastUpperBoundHelperTest extends EngineTestCase { + /** + * The type provider used to access the types. + */ + TestTypeProvider _typeProvider; + + @override + void setUp() { + super.setUp(); + _typeProvider = new TestTypeProvider(); + } + + void test_computeLongestInheritancePathToObject_multipleInterfacePaths() { + // + // Object + // | + // A + // / \ + // B C + // | | + // | D + // \ / + // E + // + ClassElementImpl classA = ElementFactory.classElement2("A"); + ClassElementImpl classB = ElementFactory.classElement2("B"); + ClassElementImpl classC = ElementFactory.classElement2("C"); + ClassElementImpl classD = ElementFactory.classElement2("D"); + ClassElementImpl classE = ElementFactory.classElement2("E"); + classB.interfaces = [classA.type]; + classC.interfaces = [classA.type]; + classD.interfaces = [classC.type]; + classE.interfaces = [classB.type, classD.type]; + // 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_computeLongestInheritancePathToObject_multipleSuperclassPaths() { + // + // Object + // | + // A + // / \ + // B C + // | | + // | D + // \ / + // E + // + ClassElement classA = ElementFactory.classElement2("A"); + ClassElement classB = ElementFactory.classElement("B", classA.type); + ClassElement classC = ElementFactory.classElement("C", classA.type); + ClassElement classD = ElementFactory.classElement("D", classC.type); + ClassElementImpl classE = ElementFactory.classElement("E", classB.type); + classE.interfaces = [classD.type]; + // 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_computeLongestInheritancePathToObject_object() { + expect(_longestPathToObject(_typeProvider.objectType.element), 0); + } + + void test_computeLongestInheritancePathToObject_recursion() { + ClassElementImpl classA = ElementFactory.classElement2("A"); + ClassElementImpl classB = ElementFactory.classElement("B", classA.type); + classA.supertype = classB.type; + expect(_longestPathToObject(classA), 2); + } + + void test_computeLongestInheritancePathToObject_singleInterfacePath() { + // + // Object + // | + // A + // | + // B + // | + // C + // + ClassElementImpl classA = ElementFactory.classElement2("A"); + ClassElementImpl classB = ElementFactory.classElement2("B"); + ClassElementImpl classC = ElementFactory.classElement2("C"); + classB.interfaces = [classA.type]; + classC.interfaces = [classB.type]; + expect(_longestPathToObject(classA), 1); + expect(_longestPathToObject(classB), 2); + expect(_longestPathToObject(classC), 3); + } + + void test_computeLongestInheritancePathToObject_singleSuperclassPath() { + // + // Object + // | + // A + // | + // B + // | + // C + // + ClassElement classA = ElementFactory.classElement2("A"); + ClassElement classB = ElementFactory.classElement("B", classA.type); + ClassElement classC = ElementFactory.classElement("C", classB.type); + expect(_longestPathToObject(classA), 1); + expect(_longestPathToObject(classB), 2); + expect(_longestPathToObject(classC), 3); + } + + void test_computeSuperinterfaceSet_genericInterfacePath() { + // + // A + // | implements + // B + // | implements + // C + // + // D + // + + var instObject = InstantiatedClass.of(_typeProvider.objectType); + + ClassElementImpl classA = ElementFactory.classElement2('A'); + var instA = InstantiatedClass(classA, const []); + + var classB = ElementFactory.classElement3( + name: 'B', + typeParameterNames: ['T'], + interfaces: [instA.withNullabilitySuffixNone], + ); + + var typeParametersC = ElementFactory.typeParameters(['T']); + var classC = ElementFactory.classElement3( + name: 'B', + typeParameters: typeParametersC, + interfaces: [ + InstantiatedClass( + classB, + [typeParametersC[0].type], + ).withNullabilitySuffixNone, + ], + ); + + var classD = ElementFactory.classElement2('D'); + + InterfaceTypeImpl typeBT = new InterfaceTypeImpl(classB); + DartType typeT = classC.type.typeArguments[0]; + typeBT.typeArguments = [typeT]; + classC.interfaces = [typeBT]; + + // A + expect( + _superInterfaces(instA), + unorderedEquals([instObject]), + ); + + // B + expect( + _superInterfaces( + InstantiatedClass(classB, [classD.type]), + ), + unorderedEquals([instObject, instA]), + ); + + // C + expect( + _superInterfaces( + InstantiatedClass(classC, [classD.type]), + ), + unorderedEquals([ + instObject, + instA, + InstantiatedClass(classB, [classD.type]), + ]), + ); + } + + void test_computeSuperinterfaceSet_genericSuperclassPath() { + // + // A + // | + // B + // | + // C + // + // D + // + + var instObject = InstantiatedClass.of(_typeProvider.objectType); + + ClassElementImpl classA = ElementFactory.classElement2('A'); + var instA = InstantiatedClass(classA, const []); + + var classB = ElementFactory.classElement3( + name: 'B', + typeParameterNames: ['T'], + supertype: instA.withNullabilitySuffixNone, + ); + + var typeParametersC = ElementFactory.typeParameters(['T']); + var classC = ElementFactory.classElement3( + name: 'B', + typeParameters: typeParametersC, + supertype: InstantiatedClass( + classB, + [typeParametersC[0].type], + ).withNullabilitySuffixNone, + ); + + var classD = ElementFactory.classElement2('D'); + + // A + expect( + _superInterfaces(instA), + unorderedEquals([instObject]), + ); + + // B + expect( + _superInterfaces( + InstantiatedClass(classB, [classD.type]), + ), + unorderedEquals([instObject, instA]), + ); + + // C + expect( + _superInterfaces( + InstantiatedClass(classC, [classD.type]), + ), + unorderedEquals([ + instObject, + instA, + InstantiatedClass(classB, [classD.type]), + ]), + ); + } + + void test_computeSuperinterfaceSet_multipleInterfacePaths() { + var instObject = InstantiatedClass.of(_typeProvider.objectType); + + var classA = ElementFactory.classElement3(name: 'A'); + var instA = InstantiatedClass(classA, const []); + + var classB = ElementFactory.classElement3( + name: 'B', + interfaces: [instA.withNullabilitySuffixNone], + ); + var instB = InstantiatedClass(classB, const []); + + var classC = ElementFactory.classElement3( + name: 'C', + interfaces: [instA.withNullabilitySuffixNone], + ); + var instC = InstantiatedClass(classC, const []); + + var classD = ElementFactory.classElement3( + name: 'D', + interfaces: [instC.withNullabilitySuffixNone], + ); + var instD = InstantiatedClass(classD, const []); + + var classE = ElementFactory.classElement3( + 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_computeSuperinterfaceSet_multipleSuperclassPaths() { + var instObject = InstantiatedClass.of(_typeProvider.objectType); + + var classA = ElementFactory.classElement3(name: 'A'); + var instA = InstantiatedClass(classA, const []); + + var classB = ElementFactory.classElement3( + name: 'B', + supertype: instA.withNullabilitySuffixNone, + ); + var instB = InstantiatedClass(classB, const []); + + var classC = ElementFactory.classElement3( + name: 'C', + supertype: instA.withNullabilitySuffixNone, + ); + var instC = InstantiatedClass(classC, const []); + + var classD = ElementFactory.classElement3( + name: 'D', + supertype: instC.withNullabilitySuffixNone, + ); + var instD = InstantiatedClass(classD, const []); + + var classE = ElementFactory.classElement3( + 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_computeSuperinterfaceSet_recursion() { + var classA = ElementFactory.classElement3(name: 'A'); + var instA = InstantiatedClass(classA, const []); + + var classB = ElementFactory.classElement3( + 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_computeSuperinterfaceSet_singleInterfacePath() { + var instObject = InstantiatedClass.of(_typeProvider.objectType); + + var classA = ElementFactory.classElement3(name: 'A'); + var instA = InstantiatedClass(classA, const []); + + var classB = ElementFactory.classElement3( + name: 'B', + interfaces: [instA.withNullabilitySuffixNone], + ); + var instB = InstantiatedClass(classB, const []); + + var classC = ElementFactory.classElement3( + 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_computeSuperinterfaceSet_singleSuperclassPath() { + // + // A + // | + // B + // | + // C + // + var instObject = InstantiatedClass.of(_typeProvider.objectType); + + var classA = ElementFactory.classElement3(name: 'A'); + var instA = InstantiatedClass(classA, const []); + + var classB = ElementFactory.classElement3( + name: 'B', + supertype: instA.withNullabilitySuffixNone, + ); + var instB = InstantiatedClass(classB, const []); + + var classC = ElementFactory.classElement3( + 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]), + ); + } + + int _longestPathToObject(ClassElement element) { + return InterfaceLeastUpperBoundHelper.computeLongestInheritancePathToObject( + element); + } + + Set _superInterfaces(InstantiatedClass type) { + return InterfaceLeastUpperBoundHelper.computeSuperinterfaceSet(type); + } +} diff --git a/pkg/analyzer/test/src/dart/element/test_all.dart b/pkg/analyzer/test/src/dart/element/test_all.dart index d51bf5259b4eb..5ed89da9f1b8b 100644 --- a/pkg/analyzer/test/src/dart/element/test_all.dart +++ b/pkg/analyzer/test/src/dart/element/test_all.dart @@ -7,6 +7,7 @@ import 'package:test_reflective_loader/test_reflective_loader.dart'; import 'element_test.dart' as element; import 'function_type_test.dart' as function_type; import 'inheritance_manager2_test.dart' as inheritance_manager2; +import 'least_upper_bound_helper_test.dart' as least_upper_bound_helper; import 'type_algebra_test.dart' as type_algebra; /// Utility for manually running all tests. @@ -15,6 +16,7 @@ main() { element.main(); function_type.main(); inheritance_manager2.main(); + least_upper_bound_helper.main(); type_algebra.main(); }, name: 'element'); }