| // 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/analysis/features.dart'; |
| import 'package:analyzer/dart/ast/ast.dart'; |
| import 'package:analyzer/dart/element/element.dart'; |
| import 'package:analyzer/dart/element/nullability_suffix.dart'; |
| import 'package:analyzer/dart/element/type.dart'; |
| import 'package:analyzer/dart/element/type_provider.dart'; |
| import 'package:analyzer/dart/element/type_system.dart'; |
| import 'package:analyzer/src/dart/analysis/experiments.dart'; |
| import 'package:analyzer/src/dart/element/element.dart'; |
| import 'package:analyzer/src/dart/element/type.dart'; |
| import 'package:analyzer/src/dart/element/type_provider.dart'; |
| import 'package:analyzer/src/dart/element/type_system.dart' show TypeSystemImpl; |
| import 'package:analyzer/src/dart/error/hint_codes.dart'; |
| import 'package:analyzer/src/generated/source.dart'; |
| import 'package:analyzer/src/generated/testing/test_type_provider.dart'; |
| import 'package:nnbd_migration/fix_reason_target.dart'; |
| import 'package:nnbd_migration/instrumentation.dart'; |
| import 'package:nnbd_migration/src/decorated_class_hierarchy.dart'; |
| import 'package:nnbd_migration/src/decorated_type.dart'; |
| import 'package:nnbd_migration/src/edge_builder.dart'; |
| import 'package:nnbd_migration/src/edge_origin.dart'; |
| import 'package:nnbd_migration/src/expression_checks.dart'; |
| import 'package:nnbd_migration/src/nullability_node.dart'; |
| import 'package:pub_semver/pub_semver.dart'; |
| import 'package:test/test.dart'; |
| import 'package:test_reflective_loader/test_reflective_loader.dart'; |
| |
| import 'migration_visitor_test_base.dart'; |
| |
| main() { |
| defineReflectiveSuite(() { |
| defineReflectiveTests(AssignmentCheckerTest); |
| defineReflectiveTests(EdgeBuilderTest); |
| }); |
| } |
| |
| @reflectiveTest |
| class AssignmentCheckerTest extends Object |
| with EdgeTester, DecoratedTypeTester { |
| static const EdgeOrigin origin = _TestEdgeOrigin(); |
| |
| LibraryElementImpl? _myLibrary; |
| |
| ClassElement? _myListOfListClass; |
| |
| late DecoratedType _myListOfListSupertype; |
| |
| @override |
| final TypeProvider typeProvider; |
| |
| @override |
| final NullabilityGraphForTesting graph; |
| |
| @override |
| final decoratedTypeParameterBounds = DecoratedTypeParameterBounds(); |
| |
| final AssignmentCheckerForTesting checker; |
| |
| factory AssignmentCheckerTest() { |
| var typeProvider = TestTypeProvider().asLegacy; |
| _setCoreLibrariesTypeSystem(typeProvider); |
| |
| var graph = NullabilityGraphForTesting(); |
| var decoratedClassHierarchy = _DecoratedClassHierarchyForTesting(); |
| var checker = AssignmentCheckerForTesting( |
| TypeSystemImpl( |
| implicitCasts: true, |
| isNonNullableByDefault: false, |
| strictInference: false, |
| typeProvider: typeProvider, |
| ), |
| typeProvider, |
| graph, |
| decoratedClassHierarchy); |
| var assignmentCheckerTest = |
| AssignmentCheckerTest._(typeProvider, graph, checker); |
| decoratedClassHierarchy.assignmentCheckerTest = assignmentCheckerTest; |
| return assignmentCheckerTest; |
| } |
| |
| AssignmentCheckerTest._(this.typeProvider, this.graph, this.checker); |
| |
| void assign(DecoratedType source, DecoratedType destination, |
| {bool hard = false}) { |
| checker.checkAssignment(origin, |
| source: source, destination: destination, hard: hard); |
| } |
| |
| DecoratedType myListOfList(DecoratedType elementType) { |
| _initMyLibrary(); |
| if (_myListOfListClass == null) { |
| var t = typeParameter('T', object()); |
| _myListOfListSupertype = list(list(typeParameterType(t))); |
| _myListOfListClass = ClassElementImpl('MyListOfList', 0) |
| ..enclosingElement = _myLibrary!.definingCompilationUnit |
| ..typeParameters = [t] |
| ..supertype = _myListOfListSupertype.type as InterfaceType?; |
| } |
| return DecoratedType( |
| InterfaceTypeImpl( |
| element: _myListOfListClass!, |
| typeArguments: [elementType.type!], |
| nullabilitySuffix: NullabilitySuffix.star, |
| ), |
| newNode(), |
| typeArguments: [elementType], |
| ); |
| } |
| |
| void test_bottom_to_generic() { |
| var t = list(object()); |
| assign(bottom, t); |
| assertEdge(never, t.node, hard: false); |
| assertNoEdge(anyNode, t.typeArguments[0]!.node); |
| } |
| |
| void test_bottom_to_simple() { |
| var t = object(); |
| assign(bottom, t); |
| assertEdge(never, t.node, hard: false); |
| } |
| |
| void test_complex_to_typeParam() { |
| var bound = list(object()); |
| var t1 = list(object()); |
| var t2 = typeParameterType(typeParameter('T', bound)); |
| assign(t1, t2, hard: true); |
| assertEdge(t1.node, t2.node, hard: true); |
| assertEdge(t1.node, bound.node, hard: false); |
| // TODO(40622): Should this be a checkable edge? |
| assertEdge(t1.typeArguments[0]!.node, bound.typeArguments[0]!.node, |
| hard: false, checkable: false); |
| } |
| |
| void test_dynamic_to_dynamic() { |
| assign(dynamic_, dynamic_); |
| // Note: no assertions to do; just need to make sure there wasn't a crash. |
| } |
| |
| void test_dynamic_to_void() { |
| assign(dynamic_, void_); |
| // Note: no assertions to do; just need to make sure there wasn't a crash. |
| } |
| |
| void test_function_type_named_parameter() { |
| var t1 = function(dynamic_, named: {'x': object()}); |
| var t2 = function(dynamic_, named: {'x': object()}); |
| assign(t1, t2, hard: true); |
| // Note: t1 and t2 are swapped due to contravariance. |
| assertEdge(t2.namedParameters!['x']!.node, t1.namedParameters!['x']!.node, |
| hard: false, checkable: false); |
| } |
| |
| void test_function_type_named_to_no_parameter() { |
| var t1 = function(dynamic_, named: {'x': object()}); |
| var t2 = function(dynamic_); |
| assign(t1, t2); |
| // Note: no assertions to do; just need to make sure there wasn't a crash. |
| } |
| |
| void test_function_type_positional_parameter() { |
| var t1 = function(dynamic_, positional: [object()]); |
| var t2 = function(dynamic_, positional: [object()]); |
| assign(t1, t2, hard: true); |
| // Note: t1 and t2 are swapped due to contravariance. |
| assertEdge( |
| t2.positionalParameters![0]!.node, t1.positionalParameters![0]!.node, |
| hard: false, checkable: false); |
| } |
| |
| void test_function_type_positional_to_no_parameter() { |
| var t1 = function(dynamic_, positional: [object()]); |
| var t2 = function(dynamic_); |
| assign(t1, t2); |
| // Note: no assertions to do; just need to make sure there wasn't a crash. |
| } |
| |
| void test_function_type_positional_to_required_parameter() { |
| var t1 = function(dynamic_, positional: [object()]); |
| var t2 = function(dynamic_, required: [object()]); |
| assign(t1, t2, hard: true); |
| // Note: t1 and t2 are swapped due to contravariance. |
| assertEdge( |
| t2.positionalParameters![0]!.node, t1.positionalParameters![0]!.node, |
| hard: false, checkable: false); |
| } |
| |
| void test_function_type_required_parameter() { |
| var t1 = function(dynamic_, required: [object()]); |
| var t2 = function(dynamic_, required: [object()]); |
| assign(t1, t2); |
| // Note: t1 and t2 are swapped due to contravariance. |
| assertEdge( |
| t2.positionalParameters![0]!.node, t1.positionalParameters![0]!.node, |
| hard: false, checkable: false); |
| } |
| |
| void test_function_type_return_type() { |
| var t1 = function(object()); |
| var t2 = function(object()); |
| assign(t1, t2, hard: true); |
| assertEdge(t1.returnType!.node, t2.returnType!.node, |
| hard: false, checkable: false); |
| } |
| |
| void test_function_void_to_function_object() { |
| // This is not an ideal pattern, but void is assignable to Object in certain |
| // cases such as those with compound types here. We must support it. |
| var t1 = function(void_); |
| var t2 = function(object()); |
| assign(t1, t2, hard: true); |
| assertEdge(t1.returnType!.node, t2.returnType!.node, |
| hard: false, checkable: false); |
| } |
| |
| void test_future_int_to_future_or_int() { |
| var t1 = future(int_()); |
| var t2 = futureOr(int_()); |
| assign(t1, t2, hard: true); |
| assertEdge(t1.node, t2.node, hard: true); |
| assertEdge(t1.typeArguments[0]!.node, t2.typeArguments[0]!.node, |
| hard: true, checkable: false); |
| } |
| |
| void test_future_or_int_to_future_int() { |
| var t1 = futureOr(int_()); |
| var t2 = future(int_()); |
| assign(t1, t2, hard: true); |
| // FutureOr<int>? is nullable, so Future<int>? should be. |
| assertEdge(t1.node, t2.node, hard: true); |
| // FutureOr<int?> is nullable, so Future<int>? should be. |
| assertEdge(t1.typeArguments[0]!.node, t2.node, hard: true); |
| // FutureOr<int?> may hold a Future<int?>, so carry that forward. |
| assertEdge(t1.typeArguments[0]!.node, t2.typeArguments[0]!.node, |
| hard: false); |
| // FutureOr<int>? does not accept a Future<int?>, so don't draw this. |
| assertNoEdge(t1.node, t2.typeArguments[0]!.node); |
| } |
| |
| void test_future_or_int_to_int() { |
| var t1 = futureOr(int_()); |
| var t2 = int_(); |
| assign(t1, t2, hard: true); |
| assertEdge(t1.node, t2.node, hard: true); |
| assertEdge(t1.typeArguments[0]!.node, t2.node, hard: false); |
| } |
| |
| void test_future_or_list_object_to_list_int() { |
| var t1 = futureOr(list(object())); |
| var t2 = list(int_()); |
| assign(t1, t2, hard: true); |
| assertEdge(t1.node, t2.node, hard: true); |
| assertEdge(t1.typeArguments[0]!.node, t2.node, hard: false); |
| assertEdge( |
| t1.typeArguments[0]!.typeArguments[0]!.node, t2.typeArguments[0]!.node, |
| hard: false); |
| } |
| |
| void test_future_or_object_to_future_or_int() { |
| var t1 = futureOr(object()); |
| var t2 = futureOr(int_()); |
| assign(t1, t2, hard: true); |
| assertEdge(t1.node, t2.node, hard: true); |
| assertEdge(t1.typeArguments[0]!.node, t2.typeArguments[0]!.node, |
| hard: false); |
| } |
| |
| void test_future_or_to_future_or() { |
| var t1 = futureOr(int_()); |
| var t2 = futureOr(int_()); |
| assign(t1, t2, hard: true); |
| assertEdge(t1.node, t2.node, hard: true); |
| assertEdge(t1.typeArguments[0]!.node, t2.typeArguments[0]!.node, |
| hard: false); |
| } |
| |
| void test_generic_to_dynamic() { |
| var t = list(object()); |
| assign(t, dynamic_); |
| assertEdge(t.node, always, hard: false); |
| assertNoEdge(t.typeArguments[0]!.node, anyNode); |
| } |
| |
| void test_generic_to_generic_downcast() { |
| var t1 = list(list(object())); |
| var t2 = myListOfList(object()); |
| assign(t1, t2, hard: true); |
| assertEdge(t1.node, t2.node, hard: true); |
| // Let A, B, and C be nullability nodes such that: |
| // - t2 is MyListOfList<Object?A> |
| var a = t2.typeArguments[0]!.node; |
| // - t1 is List<List<Object?B>> |
| var b = t1.typeArguments[0]!.typeArguments[0]!.node; |
| // - the supertype of MyListOfList<T> is List<List<T?C>> |
| var c = _myListOfListSupertype.typeArguments[0]!.typeArguments[0]!.node; |
| // Then there should be an edge from b to substitute(a, c) |
| assertEdge(b, substitutionNode(a, c), hard: false); |
| } |
| |
| void test_generic_to_generic_downcast_of_type_parameter() { |
| var t = typeParameterType(typeParameter('T', object())); |
| var t1 = iterable(t); |
| var t2 = list(t); |
| assign(t1, t2, hard: true); |
| assertEdge(t1.node, t2.node, hard: true); |
| var a = t1.typeArguments[0]!.node; |
| var b = t2.typeArguments[0]!.node; |
| assertEdge(a, b, hard: false); |
| } |
| |
| void test_generic_to_generic_downcast_same_element() { |
| var t1 = list(object()); |
| var t2 = list(int_()); |
| assign(t1, t2, hard: true); |
| assertEdge(t1.node, t2.node, hard: true); |
| assertEdge(t1.typeArguments[0]!.node, t2.typeArguments[0]!.node, |
| hard: false); |
| } |
| |
| void test_generic_to_generic_same_element() { |
| var t1 = list(object()); |
| var t2 = list(object()); |
| assign(t1, t2, hard: true); |
| assertEdge(t1.node, t2.node, hard: true); |
| assertEdge(t1.typeArguments[0]!.node, t2.typeArguments[0]!.node, |
| hard: true, checkable: false); |
| } |
| |
| void test_generic_to_generic_upcast() { |
| var t1 = myListOfList(object()); |
| var t2 = list(list(object())); |
| assign(t1, t2); |
| assertEdge(t1.node, t2.node, hard: false); |
| // Let A, B, and C be nullability nodes such that: |
| // - t1 is MyListOfList<Object?A> |
| var a = t1.typeArguments[0]!.node; |
| // - t2 is List<List<Object?B>> |
| var b = t2.typeArguments[0]!.typeArguments[0]!.node; |
| // - the supertype of MyListOfList<T> is List<List<T?C>> |
| var c = _myListOfListSupertype.typeArguments[0]!.typeArguments[0]!.node; |
| // Then there should be an edge from substitute(a, c) to b. |
| assertEdge(substitutionNode(a, c), b, hard: false, checkable: false); |
| } |
| |
| void test_generic_to_object() { |
| var t1 = list(object()); |
| var t2 = object(); |
| assign(t1, t2); |
| assertEdge(t1.node, t2.node, hard: false); |
| assertNoEdge(t1.typeArguments[0]!.node, anyNode); |
| } |
| |
| void test_generic_to_void() { |
| var t = list(object()); |
| assign(t, void_); |
| assertEdge(t.node, always, hard: false); |
| assertNoEdge(t.typeArguments[0]!.node, anyNode); |
| } |
| |
| void test_int_to_future_or_int() { |
| var t1 = int_(); |
| var t2 = futureOr(int_()); |
| assign(t1, t2, hard: true); |
| // Note: given code like: |
| // int x = null; |
| // FutureOr<int> y = x; |
| // There are two possible migrations for `FutureOr<int>`: we could change it |
| // to either `FutureOr<int?>` or `FutureOr<int>?`. We choose to do |
| // `FutureOr<int>?` because it is a narrower type, so it is less likely to |
| // cause a proliferation of nullable types in the user's program. |
| assertEdge(t1.node, t2.node, hard: true); |
| assertNoEdge(t1.node, t2.typeArguments[0]!.node); |
| } |
| |
| void test_iterable_object_to_list_void() { |
| assign(iterable(object()), list(void_)); |
| // Note: no assertions to do; just need to make sure there wasn't a crash. |
| } |
| |
| void test_null_to_generic() { |
| var t = list(object()); |
| assign(null_, t); |
| assertEdge(always, t.node, hard: false); |
| assertNoEdge(anyNode, t.typeArguments[0]!.node); |
| } |
| |
| void test_null_to_simple() { |
| var t = object(); |
| assign(null_, t); |
| assertEdge(always, t.node, hard: false); |
| } |
| |
| void test_object_to_void() { |
| assign(object(), void_); |
| // Note: no assertions to do; just need to make sure there wasn't a crash. |
| } |
| |
| void test_simple_to_dynamic() { |
| var t = object(); |
| assign(t, dynamic_); |
| assertEdge(t.node, always, hard: false); |
| } |
| |
| void test_simple_to_simple() { |
| var t1 = object(); |
| var t2 = object(); |
| assign(t1, t2); |
| assertEdge(t1.node, t2.node, hard: false); |
| } |
| |
| void test_simple_to_simple_hard() { |
| var t1 = object(); |
| var t2 = object(); |
| assign(t1, t2, hard: true); |
| assertEdge(t1.node, t2.node, hard: true); |
| } |
| |
| void test_simple_to_void() { |
| var t = object(); |
| assign(t, void_); |
| assertEdge(t.node, always, hard: false); |
| } |
| |
| void test_typeParam_to_complex() { |
| var bound = list(object()); |
| var t1 = typeParameterType(typeParameter('T', bound)); |
| var t2 = list(object()); |
| assign(t1, t2, hard: true); |
| assertEdge(t1.node, t2.node, hard: true); |
| assertEdge(bound.node, t2.node, hard: false); |
| // TODO(40622): Should this be a checkable edge? |
| assertEdge(bound.typeArguments[0]!.node, t2.typeArguments[0]!.node, |
| hard: false, checkable: false); |
| } |
| |
| void test_typeParam_to_object() { |
| var t1 = typeParameterType(typeParameter('T', object())); |
| var t2 = object(); |
| assign(t1, t2); |
| assertEdge(t1.node, t2.node, hard: false); |
| } |
| |
| void test_typeParam_to_typeParam() { |
| var t = typeParameter('T', object()); |
| var t1 = typeParameterType(t); |
| var t2 = typeParameterType(t); |
| assign(t1, t2); |
| assertEdge(t1.node, t2.node, hard: false); |
| } |
| |
| @override |
| TypeParameterElement typeParameter(String name, DecoratedType bound) { |
| var t = super.typeParameter(name, bound); |
| checker.bounds[t] = bound; |
| return t; |
| } |
| |
| void _initMyLibrary() { |
| if (_myLibrary != null) { |
| return; |
| } |
| |
| var coreLibrary = typeProvider.boolElement.library as LibraryElementImpl; |
| var analysisContext = coreLibrary.context; |
| var analysisSession = coreLibrary.session; |
| var typeSystem = coreLibrary.typeSystem; |
| |
| var uriStr = 'package:test/test.dart'; |
| |
| _myLibrary = LibraryElementImpl( |
| analysisContext, |
| analysisSession, |
| uriStr, |
| -1, |
| 0, |
| FeatureSet.fromEnableFlags2( |
| sdkLanguageVersion: Version.parse('2.10.0'), |
| flags: [EnableString.non_nullable], |
| ), |
| ); |
| _myLibrary!.typeSystem = typeSystem; |
| _myLibrary!.typeProvider = coreLibrary.typeProvider; |
| |
| var uri = Uri.parse(uriStr); |
| var source = _MockSource(uri); |
| |
| var definingUnit = CompilationUnitElementImpl(); |
| definingUnit.source = source; |
| definingUnit.librarySource = source; |
| |
| definingUnit.enclosingElement = _myLibrary; |
| _myLibrary!.definingCompilationUnit = definingUnit; |
| } |
| |
| static void _setCoreLibrariesTypeSystem(TypeProviderImpl typeProvider) { |
| var typeSystem = TypeSystemImpl( |
| isNonNullableByDefault: false, |
| implicitCasts: true, |
| strictInference: false, |
| typeProvider: typeProvider, |
| ); |
| _setLibraryTypeSystem( |
| typeProvider.objectElement.library, |
| typeProvider, |
| typeSystem, |
| ); |
| _setLibraryTypeSystem( |
| typeProvider.futureElement.library, |
| typeProvider, |
| typeSystem, |
| ); |
| } |
| |
| static void _setLibraryTypeSystem( |
| LibraryElement libraryElement, |
| TypeProvider typeProvider, |
| TypeSystem typeSystem, |
| ) { |
| var libraryElementImpl = libraryElement as LibraryElementImpl; |
| libraryElementImpl.typeProvider = typeProvider as TypeProviderImpl; |
| libraryElementImpl.typeSystem = typeSystem as TypeSystemImpl; |
| } |
| } |
| |
| @reflectiveTest |
| class EdgeBuilderTest extends EdgeBuilderTestBase { |
| void assertGLB( |
| NullabilityNode? node, NullabilityNode? left, NullabilityNode? right) { |
| expect(node, isNot(TypeMatcher<NullabilityNodeForLUB>())); |
| assertEdge(left, node, hard: false, guards: [right]); |
| assertEdge(node, left, hard: false); |
| assertEdge(node, right, hard: false); |
| } |
| |
| void assertLUB(NullabilityNode node, Object? left, Object? right) { |
| var conditionalNode = node as NullabilityNodeForLUB; |
| var leftMatcher = NodeMatcher(left); |
| var rightMatcher = NodeMatcher(right); |
| expect(leftMatcher.matches(conditionalNode.left), true); |
| expect(rightMatcher.matches(conditionalNode.right), true); |
| } |
| |
| /// Checks that there are no nullability nodes upstream from [node] that could |
| /// cause it to become nullable. |
| void assertNoUpstreamNullability(NullabilityNode? node) { |
| // Store `neverClosure` in a local variable so that we avoid the |
| // computational expense of recomputing it each time through the loop below. |
| var neverClosure = this.neverClosure; |
| |
| // Any node with a hard edge to never (or never itself) won't become |
| // nullable, even if it has nodes upstream from it. |
| if (neverClosure.contains(node)) return; |
| |
| // Otherwise, make sure that every node directly upstream from this node |
| // has a hard edge to never. |
| for (var edge in getEdges(anyNode, node)) { |
| expect(neverClosure, contains(edge.sourceNode)); |
| } |
| } |
| |
| /// Verifies that a null check will occur when the given edge is unsatisfied. |
| /// |
| /// [expressionChecks] is the object tracking whether or not a null check is |
| /// needed. |
| void assertNullCheck( |
| ExpressionChecksOrigin expressionChecks, NullabilityEdge expectedEdge) { |
| expect(expressionChecks.checks.edges.values, contains(expectedEdge)); |
| } |
| |
| /// Gets the [ExpressionChecks] associated with the expression whose text |
| /// representation is [text], or `null` if the expression has no |
| /// [ExpressionChecks] associated with it. |
| ExpressionChecksOrigin? checkExpression(String text) { |
| return variables!.checkExpression(findNode.expression(text)); |
| } |
| |
| /// Gets the [DecoratedType] associated with the expression whose text |
| /// representation is [text], or `null` if the expression has no |
| /// [DecoratedType] associated with it. |
| DecoratedType? decoratedExpressionType(String text) { |
| return variables!.decoratedExpressionType(findNode.expression(text)); |
| } |
| |
| bool hasNullCheckHint(Expression expression) => |
| variables!.getNullCheckHint(testSource, expression) != null; |
| |
| Future<void> test_already_migrated_field() async { |
| await analyze(''' |
| double f() => double.nan; |
| '''); |
| var nanElement = typeProvider.doubleType.element.getField('nan')!; |
| assertEdge(variables!.decoratedElementType(nanElement).node, |
| decoratedTypeAnnotation('double f').node, |
| hard: false); |
| } |
| |
| Future<void> test_ArgumentError_checkNotNull_not_postDominating() async { |
| await analyze(''' |
| void f(bool b, int i, int j) { |
| ArgumentError.checkNotNull(j); |
| if (b) return; |
| ArgumentError.checkNotNull(i); |
| } |
| '''); |
| |
| // Asserts after ifs don't demonstrate non-null intent. |
| assertNoEdge(decoratedTypeAnnotation('int i').node, never); |
| // But asserts before ifs do |
| assertEdge(decoratedTypeAnnotation('int j').node, never, hard: true); |
| } |
| |
| Future<void> test_ArgumentError_checkNotNull_postDominating() async { |
| await analyze(''' |
| void f(int i) { |
| ArgumentError.checkNotNull(i); |
| } |
| '''); |
| |
| assertEdge(decoratedTypeAnnotation('int i').node, never, hard: true); |
| } |
| |
| Future<void> test_ArgumentError_checkNotNull_prefixed() async { |
| await analyze(''' |
| import 'dart:core' as core; |
| void f(core.int i) { |
| core.ArgumentError.checkNotNull(i); |
| } |
| '''); |
| |
| assertEdge(decoratedTypeAnnotation('int i').node, never, hard: true); |
| } |
| |
| Future<void> test_as_dynamic() async { |
| await analyze(''' |
| void f(Object o) { |
| (o as dynamic).gcd(1); |
| } |
| '''); |
| assertEdge(decoratedTypeAnnotation('Object o').node, |
| decoratedTypeAnnotation('dynamic').node, |
| hard: true); |
| assertEdge(decoratedTypeAnnotation('dynamic').node, never, hard: true); |
| } |
| |
| Future<void> test_as_int() async { |
| await analyze(''' |
| void f(Object o) { |
| (o as int).gcd(1); |
| } |
| '''); |
| assertEdge(decoratedTypeAnnotation('Object o').node, |
| decoratedTypeAnnotation('int').node, |
| hard: true); |
| assertEdge(decoratedTypeAnnotation('int').node, never, hard: true); |
| expect( |
| variables!.wasUnnecessaryCast(testSource, findNode.as_('o as')), false); |
| } |
| |
| Future<void> test_as_int_null_ok() async { |
| await analyze(''' |
| void f(Object o) { |
| (o as int)?.gcd(1); |
| } |
| '''); |
| assertEdge(decoratedTypeAnnotation('Object o').node, |
| decoratedTypeAnnotation('int').node, |
| hard: true); |
| assertNoEdge(decoratedTypeAnnotation('int').node, never); |
| } |
| |
| Future<void> test_as_int_unnecessary() async { |
| verifyNoTestUnitErrors = false; |
| await analyze(''' |
| void f(int i) { |
| (i as int).gcd(1); |
| } |
| '''); |
| expect( |
| testAnalysisResult.errors.single.errorCode, HintCode.UNNECESSARY_CAST); |
| assertEdge(decoratedTypeAnnotation('int i').node, |
| decoratedTypeAnnotation('int)').node, |
| hard: true); |
| assertEdge(decoratedTypeAnnotation('int)').node, never, hard: true); |
| expect( |
| variables!.wasUnnecessaryCast(testSource, findNode.as_('i as')), true); |
| } |
| |
| Future<void> test_as_side_cast() async { |
| await analyze(''' |
| class A {} |
| class B {} |
| class C implements A, B {} |
| B f(A a) { |
| // possible via f(C()); |
| return a as B; |
| } |
| '''); |
| assertEdge( |
| decoratedTypeAnnotation('A a').node, decoratedTypeAnnotation('B;').node, |
| hard: true); |
| } |
| |
| Future<void> test_as_side_cast_generics() async { |
| await analyze(''' |
| class A<T> {} |
| class B<T> {} |
| class C implements A<int>, B<bool> {} |
| B<bool> f(A<int> a) { |
| // possible via f(C()); |
| return a as B<bool>; |
| } |
| '''); |
| assertEdge(decoratedTypeAnnotation('A<int> a').node, |
| decoratedTypeAnnotation('B<bool>;').node, |
| hard: true); |
| assertEdge(decoratedTypeAnnotation('bool>;').node, |
| decoratedTypeAnnotation('bool> f').node, |
| hard: true, checkable: false); |
| assertNoEdge(anyNode, decoratedTypeAnnotation('bool>;').node); |
| assertNoEdge(anyNode, decoratedTypeAnnotation('int> a').node); |
| // int> a should be connected to the bound of T in A<T>, but nothing else. |
| expect( |
| decoratedTypeAnnotation('int> a').node!.downstreamEdges, hasLength(1)); |
| } |
| |
| Future<void> test_assert_demonstrates_non_null_intent() async { |
| await analyze(''' |
| void f(int i) { |
| assert(i != null); |
| } |
| '''); |
| |
| assertEdge(decoratedTypeAnnotation('int i').node, never, hard: true); |
| } |
| |
| Future<void> test_assert_initializer_demonstrates_non_null_intent() async { |
| await analyze(''' |
| class C { |
| C(int i) |
| : assert(i != null); |
| } |
| '''); |
| |
| assertEdge(decoratedTypeAnnotation('int i').node, never, hard: true); |
| } |
| |
| Future<void> test_assert_is_demonstrates_non_null_intent() async { |
| // Note, this could also be handled via improved flow analysis rather than a |
| // hard edge. |
| await analyze(''' |
| void f(dynamic i) { |
| assert(i is int); |
| } |
| '''); |
| |
| assertEdge(decoratedTypeAnnotation('dynamic i').node, never, hard: true); |
| } |
| |
| Future<void> test_assign_bound_to_type_parameter() async { |
| await analyze(''' |
| class C<T extends List<int>> { |
| T f(List<int> x) => x; |
| } |
| '''); |
| var boundType = decoratedTypeAnnotation('List<int>>'); |
| var parameterType = decoratedTypeAnnotation('List<int> x'); |
| var tType = decoratedTypeAnnotation('T f'); |
| assertEdge(parameterType.node, tType.node, hard: true); |
| assertEdge(parameterType.node, boundType.node, hard: false); |
| // TODO(mfairhurst): Confirm we want this edge. |
| // TODO(40622): Should this be a checkable edge? |
| assertEdge( |
| parameterType.typeArguments[0]!.node, boundType.typeArguments[0]!.node, |
| hard: false, checkable: false); |
| } |
| |
| Future<void> test_assign_dynamic_to_other_type() async { |
| await analyze(''' |
| int f(dynamic d) => d; |
| '''); |
| // There is no explicit null check necessary, since `dynamic` is |
| // downcastable to any type, nullable or not. |
| expect(checkExpression('d;'), isNull); |
| // But we still create an edge, to make sure that the possibility of `null` |
| // propagates to callees. |
| assertEdge(decoratedTypeAnnotation('dynamic').node, |
| decoratedTypeAnnotation('int').node, |
| hard: true); |
| } |
| |
| Future<void> test_assign_function_type_to_function_interface_type() async { |
| await analyze(''' |
| Function f(void Function() x) => x; |
| '''); |
| assertEdge(decoratedGenericFunctionTypeAnnotation('void Function()').node, |
| decoratedTypeAnnotation('Function f').node, |
| hard: true); |
| } |
| |
| Future<void> test_assign_future_to_futureOr_complex() async { |
| await analyze(''' |
| import 'dart:async'; |
| FutureOr<List<int>> f(Future<List<int>> x) => x; |
| '''); |
| // If `x` is `Future<List<int?>>`, then the only way to migrate is to make |
| // the return type `FutureOr<List<int?>>`. |
| assertEdge(decoratedTypeAnnotation('int>> x').node, |
| decoratedTypeAnnotation('int>> f').node, |
| hard: true, checkable: false); |
| assertNoEdge(decoratedTypeAnnotation('int>> x').node, |
| decoratedTypeAnnotation('List<int>> f').node); |
| assertNoEdge(decoratedTypeAnnotation('int>> x').node, |
| decoratedTypeAnnotation('FutureOr<List<int>> f').node); |
| } |
| |
| Future<void> test_assign_future_to_futureOr_simple() async { |
| await analyze(''' |
| import 'dart:async'; |
| FutureOr<int> f(Future<int> x) => x; |
| '''); |
| // If `x` is nullable, then there are two migrations possible: we could make |
| // the return type `FutureOr<int?>` or we could make it `FutureOr<int>?`. |
| // We choose `FutureOr<int>?` because it's strictly more conservative (it's |
| // a subtype of `FutureOr<int?>`). |
| assertEdge(decoratedTypeAnnotation('Future<int> x').node, |
| decoratedTypeAnnotation('FutureOr<int>').node, |
| hard: true); |
| assertNoEdge(decoratedTypeAnnotation('Future<int> x').node, |
| decoratedTypeAnnotation('int> f').node); |
| // If `x` is `Future<int?>`, then the only way to migrate is to make the |
| // return type `FutureOr<int?>`. |
| |
| assertEdge( |
| substitutionNode( |
| decoratedTypeAnnotation('int> x').node, inSet(pointsToNever)), |
| decoratedTypeAnnotation('int> f').node, |
| hard: true, |
| checkable: false); |
| assertNoEdge(decoratedTypeAnnotation('int> x').node, |
| decoratedTypeAnnotation('FutureOr<int>').node); |
| } |
| |
| Future<void> test_assign_non_future_to_futureOr_complex() async { |
| await analyze(''' |
| import 'dart:async'; |
| FutureOr<List<int>> f(List<int> x) => x; |
| '''); |
| // If `x` is `List<int?>`, then the only way to migrate is to make the |
| // return type `FutureOr<List<int?>>`. |
| assertEdge(decoratedTypeAnnotation('int> x').node, |
| decoratedTypeAnnotation('int>> f').node, |
| hard: false, checkable: false); |
| assertNoEdge(decoratedTypeAnnotation('int> x').node, |
| decoratedTypeAnnotation('List<int>> f').node); |
| assertNoEdge(decoratedTypeAnnotation('int> x').node, |
| decoratedTypeAnnotation('FutureOr<List<int>> f').node); |
| } |
| |
| Future<void> test_assign_non_future_to_futureOr_simple() async { |
| await analyze(''' |
| import 'dart:async'; |
| FutureOr<int> f(int x) => x; |
| '''); |
| // If `x` is nullable, then there are two migrations possible: we could make |
| // the return type `FutureOr<int?>` or we could make it `FutureOr<int>?`. |
| // We choose `FutureOr<int>?` because it's strictly more conservative (it's |
| // a subtype of `FutureOr<int?>`). |
| assertEdge(decoratedTypeAnnotation('int x').node, |
| decoratedTypeAnnotation('FutureOr<int>').node, |
| hard: true); |
| assertNoEdge(decoratedTypeAnnotation('int x').node, |
| decoratedTypeAnnotation('int>').node); |
| } |
| |
| Future<void> test_assign_null_to_generic_type() async { |
| await analyze(''' |
| main() { |
| List<int> x = null; |
| } |
| '''); |
| // TODO(paulberry): edge should be hard. |
| assertEdge(inSet(alwaysPlus), decoratedTypeAnnotation('List').node, |
| hard: false); |
| } |
| |
| Future<void> test_assign_to_bound_as() async { |
| // TODO(mfairhurst): support downcast to type params with bounds |
| await analyze(''' |
| class C<T> {} |
| void f(Object o) { |
| o as C<int>; |
| } |
| '''); |
| // For now, edge to `anyNode`, because the true bound is inferred. |
| assertEdge(decoratedTypeAnnotation('int').node, anyNode, hard: true); |
| } |
| |
| Future<void> test_assign_to_bound_class_alias() async { |
| await analyze(''' |
| class C<T extends Object/*1*/> {} |
| class D<T extends Object/*2*/> {} |
| mixin M<T extends Object/*3*/> {} |
| class F = C<int> with M<String> implements D<num>; |
| '''); |
| assertEdge(decoratedTypeAnnotation('int').node, |
| decoratedTypeAnnotation('Object/*1*/').node, |
| hard: true); |
| assertEdge(decoratedTypeAnnotation('num').node, |
| decoratedTypeAnnotation('Object/*2*/').node, |
| hard: true); |
| assertEdge(decoratedTypeAnnotation('String').node, |
| decoratedTypeAnnotation('Object/*3*/').node, |
| hard: true); |
| } |
| |
| Future<void> test_assign_to_bound_class_extends() async { |
| await analyze(''' |
| class A<T extends Object> {} |
| class C extends A<int> {} |
| '''); |
| assertEdge(decoratedTypeAnnotation('int').node, |
| decoratedTypeAnnotation('Object').node, |
| hard: true); |
| } |
| |
| Future<void> test_assign_to_bound_class_implements() async { |
| await analyze(''' |
| class A<T extends Object> {} |
| class C implements A<int> {} |
| '''); |
| assertEdge(decoratedTypeAnnotation('int').node, |
| decoratedTypeAnnotation('Object').node, |
| hard: true); |
| } |
| |
| Future<void> test_assign_to_bound_class_with() async { |
| await analyze(''' |
| class A<T extends Object> {} |
| class C extends Object with A<int> {} |
| '''); |
| assertEdge(decoratedTypeAnnotation('int').node, |
| decoratedTypeAnnotation('Object>').node, |
| hard: true); |
| } |
| |
| Future<void> test_assign_to_bound_extension_extended_type() async { |
| await analyze(''' |
| class C<T extends Object> {} |
| extension E on C<int> {} |
| '''); |
| assertEdge(decoratedTypeAnnotation('int').node, |
| decoratedTypeAnnotation('Object>').node, |
| hard: true); |
| } |
| |
| Future<void> test_assign_to_bound_field_formal_typed() async { |
| await analyze(''' |
| class C<T extends Object> {} |
| class D { |
| dynamic i; |
| D(C<int> this.i); |
| } |
| '''); |
| assertEdge(decoratedTypeAnnotation('int').node, |
| decoratedTypeAnnotation('Object').node, |
| hard: true); |
| } |
| |
| Future<void> test_assign_to_bound_field_formal_typed_function() async { |
| await analyze(''' |
| class C<T extends Object> {} |
| class D { |
| dynamic i; |
| D(this.i(C<int> name)); |
| } |
| '''); |
| assertEdge(decoratedTypeAnnotation('int').node, |
| decoratedTypeAnnotation('Object').node, |
| hard: true); |
| } |
| |
| Future<void> test_assign_to_bound_for() async { |
| await analyze(''' |
| class C<T extends Object> {} |
| void main() { |
| for (C<int> c = null ;;) {} |
| } |
| '''); |
| assertEdge(decoratedTypeAnnotation('int').node, |
| decoratedTypeAnnotation('Object>').node, |
| hard: true); |
| } |
| |
| Future<void> test_assign_to_bound_for_element() async { |
| await analyze(''' |
| class C<T extends Object> {} |
| void main() { |
| [for (C<int> c = null ;;) c]; |
| } |
| '''); |
| assertEdge(decoratedTypeAnnotation('int').node, |
| decoratedTypeAnnotation('Object>').node, |
| hard: true); |
| } |
| |
| Future<void> test_assign_to_bound_for_in() async { |
| await analyze(''' |
| class C<T extends Object> {} |
| void main() { |
| for (C<int> c in []) {} |
| } |
| '''); |
| assertEdge(decoratedTypeAnnotation('int').node, |
| decoratedTypeAnnotation('Object>').node, |
| hard: true); |
| } |
| |
| Future<void> test_assign_to_bound_for_in_element() async { |
| await analyze(''' |
| class C<T extends Object> {} |
| void main() { |
| [for (C<int> c in []) c]; |
| } |
| '''); |
| assertEdge(decoratedTypeAnnotation('int').node, |
| decoratedTypeAnnotation('Object>').node, |
| hard: true); |
| } |
| |
| Future<void> test_assign_to_bound_function_invocation_type_argument() async { |
| await analyze(''' |
| void f<T extends Object>() {} |
| void main() { |
| (f)<int>(); |
| } |
| '''); |
| assertEdge(decoratedTypeAnnotation('int').node, |
| decoratedTypeAnnotation('Object').node, |
| hard: true); |
| } |
| |
| Future<void> test_assign_to_bound_in_return_type() async { |
| await analyze(''' |
| class C<T extends Object> {} |
| C<int> f() => null; |
| '''); |
| assertEdge(decoratedTypeAnnotation('int').node, |
| decoratedTypeAnnotation('Object').node, |
| hard: true); |
| } |
| |
| Future<void> test_assign_to_bound_in_type_argument() async { |
| await analyze(''' |
| class C<T extends Object> {} |
| C<C<int>> f() => null; |
| '''); |
| assertEdge(decoratedTypeAnnotation('C<int>').node, |
| decoratedTypeAnnotation('Object').node, |
| hard: true); |
| assertEdge(decoratedTypeAnnotation('int').node, |
| decoratedTypeAnnotation('Object').node, |
| hard: true); |
| } |
| |
| Future<void> test_assign_to_bound_instance_creation() async { |
| await analyze(''' |
| class C<T extends Object> {} |
| void main() { |
| C<int>(); |
| } |
| '''); |
| assertEdge(decoratedTypeAnnotation('int').node, |
| decoratedTypeAnnotation('Object').node, |
| hard: true); |
| } |
| |
| Future<void> test_assign_to_bound_list_literal() async { |
| await analyze(''' |
| class C<T extends Object> {} |
| void main() { |
| <C<int>>[]; |
| } |
| '''); |
| assertEdge(decoratedTypeAnnotation('int').node, |
| decoratedTypeAnnotation('Object').node, |
| hard: true); |
| } |
| |
| Future<void> test_assign_to_bound_local_variable() async { |
| await analyze(''' |
| class C<T extends Object> {} |
| main() { |
| C<int> c = null; |
| } |
| '''); |
| assertEdge(decoratedTypeAnnotation('int').node, |
| decoratedTypeAnnotation('Object').node, |
| hard: true); |
| } |
| |
| Future<void> test_assign_to_bound_map_literal() async { |
| await analyze(''' |
| class C<T extends Object> {} |
| void main() { |
| <C<int>, C<String>>{}; |
| } |
| '''); |
| assertEdge(decoratedTypeAnnotation('int').node, |
| decoratedTypeAnnotation('Object').node, |
| hard: true); |
| assertEdge(decoratedTypeAnnotation('String').node, |
| decoratedTypeAnnotation('Object').node, |
| hard: true); |
| } |
| |
| Future<void> test_assign_to_bound_method_bound() async { |
| await analyze(''' |
| class C<T extends Object> {} |
| class D { |
| f<U extends C<int>>() {} |
| } |
| '''); |
| } |
| |
| Future<void> test_assign_to_bound_method_call_type_argument() async { |
| await analyze(''' |
| void f<T extends Object>() {} |
| void main() { |
| f<int>(); |
| } |
| '''); |
| assertEdge(decoratedTypeAnnotation('int').node, |
| decoratedTypeAnnotation('Object').node, |
| hard: true); |
| } |
| |
| Future<void> test_assign_to_bound_mixin_implements() async { |
| await analyze(''' |
| class A<T extends Object> {} |
| mixin C implements A<int> {} |
| '''); |
| assertEdge(decoratedTypeAnnotation('int').node, |
| decoratedTypeAnnotation('Object').node, |
| hard: true); |
| } |
| |
| Future<void> test_assign_to_bound_mixin_on() async { |
| await analyze(''' |
| class A<T extends Object> {} |
| mixin C on A<int> {} |
| '''); |
| assertEdge(decoratedTypeAnnotation('int').node, |
| decoratedTypeAnnotation('Object').node, |
| hard: true); |
| } |
| |
| Future<void> test_assign_to_bound_mixin_type_parameter_bound() async { |
| await analyze(''' |
| class C<T extends Object> {} |
| mixin M<T extends C<int>> {} |
| '''); |
| assertEdge(decoratedTypeAnnotation('int').node, |
| decoratedTypeAnnotation('Object').node, |
| hard: true); |
| } |
| |
| Future<void> test_assign_to_bound_redirecting_constructor_argument() async { |
| await analyze(''' |
| class A<T extends Object> {} |
| class C { |
| factory C() = D<A<int>>; |
| } |
| class D<U> implements C {} |
| '''); |
| assertEdge(decoratedTypeAnnotation('int').node, |
| decoratedTypeAnnotation('Object').node, |
| hard: true); |
| } |
| |
| Future<void> test_assign_to_bound_set_literal() async { |
| await analyze(''' |
| class C<T extends Object> {} |
| void main() { |
| <C<int>>{}; |
| } |
| '''); |
| assertEdge(decoratedTypeAnnotation('int').node, |
| decoratedTypeAnnotation('Object').node, |
| hard: true); |
| } |
| |
| Future<void> test_assign_to_bound_within_bound() async { |
| await analyze(''' |
| class A<T extends Object> {} |
| class B<T extends A<int>> {} |
| '''); |
| var aBound = decoratedTypeAnnotation('Object').node; |
| var aBoundInt = decoratedTypeAnnotation('int').node; |
| assertEdge(aBoundInt, aBound, hard: true); |
| } |
| |
| Future<void> test_assign_to_bound_within_bound_method() async { |
| await analyze(''' |
| class C<T extends Object> {} |
| void f<T extends C<int>>() {} |
| '''); |
| var cBound = decoratedTypeAnnotation('Object').node; |
| var fcInt = decoratedTypeAnnotation('int').node; |
| assertEdge(fcInt, cBound, hard: true); |
| } |
| |
| Future<void> test_assign_type_parameter_to_bound() async { |
| await analyze(''' |
| class C<T extends List<int>> { |
| List<int> f(T x) => x; |
| } |
| '''); |
| var boundType = decoratedTypeAnnotation('List<int>>'); |
| var returnType = decoratedTypeAnnotation('List<int> f'); |
| var tType = decoratedTypeAnnotation('T x'); |
| assertEdge(tType.node, returnType.node, hard: true); |
| assertEdge(boundType.node, returnType.node, hard: false); |
| // TODO(40622): Should this be a checkable edge? |
| assertEdge( |
| boundType.typeArguments[0]!.node, returnType.typeArguments[0]!.node, |
| hard: false, checkable: false); |
| } |
| |
| Future<void> test_assign_upcast_generic() async { |
| await analyze(''' |
| void f(Iterable<int> x) {} |
| void g(List<int> x) { |
| f(x); |
| } |
| '''); |
| |
| var iterableInt = decoratedTypeAnnotation('Iterable<int>'); |
| var listInt = decoratedTypeAnnotation('List<int>'); |
| assertEdge(listInt.node, iterableInt.node, hard: true); |
| assertEdge( |
| substitutionNode(listInt.typeArguments[0]!.node, inSet(pointsToNever)), |
| iterableInt.typeArguments[0]!.node, |
| hard: true, |
| checkable: false); |
| } |
| |
| Future<void> test_assignment_code_reference() async { |
| await analyze(''' |
| void f(int i) { |
| int j = i; |
| } |
| '''); |
| var edge = assertEdge(decoratedTypeAnnotation('int i').node, |
| decoratedTypeAnnotation('int j').node, |
| hard: true); |
| var codeReference = edge.codeReference!; |
| expect(codeReference, isNotNull); |
| expect(codeReference.path, contains('test.dart')); |
| expect(codeReference.line, 2); |
| expect(codeReference.column, 11); |
| } |
| |
| Future<void> test_assignmentExpression_compound_dynamic() async { |
| await analyze(''' |
| void f(dynamic x, int y) { |
| x += y; |
| } |
| '''); |
| // No assertions; just making sure this doesn't crash. |
| } |
| |
| Future<void> test_assignmentExpression_compound_simple() async { |
| var code = ''' |
| abstract class C { |
| C operator+(C x); |
| } |
| C f(C y, C z) => (y += z); |
| '''; |
| await analyze(code); |
| var targetEdge = assertEdge( |
| decoratedTypeAnnotation('C y').node, inSet(pointsToNever), |
| hard: true); |
| expect( |
| (graph.getEdgeOrigin(targetEdge) as CompoundAssignmentOrigin) |
| .node! |
| .operator |
| .offset, |
| code.indexOf('+=')); |
| assertNullCheck( |
| checkExpression('z);')!, |
| assertEdge(decoratedTypeAnnotation('C z').node, |
| decoratedTypeAnnotation('C x').node, |
| hard: true)); |
| var operatorReturnEdge = assertEdge( |
| decoratedTypeAnnotation('C operator').node, |
| decoratedTypeAnnotation('C y').node, |
| hard: false); |
| expect( |
| (graph.getEdgeOrigin(operatorReturnEdge) as CompoundAssignmentOrigin) |
| .node! |
| .operator |
| .offset, |
| code.indexOf('+=')); |
| var fReturnEdge = assertEdge(decoratedTypeAnnotation('C operator').node, |
| decoratedTypeAnnotation('C f').node, |
| hard: false); |
| assertNullCheck(checkExpression('(y += z)')!, fReturnEdge); |
| } |
| |
| Future<void> test_assignmentExpression_compound_withSubstitution() async { |
| // Failing due to a side-cast from incorrectly instantiating the operator. |
| var code = ''' |
| abstract class C<T> { |
| C<T> operator+(C<T> x); |
| } |
| C<int> f(C<int> y, C<int> z) => (y += z); |
| '''; |
| await analyze(code); |
| var targetEdge = assertEdge( |
| decoratedTypeAnnotation('C<int> y').node, inSet(pointsToNever), |
| hard: true); |
| expect( |
| (graph.getEdgeOrigin(targetEdge) as CompoundAssignmentOrigin) |
| .node! |
| .operator |
| .offset, |
| code.indexOf('+=')); |
| assertNullCheck( |
| checkExpression('z);')!, |
| assertEdge(decoratedTypeAnnotation('C<int> z').node, |
| decoratedTypeAnnotation('C<T> x').node, |
| hard: true)); |
| var operatorReturnEdge = assertEdge( |
| decoratedTypeAnnotation('C<T> operator').node, |
| decoratedTypeAnnotation('C<int> y').node, |
| hard: false); |
| expect( |
| (graph.getEdgeOrigin(operatorReturnEdge) as CompoundAssignmentOrigin) |
| .node! |
| .operator |
| .offset, |
| code.indexOf('+=')); |
| var fReturnEdge = assertEdge(decoratedTypeAnnotation('C<T> operator').node, |
| decoratedTypeAnnotation('C<int> f').node, |
| hard: false); |
| assertNullCheck(checkExpression('(y += z)')!, fReturnEdge); |
| } |
| |
| Future<void> test_assignmentExpression_field() async { |
| await analyze(''' |
| class C { |
| int x = 0; |
| } |
| void f(C c, int i) { |
| c.x = i; |
| } |
| '''); |
| assertEdge(decoratedTypeAnnotation('int i').node, |
| decoratedTypeAnnotation('int x').node, |
| hard: true); |
| } |
| |
| Future<void> test_assignmentExpression_field_cascaded() async { |
| await analyze(''' |
| class C { |
| int x = 0; |
| } |
| void f(C c, int i) { |
| c..x = i; |
| } |
| '''); |
| assertEdge(decoratedTypeAnnotation('int i').node, |
| decoratedTypeAnnotation('int x').node, |
| hard: true); |
| } |
| |
| Future<void> test_assignmentExpression_field_target_check() async { |
| await analyze(''' |
| class C { |
| int x = 0; |
| } |
| void f(C c, int i) { |
| c.x = i; |
| } |
| '''); |
| assertNullCheck(checkExpression('c.x')!, |
| assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true)); |
| } |
| |
| Future<void> test_assignmentExpression_field_target_check_cascaded() async { |
| await analyze(''' |
| class C { |
| int x = 0; |
| } |
| void f(C c, int i) { |
| c..x = i; |
| } |
| '''); |
| assertNullCheck(checkExpression('c..x')!, |
| assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true)); |
| } |
| |
| Future<void> test_assignmentExpression_indexExpression_index() async { |
| await analyze(''' |
| class C { |
| void operator[]=(int a, int b) {} |
| } |
| void f(C c, int i, int j) { |
| c[i] = j; |
| } |
| '''); |
| assertEdge(decoratedTypeAnnotation('int i').node, |
| decoratedTypeAnnotation('int a').node, |
| hard: true); |
| } |
| |
| Future<void> test_assignmentExpression_indexExpression_return_value() async { |
| await analyze(''' |
| class C { |
| void operator[]=(int a, int b) {} |
| } |
| int f(C c, int i, int j) => c[i] = j; |
| '''); |
| assertEdge(decoratedTypeAnnotation('int j').node, |
| decoratedTypeAnnotation('int f').node, |
| hard: false); |
| } |
| |
| Future<void> test_assignmentExpression_indexExpression_target_check() async { |
| await analyze(''' |
| class C { |
| void operator[]=(int a, int b) {} |
| } |
| void f(C c, int i, int j) { |
| c[i] = j; |
| } |
| '''); |
| assertNullCheck(checkExpression('c[')!, |
| assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true)); |
| } |
| |
| Future<void> test_assignmentExpression_indexExpression_value() async { |
| await analyze(''' |
| class C { |
| void operator[]=(int a, int b) {} |
| } |
| void f(C c, int i, int j) { |
| c[i] = j; |
| } |
| '''); |
| assertEdge(decoratedTypeAnnotation('int j').node, |
| decoratedTypeAnnotation('int b').node, |
| hard: true); |
| } |
| |
| Future<void> |
| test_assignmentExpression_nullAware_complex_contravariant() async { |
| await analyze(''' |
| void Function(int) f(void Function(int) x, void Function(int) y) => x ??= y; |
| '''); |
| var xNullable = |
| decoratedGenericFunctionTypeAnnotation('void Function(int) x').node; |
| var xParamNullable = decoratedTypeAnnotation('int) x').node; |
| var yParamNullable = decoratedTypeAnnotation('int) y').node; |
| var returnParamNullable = decoratedTypeAnnotation('int) f').node; |
| assertEdge(xParamNullable, yParamNullable, |
| hard: false, checkable: false, guards: [xNullable]); |
| assertEdge(returnParamNullable, xParamNullable, |
| hard: false, checkable: false); |
| } |
| |
| Future<void> test_assignmentExpression_nullAware_complex_covariant() async { |
| await analyze(''' |
| List<int> f(List<int> x, List<int> y) => x ??= y; |
| '''); |
| var xNullable = decoratedTypeAnnotation('List<int> x').node; |
| var yNullable = decoratedTypeAnnotation('List<int> y').node; |
| var xElementNullable = decoratedTypeAnnotation('int> x').node; |
| var yElementNullable = decoratedTypeAnnotation('int> y').node; |
| var returnElementNullable = decoratedTypeAnnotation('int> f').node; |
| assertEdge(yNullable, xNullable, hard: false, guards: [xNullable]); |
| assertEdge(yElementNullable, xElementNullable, |
| hard: false, checkable: false, guards: [xNullable]); |
| assertEdge(xElementNullable, returnElementNullable, |
| hard: false, checkable: false); |
| } |
| |
| Future<void> test_assignmentExpression_nullAware_simple() async { |
| await analyze(''' |
| int f(int x, int y) => (x ??= y); |
| '''); |
| var yNullable = decoratedTypeAnnotation('int y').node; |
| var xNullable = decoratedTypeAnnotation('int x').node; |
| var returnNullable = decoratedTypeAnnotation('int f').node; |
| var glbNode = decoratedExpressionType('(x ??= y)')!.node; |
| assertEdge(yNullable, xNullable, hard: false, guards: [xNullable]); |
| assertEdge(yNullable, glbNode, hard: false, guards: [xNullable]); |
| assertEdge(glbNode, xNullable, hard: false); |
| assertEdge(glbNode, yNullable, hard: false); |
| assertEdge(glbNode, returnNullable, hard: false); |
| } |
| |
| Future<void> test_assignmentExpression_operands() async { |
| await analyze(''' |
| void f(int i, int j) { |
| i = j; |
| } |
| '''); |
| assertEdge(decoratedTypeAnnotation('int j').node, |
| decoratedTypeAnnotation('int i').node, |
| hard: true); |
| } |
| |
| Future<void> test_assignmentExpression_return_value() async { |
| await analyze(''' |
| void f(int i, int j) { |
| g(i = j); |
| } |
| void g(int k) {} |
| '''); |
| assertEdge(decoratedTypeAnnotation('int j').node, |
| decoratedTypeAnnotation('int k').node, |
| hard: false); |
| } |
| |
| Future<void> test_assignmentExpression_setter() async { |
| await analyze(''' |
| class C { |
| void set s(int value) {} |
| } |
| void f(C c, int i) { |
| c.s = i; |
| } |
| '''); |
| assertEdge(decoratedTypeAnnotation('int i').node, |
| decoratedTypeAnnotation('int value').node, |
| hard: true); |
| } |
| |
| Future<void> test_assignmentExpression_setter_null_aware() async { |
| await analyze(''' |
| class C { |
| void set s(int value) {} |
| } |
| int f(C c, int i) => (c?.s = i); |
| '''); |
| var lubNode = |
| decoratedExpressionType('(c?.s = i)')!.node as NullabilityNodeForLUB; |
| expect(lubNode.left, same(decoratedTypeAnnotation('C c').node)); |
| expect(lubNode.right, same(decoratedTypeAnnotation('int i').node)); |
| assertEdge(lubNode, decoratedTypeAnnotation('int f').node, hard: false); |
| } |
| |
| Future<void> test_assignmentExpression_setter_target_check() async { |
| await analyze(''' |
| class C { |
| void set s(int value) {} |
| } |
| void f(C c, int i) { |
| c.s = i; |
| } |
| '''); |
| assertNullCheck(checkExpression('c.s')!, |
| assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true)); |
| } |
| |
| @failingTest |
| Future<void> test_awaitExpression_future_nonNullable() async { |
| await analyze(''' |
| Future<void> f() async { |
| int x = await g(); |
| } |
| Future<int> g() async => 3; |
| '''); |
| |
| assertNoUpstreamNullability(decoratedTypeAnnotation('int').node); |
| } |
| |
| @failingTest |
| Future<void> test_awaitExpression_future_nullable() async { |
| await analyze(''' |
| Future<void> f() async { |
| int x = await g(); |
| } |
| Future<int> g() async => null; |
| '''); |
| |
| assertNoUpstreamNullability(decoratedTypeAnnotation('int').node); |
| } |
| |
| Future<void> test_awaitExpression_nonFuture() async { |
| await analyze(''' |
| Future<void> f() async { |
| int x = await 3; |
| } |
| '''); |
| |
| assertNoUpstreamNullability(decoratedTypeAnnotation('int').node); |
| } |
| |
| Future<void> test_binaryExpression_ampersand_result_not_null() async { |
| await analyze(''' |
| int f(int i, int j) => i & j; |
| '''); |
| |
| assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node); |
| } |
| |
| Future<void> test_binaryExpression_ampersandAmpersand() async { |
| await analyze(''' |
| bool f(bool i, bool j) => i && j; |
| '''); |
| |
| assertNoUpstreamNullability(decoratedTypeAnnotation('bool i').node); |
| } |
| |
| Future<void> test_binaryExpression_bar_result_not_null() async { |
| await analyze(''' |
| int f(int i, int j) => i | j; |
| '''); |
| |
| assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node); |
| } |
| |
| Future<void> test_binaryExpression_barBar() async { |
| await analyze(''' |
| bool f(bool i, bool j) => i || j; |
| '''); |
| |
| assertNoUpstreamNullability(decoratedTypeAnnotation('bool i').node); |
| } |
| |
| Future<void> test_binaryExpression_caret_result_not_null() async { |
| await analyze(''' |
| int f(int i, int j) => i ^ j; |
| '''); |
| |
| assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node); |
| } |
| |
| Future<void> test_binaryExpression_equal() async { |
| await analyze(''' |
| bool f(int i, int j) => i == j; |
| '''); |
| |
| assertNoUpstreamNullability(decoratedTypeAnnotation('bool f').node); |
| } |
| |
| Future<void> test_binaryExpression_equal_null() async { |
| await analyze(''' |
| void f(int i) { |
| if (i == null) { |
| g(i); |
| } else { |
| h(i); |
| } |
| } |
| void g(int j) {} |
| void h(int k) {} |
| '''); |
| var iNode = decoratedTypeAnnotation('int i').node; |
| var jNode = decoratedTypeAnnotation('int j').node; |
| var kNode = decoratedTypeAnnotation('int k').node; |
| // No edge from i to k because i is known to be non-nullable at the site of |
| // the call to h() |
| assertNoEdge(iNode, kNode); |
| // But there is an edge from i to j |
| assertEdge(iNode, jNode, hard: false, guards: [iNode]); |
| } |
| |
| Future<void> test_binaryExpression_equal_null_null() async { |
| await analyze(''' |
| void f(int i) { |
| if (null == null) { |
| g(i); |
| } |
| } |
| void g(int j) {} |
| '''); |
| var iNode = decoratedTypeAnnotation('int i').node; |
| var jNode = decoratedTypeAnnotation('int j').node; |
| assertEdge(iNode, jNode, |
| hard: false, |
| guards: TypeMatcher<Iterable<NullabilityNode?>>() |
| .having((g) => g.single, 'single value', isIn(alwaysPlus))); |
| } |
| |
| Future<void> test_binaryExpression_equal_null_yoda_condition() async { |
| await analyze(''' |
| void f(int i) { |
| if (null == i) { |
| g(i); |
| } else { |
| h(i); |
| } |
| } |
| void g(int j) {} |
| void h(int k) {} |
| '''); |
| var iNode = decoratedTypeAnnotation('int i').node; |
| var jNode = decoratedTypeAnnotation('int j').node; |
| var kNode = decoratedTypeAnnotation('int k').node; |
| // No edge from i to k because i is known to be non-nullable at the site of |
| // the call to h() |
| assertNoEdge(iNode, kNode); |
| // But there is an edge from i to j |
| assertEdge(iNode, jNode, hard: false, guards: [iNode]); |
| } |
| |
| Future<void> test_binaryExpression_gt_result_not_null() async { |
| await analyze(''' |
| bool f(int i, int j) => i > j; |
| '''); |
| |
| assertNoUpstreamNullability(decoratedTypeAnnotation('bool f').node); |
| } |
| |
| Future<void> test_binaryExpression_gtEq_result_not_null() async { |
| await analyze(''' |
| bool f(int i, int j) => i >= j; |
| '''); |
| |
| assertNoUpstreamNullability(decoratedTypeAnnotation('bool f').node); |
| } |
| |
| Future<void> test_binaryExpression_gtGt_result_not_null() async { |
| await analyze(''' |
| int f(int i, int j) => i >> j; |
| '''); |
| |
| assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node); |
| } |
| |
| Future<void> test_binaryExpression_left_dynamic() async { |
| await analyze(''' |
| Object f(dynamic x, int y) => x + g(y); |
| int g(int z) => z; |
| '''); |
| assertEdge(decoratedTypeAnnotation('int y').node, |
| decoratedTypeAnnotation('int z').node, |
| hard: true); |
| assertNoEdge(decoratedTypeAnnotation('int g').node, anyNode); |
| assertEdge(inSet(alwaysPlus), decoratedTypeAnnotation('Object f').node, |
| hard: false); |
| } |
| |
| Future<void> test_binaryExpression_lt_result_not_null() async { |
| await analyze(''' |
| bool f(int i, int j) => i < j; |
| '''); |
| |
| assertNoUpstreamNullability(decoratedTypeAnnotation('bool f').node); |
| } |
| |
| Future<void> test_binaryExpression_ltEq_result_not_null() async { |
| await analyze(''' |
| bool f(int i, int j) => i <= j; |
| '''); |
| |
| assertNoUpstreamNullability(decoratedTypeAnnotation('bool f').node); |
| } |
| |
| Future<void> test_binaryExpression_ltLt_result_not_null() async { |
| await analyze(''' |
| int f(int i, int j) => i << j; |
| '''); |
| |
| assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node); |
| } |
| |
| Future<void> test_binaryExpression_minus_result_not_null() async { |
| await analyze(''' |
| int f(int i, int j) => i - j; |
| '''); |
| |
| assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node); |
| } |
| |
| Future<void> test_binaryExpression_notEqual() async { |
| await analyze(''' |
| bool f(int i, int j) => i != j; |
| '''); |
| |
| assertNoUpstreamNullability(decoratedTypeAnnotation('bool f').node); |
| } |
| |
| Future<void> test_binaryExpression_notEqual_null() async { |
| await analyze(''' |
| void f(int i) { |
| if (i != null) { |
| h(i); |
| } else { |
| g(i); |
| } |
| } |
| void g(int j) {} |
| void h(int k) {} |
| '''); |
| var iNode = decoratedTypeAnnotation('int i').node; |
| var jNode = decoratedTypeAnnotation('int j').node; |
| var kNode = decoratedTypeAnnotation('int k').node; |
| // No edge from i to k because i is known to be non-nullable at the site of |
| // the call to h() |
| assertNoEdge(iNode, kNode); |
| // But there is an edge from i to j |
| assertEdge(iNode, jNode, hard: false, guards: [iNode]); |
| } |
| |
| Future<void> test_binaryExpression_percent_result_not_null() async { |
| await analyze(''' |
| int f(int i, int j) => i % j; |
| '''); |
| |
| assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node); |
| } |
| |
| Future<void> test_binaryExpression_plus_left_check() async { |
| await analyze(''' |
| int f(int i, int j) => i + j; |
| '''); |
| |
| assertNullCheck(checkExpression('i +')!, |
| assertEdge(decoratedTypeAnnotation('int i').node, never, hard: true)); |
| } |
| |
| Future<void> test_binaryExpression_plus_left_check_custom() async { |
| await analyze(''' |
| class Int { |
| Int operator+(Int other) => this; |
| } |
| Int f(Int i, Int j) => i + j; |
| '''); |
| |
| assertNullCheck(checkExpression('i +')!, |
| assertEdge(decoratedTypeAnnotation('Int i').node, never, hard: true)); |
| } |
| |
| Future<void> test_binaryExpression_plus_result_custom() async { |
| await analyze(''' |
| class Int { |
| Int operator+(Int other) => this; |
| } |
| Int f(Int i, Int j) => (i + j); |
| '''); |
| |
| assertNullCheck( |
| checkExpression('(i + j)')!, |
| assertEdge(decoratedTypeAnnotation('Int operator+').node, |
| decoratedTypeAnnotation('Int f').node, |
| hard: false)); |
| } |
| |
| Future<void> test_binaryExpression_plus_result_not_null() async { |
| await analyze(''' |
| int f(int i, int j) => i + j; |
| '''); |
| |
| assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node); |
| } |
| |
| Future<void> test_binaryExpression_plus_right_check() async { |
| await analyze(''' |
| int f(int i, int j) => i + j; |
| '''); |
| |
| assertNullCheck( |
| checkExpression('j;')!, |
| assertEdge(decoratedTypeAnnotation('int j').node, inSet(pointsToNever), |
| hard: true)); |
| } |
| |
| Future<void> test_binaryExpression_plus_right_check_custom() async { |
| await analyze(''' |
| class Int { |
| Int operator+(Int other) => this; |
| } |
| Int f(Int i, Int j) => i + j/*check*/; |
| '''); |
| |
| assertNullCheck( |
| checkExpression('j/*check*/')!, |
| assertEdge(decoratedTypeAnnotation('Int j').node, |
| decoratedTypeAnnotation('Int other').node, |
| hard: true)); |
| } |
| |
| Future<void> test_binaryExpression_plus_substituted() async { |
| await analyze(''' |
| class _C<T, U> { |
| T operator+(U u) => throw 'foo'; |
| } |
| Object _f(_C<int, String> c, String s) => c + s; |
| '''); |
| assertEdge( |
| decoratedTypeAnnotation('String s').node, |
| substitutionNode(decoratedTypeAnnotation('String>').node, |
| decoratedTypeAnnotation('U u').node), |
| hard: true); |
| assertEdge( |
| substitutionNode(decoratedTypeAnnotation('int,').node, |
| decoratedTypeAnnotation('T operator').node), |
| decoratedTypeAnnotation('Object _f').node, |
| hard: false); |
| } |
| |
| Future<void> test_binaryExpression_questionQuestion() async { |
| await analyze(''' |
| int f(int i, int j) => i ?? j; |
| '''); |
| |
| var left = decoratedTypeAnnotation('int i').node; |
| var right = decoratedTypeAnnotation('int j').node; |
| var expression = decoratedExpressionType('??')!.node!; |
| assertEdge(right, expression, guards: [left], hard: false); |
| expect(expression.displayName, '?? operator (test.dart:1:24)'); |
| } |
| |
| Future<void> |
| test_binaryExpression_questionQuestion_genericReturnType() async { |
| await analyze(''' |
| class C<E> { |
| C<E> operator +(C<E> c) => this; |
| } |
| C<int> f(C<int> i, C<int> j) => i ?? j; |
| '''); |
| } |
| |
| Future<void> test_binaryExpression_right_dynamic() async { |
| await analyze(''' |
| class C { |
| C operator+(C other) => other; |
| } |
| C f(C x, dynamic y) => x + y; |
| '''); |
| assertNullCheck(checkExpression('x +')!, |
| assertEdge(decoratedTypeAnnotation('C x').node, never, hard: true)); |
| assertEdge(decoratedTypeAnnotation('C operator').node, |
| decoratedTypeAnnotation('C f').node, |
| hard: false); |
| } |
| |
| Future<void> test_binaryExpression_slash_result_not_null() async { |
| await analyze(''' |
| double f(int i, int j) => i / j; |
| '''); |
| |
| assertNoUpstreamNullability(decoratedTypeAnnotation('double f').node); |
| } |
| |
| Future<void> test_binaryExpression_star_result_not_null() async { |
| await analyze(''' |
| int f(int i, int j) => i * j; |
| '''); |
| |
| assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node); |
| } |
| |
| Future<void> test_binaryExpression_tildeSlash_result_not_null() async { |
| await analyze(''' |
| int f(int i, int j) => i ~/ j; |
| '''); |
| |
| assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node); |
| } |
| |
| Future<void> test_boolLiteral() async { |
| await analyze(''' |
| bool f() { |
| return true; |
| } |
| '''); |
| assertNoUpstreamNullability(decoratedTypeAnnotation('bool').node); |
| } |
| |
| Future<void> test_cascadeExpression() async { |
| await analyze(''' |
| class C { |
| int x = 0; |
| } |
| C f(C c, int i) => c..x = i; |
| '''); |
| assertEdge(decoratedTypeAnnotation('C c').node, |
| decoratedTypeAnnotation('C f').node, |
| hard: false); |
| } |
| |
| Future<void> test_cast_type_used_as_non_nullable() async { |
| await analyze(''' |
| void f(int/*!*/ i) {} |
| void g(num/*?*/ j) { |
| f(j as int); |
| } |
| '''); |
| assertEdge(decoratedTypeAnnotation('int)').node, |
| decoratedTypeAnnotation('int/*!*/').node, |
| hard: true); |
| } |
| |
| Future<void> test_catch_clause() async { |
| await analyze(''' |
| foo() => 1; |
| main() { |
| try { foo(); } on Exception catch (e) { print(e); } |
| } |
| '''); |
| // No assertions; just checking that it doesn't crash. |
| } |
| |
| Future<void> test_catch_clause_no_type() async { |
| await analyze(''' |
| foo() => 1; |
| main() { |
| try { foo(); } catch (e) { print(e); } |
| } |
| '''); |
| // No assertions; just checking that it doesn't crash. |
| } |
| |
| Future<void> |
| test_class_alias_synthetic_constructor_with_parameters_complex() async { |
| await analyze(''' |
| class MyList<T> {} |
| class C { |
| C(MyList<int>/*1*/ x); |
| } |
| mixin M {} |
| class D = C with M; |
| D f(MyList<int>/*2*/ x) => D(x); |
| '''); |
| var syntheticConstructor = findElement.unnamedConstructor('D'); |
| var constructorType = variables!.decoratedElementType(syntheticConstructor); |
| var constructorParameterType = constructorType.positionalParameters![0]!; |
| assertEdge(decoratedTypeAnnotation('MyList<int>/*2*/').node, |
| constructorParameterType.node, |
| hard: true); |
| assertEdge(decoratedTypeAnnotation('int>/*2*/').node, |
| constructorParameterType.typeArguments[0]!.node, |
| hard: true, checkable: false); |
| assertUnion(constructorParameterType.node, |
| decoratedTypeAnnotation('MyList<int>/*1*/').node); |
| assertUnion(constructorParameterType.typeArguments[0]!.node, |
| decoratedTypeAnnotation('int>/*1*/').node); |
| } |
| |
| Future<void> |
| test_class_alias_synthetic_constructor_with_parameters_generic() async { |
| await analyze(''' |
| class C<T> { |
| C(T t); |
| } |
| mixin M {} |
| class D<U> = C<U> with M; |
| '''); |
| var syntheticConstructor = findElement.unnamedConstructor('D'); |
| var constructorType = variables!.decoratedElementType(syntheticConstructor); |
| var constructorParameterType = constructorType.positionalParameters![0]!; |
| assertUnion( |
| constructorParameterType.node, decoratedTypeAnnotation('T t').node); |
| } |
| |
| Future<void> |
| test_class_alias_synthetic_constructor_with_parameters_named() async { |
| await analyze(''' |
| class C { |
| C({int/*1*/ i}); |
| } |
| mixin M {} |
| class D = C with M; |
| D f(int/*2*/ i) => D(i: i); |
| '''); |
| var syntheticConstructor = findElement.unnamedConstructor('D'); |
| var constructorType = variables!.decoratedElementType(syntheticConstructor); |
| var constructorParameterType = constructorType.namedParameters!['i']!; |
| assertEdge( |
| decoratedTypeAnnotation('int/*2*/').node, constructorParameterType.node, |
| hard: true); |
| assertUnion(constructorParameterType.node, |
| decoratedTypeAnnotation('int/*1*/').node); |
| } |
| |
| Future<void> |
| test_class_alias_synthetic_constructor_with_parameters_optional() async { |
| await analyze(''' |
| class C { |
| C([int/*1*/ i]); |
| } |
| mixin M {} |
| class D = C with M; |
| D f(int/*2*/ i) => D(i); |
| '''); |
| var syntheticConstructor = findElement.unnamedConstructor('D'); |
| var constructorType = variables!.decoratedElementType(syntheticConstructor); |
| var constructorParameterType = constructorType.positionalParameters![0]!; |
| assertEdge( |
| decoratedTypeAnnotation('int/*2*/').node, constructorParameterType.node, |
| hard: true); |
| assertUnion(constructorParameterType.node, |
| decoratedTypeAnnotation('int/*1*/').node); |
| } |
| |
| Future<void> |
| test_class_alias_synthetic_constructor_with_parameters_required() async { |
| await analyze(''' |
| class C { |
| C(int/*1*/ i); |
| } |
| mixin M {} |
| class D = C with M; |
| D f(int/*2*/ i) => D(i); |
| '''); |
| var syntheticConstructor = findElement.unnamedConstructor('D'); |
| var constructorType = variables!.decoratedElementType(syntheticConstructor); |
| var constructorParameterType = constructorType.positionalParameters![0]!; |
| assertEdge( |
| decoratedTypeAnnotation('int/*2*/').node, constructorParameterType.node, |
| hard: true); |
| assertUnion(constructorParameterType.node, |
| decoratedTypeAnnotation('int/*1*/').node); |
| } |
| |
| Future<void> test_class_metadata() async { |
| await analyze(''' |
| @deprecated |
| class C {} |
| '''); |
| // No assertions needed; the AnnotationTracker mixin verifies that the |
| // metadata was visited. |
| } |
| |
| Future<void> test_conditionalExpression_condition_check() async { |
| await analyze(''' |
| int f(bool b, int i, int j) { |
| return (b ? i : j); |
| } |
| '''); |
| |
| var nullable_b = decoratedTypeAnnotation('bool b').node; |
| var check_b = checkExpression('b ?')!; |
| assertNullCheck(check_b, assertEdge(nullable_b, never, hard: true)); |
| } |
| |
| Future<void> test_conditionalExpression_false_guard() async { |
| await analyze('int f(int x, int y, int z) => x != null ? null : y = z;'); |
| var guard = decoratedTypeAnnotation('int x').node; |
| assertEdge(decoratedTypeAnnotation('int z').node, |
| decoratedTypeAnnotation('int y').node, |
| hard: false, guards: [guard]); |
| var conditionalDiscard = |
| variables!.conditionalDiscard(findNode.conditionalExpression('!='))!; |
| expect(conditionalDiscard, isNotNull); |
| expect(conditionalDiscard.trueGuard, isNull); |
| expect(conditionalDiscard.falseGuard, same(guard)); |
| } |
| |
| Future<void> test_conditionalExpression_functionTyped_namedParameter() async { |
| await analyze(''' |
| void f(bool b, void Function({int p}) x, void Function({int p}) y) { |
| (b ? x : y); |
| } |
| '''); |
| var xType = |
| decoratedGenericFunctionTypeAnnotation('void Function({int p}) x'); |
| var yType = |
| decoratedGenericFunctionTypeAnnotation('void Function({int p}) y'); |
| var resultType = decoratedExpressionType('(b ?')!; |
| assertLUB(resultType.node!, xType.node, yType.node); |
| assertGLB(resultType.namedParameters!['p']!.node, |
| xType.namedParameters!['p']!.node, yType.namedParameters!['p']!.node); |
| } |
| |
| Future<void> |
| test_conditionalExpression_functionTyped_normalParameter() async { |
| await analyze(''' |
| void f(bool b, void Function(int) x, void Function(int) y) { |
| (b ? x : y); |
| } |
| '''); |
| var xType = decoratedGenericFunctionTypeAnnotation('void Function(int) x'); |
| var yType = decoratedGenericFunctionTypeAnnotation('void Function(int) y'); |
| var resultType = decoratedExpressionType('(b ?')!; |
| assertLUB(resultType.node!, xType.node, yType.node); |
| assertGLB( |
| resultType.positionalParameters![0]!.node, |
| xType.positionalParameters![0]!.node, |
| yType.positionalParameters![0]!.node); |
| } |
| |
| Future<void> |
| test_conditionalExpression_functionTyped_normalParameters() async { |
| await analyze(''' |
| void f(bool b, void Function(int, int) x, void Function(int, int) y) { |
| (b ? x : y); |
| } |
| '''); |
| var xType = |
| decoratedGenericFunctionTypeAnnotation('void Function(int, int) x'); |
| var yType = |
| decoratedGenericFunctionTypeAnnotation('void Function(int, int) y'); |
| var resultType = decoratedExpressionType('(b ?')!; |
| assertLUB(resultType.node!, xType.node, yType.node); |
| assertGLB( |
| resultType.positionalParameters![0]!.node, |
| xType.positionalParameters![0]!.node, |
| yType.positionalParameters![0]!.node); |
| assertGLB( |
| resultType.positionalParameters![1]!.node, |
| xType.positionalParameters![1]!.node, |
| yType.positionalParameters![1]!.node); |
| } |
| |
| Future<void> |
| test_conditionalExpression_functionTyped_optionalParameter() async { |
| await analyze(''' |
| void f(bool b, void Function([int]) x, void Function([int]) y) { |
| (b ? x : y); |
| } |
| '''); |
| var xType = |
| decoratedGenericFunctionTypeAnnotation('void Function([int]) x'); |
| var yType = |
| decoratedGenericFunctionTypeAnnotation('void Function([int]) y'); |
| var resultType = decoratedExpressionType('(b ?')!; |
| assertLUB(resultType.node!, xType.node, yType.node); |
| assertGLB( |
| resultType.positionalParameters![0]!.node, |
| xType.positionalParameters![0]!.node, |
| yType.positionalParameters![0]!.node); |
| } |
| |
| Future<void> test_conditionalExpression_functionTyped_returnType() async { |
| await analyze(''' |
| void f(bool b, int Function() x, int Function() y) { |
| (b ? x : y); |
| } |
| '''); |
| var xType = decoratedGenericFunctionTypeAnnotation('int Function() x'); |
| var yType = decoratedGenericFunctionTypeAnnotation('int Function() y'); |
| var resultType = decoratedExpressionType('(b ?')!; |
| assertLUB(resultType.node!, xType.node, yType.node); |
| assertLUB(resultType.returnType!.node!, xType.returnType!.node, |
| yType.returnType!.node); |
| } |
| |
| Future<void> |
| test_conditionalExpression_functionTyped_returnType_void() async { |
| await analyze(''' |
| void f(bool b, void Function() x, void Function() y) { |
| (b ? x : y); |
| } |
| '''); |
| var xType = decoratedGenericFunctionTypeAnnotation('void Function() x'); |
| var yType = decoratedGenericFunctionTypeAnnotation('void Function() y'); |
| var resultType = decoratedExpressionType('(b ?')!; |
| assertLUB(resultType.node!, xType.node, yType.node); |
| expect(resultType.returnType!.node!.isImmutable, false); |
| } |
| |
| Future<void> test_conditionalExpression_general() async { |
| await analyze(''' |
| int f(bool b, int i, int j) { |
| return (b ? i : j); |
| } |
| '''); |
| |
| var nullable_i = decoratedTypeAnnotation('int i').node; |
| var nullable_j = decoratedTypeAnnotation('int j').node; |
| var nullable_conditional = decoratedExpressionType('(b ?')!.node!; |
| assertLUB(nullable_conditional, nullable_i, nullable_j); |
| var nullable_return = decoratedTypeAnnotation('int f').node; |
| assertNullCheck(checkExpression('(b ? i : j)')!, |
| assertEdge(nullable_conditional, nullable_return, hard: false)); |
| } |
| |
| Future<void> test_conditionalExpression_generic() async { |
| await analyze(''' |
| void f(bool b, Map<int, String> x, Map<int, String> y) { |
| (b ? x : y); |
| } |
| '''); |
| var xType = decoratedTypeAnnotation('Map<int, String> x'); |
| var yType = decoratedTypeAnnotation('Map<int, String> y'); |
| var resultType = decoratedExpressionType('(b ?')!; |
| assertLUB(resultType.node!, xType.node, yType.node); |
| assertLUB(resultType.typeArguments[0]!.node!, xType.typeArguments[0]!.node, |
| yType.typeArguments[0]!.node); |
| assertLUB(resultType.typeArguments[1]!.node!, xType.typeArguments[1]!.node, |
| yType.typeArguments[1]!.node); |
| } |
| |
| Future<void> test_conditionalExpression_generic_lub() async { |
| await analyze(''' |
| class A<T> {} |
| class B<T> extends A<T/*b*/> {} |
| class C<T> extends A<T/*c*/> {} |
| A<num> f(bool b, B<num> x, C<num> y) { |
| return (b ? x : y); |
| } |
| '''); |
| var bType = decoratedTypeAnnotation('B<num> x'); |
| var cType = decoratedTypeAnnotation('C<num> y'); |
| var bInA = decoratedTypeAnnotation('T/*b*/'); |
| var cInA = decoratedTypeAnnotation('T/*c*/'); |
| var resultType = decoratedExpressionType('(b ?')!; |
| assertLUB(resultType.node!, bType.node, cType.node); |
| assertLUB( |
| resultType.typeArguments[0]!.node!, |
| substitutionNode(bType.typeArguments[0]!.node, bInA.node), |
| substitutionNode(cType.typeArguments[0]!.node, cInA.node)); |
| } |
| |
| Future<void> test_conditionalExpression_generic_lub_leftSubtype() async { |
| await analyze(''' |
| class A<T> {} |
| class B<T> extends A<T/*b*/> {} |
| A<num> f(bool b, B<num> x, A<num> y) { |
| return (b ? x : y); |
| } |
| '''); |
| var aType = decoratedTypeAnnotation('A<num> y'); |
| var bType = decoratedTypeAnnotation('B<num> x'); |
| var bInA = decoratedTypeAnnotation('T/*b*/'); |
| var resultType = decoratedExpressionType('(b ?')!; |
| assertLUB(resultType.node!, bType.node, aType.node); |
| assertLUB( |
| resultType.typeArguments[0]!.node!, |
| substitutionNode(bType.typeArguments[0]!.node, bInA.node), |
| aType.typeArguments[0]!.node); |
| } |
| |
| Future<void> test_conditionalExpression_generic_lub_rightSubtype() async { |
| await analyze(''' |
| class A<T> {} |
| class B<T> extends A<T/*b*/> {} |
| A<num> f(bool b, A<num> x, B<num> y) { |
| return (b ? x : y); |
| } |
| '''); |
| var aType = decoratedTypeAnnotation('A<num> x'); |
| var bType = decoratedTypeAnnotation('B<num> y'); |
| var bInA = decoratedTypeAnnotation('T/*b*/'); |
| var resultType = decoratedExpressionType('(b ?')!; |
| assertLUB(resultType.node!, aType.node, bType.node); |
| assertLUB(resultType.typeArguments[0]!.node!, aType.typeArguments[0]!.node, |
| substitutionNode(bType.typeArguments[0]!.node, bInA.node)); |
| } |
| |
| Future<void> test_conditionalExpression_generic_typeParameter_bound() async { |
| await analyze(''' |
| List<num> f<T extends List<num>>(bool b, List<num> x, T y) { |
| return (b ? x : y); |
| } |
| '''); |
| var aType = decoratedTypeAnnotation('List<num> x'); |
| var bType = decoratedTypeAnnotation('T y'); |
| var bBound = decoratedTypeAnnotation('List<num>>'); |
| var resultType = decoratedExpressionType('(b ?')!; |
| assertLUB(resultType.node!, aType.node, |
| substitutionNode(bBound.node, bType.node)); |
| assertLUB(resultType.typeArguments[0]!.node!, aType.typeArguments[0]!.node, |
| bBound.typeArguments[0]!.node); |
| } |
| |
| Future<void> test_conditionalExpression_left_never() async { |
| await analyze(''' |
| List<int> f(bool b, List<int> i) { |
| return (b ? (throw i) : i); |
| } |
| '''); |
| |
| var nullable_i = decoratedTypeAnnotation('List<int> i').node; |
| var nullable_conditional = |
| decoratedExpressionType('(b ?')!.node as NullabilityNodeForLUB; |
| var nullable_throw = nullable_conditional.left; |
| assertNoUpstreamNullability(nullable_throw); |
| assertLUB(nullable_conditional, nullable_throw, nullable_i); |
| } |
| |
| Future<void> test_conditionalExpression_left_non_null() async { |
| await analyze(''' |
| int f(bool b, int i) { |
| return (b ? (throw i) : i); |
| } |
| '''); |
| |
| var nullable_i = decoratedTypeAnnotation('int i').node; |
| var nullable_conditional = |
| decoratedExpressionType('(b ?')!.node as NullabilityNodeForLUB; |
| var nullable_throw = nullable_conditional.left; |
| assertNoUpstreamNullability(nullable_throw); |
| assertLUB(nullable_conditional, nullable_throw, nullable_i); |
| } |
| |
| Future<void> test_conditionalExpression_left_null() async { |
| await analyze(''' |
| int f(bool b, int i) { |
| return (b ? null : i); |
| } |
| '''); |
| |
| var nullable_i = decoratedTypeAnnotation('int i').node; |
| var nullable_conditional = decoratedExpressionType('(b ?')!.node!; |
| assertLUB(nullable_conditional, inSet(alwaysPlus), nullable_i); |
| } |
| |
| Future<void> test_conditionalExpression_left_null_right_function() async { |
| await analyze(''' |
| bool Function<T>(int) g(bool b, bool Function<T>(int) f) { |
| return (b ? null : f); |
| } |
| '''); |
| |
| var nullable_i = |
| decoratedGenericFunctionTypeAnnotation('bool Function<T>(int) f').node; |
| var nullable_conditional = decoratedExpressionType('(b ?')!.node!; |
| assertLUB(nullable_conditional, inSet(alwaysPlus), nullable_i); |
| } |
| |
| Future<void> |
| test_conditionalExpression_left_null_right_parameterType() async { |
| await analyze(''' |
| T g<T>(bool b, T t) { |
| return (b ? null : t); |
| } |
| '''); |
| |
| var nullable_t = decoratedTypeAnnotation('T t').node; |
| var nullable_conditional = decoratedExpressionType('(b ?')!.node!; |
| assertLUB(nullable_conditional, inSet(alwaysPlus), nullable_t); |
| } |
| |
| Future<void> test_conditionalExpression_left_null_right_typeArgs() async { |
| await analyze(''' |
| List<int> f(bool b, List<int> l) { |
| return (b ? null : l); |
| } |
| '''); |
| |
| var nullable_i = decoratedTypeAnnotation('List<int> l').node; |
| var nullable_conditional = decoratedExpressionType('(b ?')!.node!; |
| assertLUB(nullable_conditional, inSet(alwaysPlus), nullable_i); |
| } |
| |
| Future<void> test_conditionalExpression_nullTyped_nullParameter() async { |
| await analyze(''' |
| void f(bool b, void Function(Null p) x, void Function(List<int> p) y) { |
| (b ? x : y); |
| } |
| '''); |
| var xType = |
| decoratedGenericFunctionTypeAnnotation('void Function(Null p) x'); |
| var yType = |
| decoratedGenericFunctionTypeAnnotation('void Function(List<int> p) y'); |
| var resultType = decoratedExpressionType('(b ?')!; |
| assertLUB(resultType.node!, xType.node, yType.node); |
| assertGLB( |
| resultType.positionalParameters![0]!.node, |
| xType.positionalParameters![0]!.node, |
| yType.positionalParameters![0]!.node); |
| } |
| |
| Future<void> test_conditionalExpression_parameterType() async { |
| await analyze(''' |
| T g<T>(bool b, T x, T y) { |
| return (b ? x : y); |
| } |
| '''); |
| |
| var nullable_x = decoratedTypeAnnotation('T x').node; |
| var nullable_y = decoratedTypeAnnotation('T y').node; |
| var nullable_conditional = decoratedExpressionType('(b ?')!.node!; |
| assertLUB(nullable_conditional, nullable_x, nullable_y); |
| } |
| |
| Future<void> test_conditionalExpression_right_never() async { |
| await analyze(''' |
| List<int> f(bool b, List<int> i) { |
| return (b ? i : (throw i)); |
| } |
| '''); |
| |
| var nullable_i = decoratedTypeAnnotation('List<int> i').node; |
| var nullable_conditional = |
| decoratedExpressionType('(b ?')!.node as NullabilityNodeForLUB; |
| var nullable_throw = nullable_conditional.right; |
| assertNoUpstreamNullability(nullable_throw); |
| assertLUB(nullable_conditional, nullable_i, nullable_throw); |
| } |
| |
| Future<void> test_conditionalExpression_right_non_null() async { |
| await analyze(''' |
| int f(bool b, int i) { |
| return (b ? i : (throw i)); |
| } |
| '''); |
| |
| var nullable_i = decoratedTypeAnnotation('int i').node; |
| var nullable_conditional = |
| decoratedExpressionType('(b ?')!.node as NullabilityNodeForLUB; |
| var nullable_throw = nullable_conditional.right; |
| assertNoUpstreamNullability(nullable_throw); |
| assertLUB(nullable_conditional, nullable_i, nullable_throw); |
| } |
| |
| Future<void> test_conditionalExpression_right_null() async { |
| await analyze(''' |
| int f(bool b, int i) { |
| return (b ? i : null); |
| } |
| '''); |
| |
| var nullable_i = decoratedTypeAnnotation('int i').node; |
| var nullable_conditional = decoratedExpressionType('(b ?')!.node!; |
| assertLUB(nullable_conditional, nullable_i, inSet(alwaysPlus)); |
| } |
| |
| Future<void> test_conditionalExpression_right_null_left_function() async { |
| await analyze(''' |
| bool Function<T>(int) g(bool b, bool Function<T>(int) f) { |
| return (b ? f : null); |
| } |
| '''); |
| |
| var nullable_i = |
| decoratedGenericFunctionTypeAnnotation('bool Function<T>(int) f').node; |
| var nullable_conditional = decoratedExpressionType('(b ?')!.node!; |
| assertLUB(nullable_conditional, nullable_i, inSet(alwaysPlus)); |
| } |
| |
| Future<void> test_conditionalExpression_right_null_left_typeArgs() async { |
| await analyze(''' |
| List<int> f(bool b, List<int> l) { |
| return (b ? l : null); |
| } |
| '''); |
| |
| var nullable_i = decoratedTypeAnnotation('List<int> l').node; |
| var nullable_conditional = decoratedExpressionType('(b ?')!.node!; |
| assertLUB(nullable_conditional, nullable_i, inSet(alwaysPlus)); |
| } |
| |
| Future<void> |
| test_conditionalExpression_right_null_left_typeParameter() async { |
| await analyze(''' |
| T f<T>(bool b, T t) { |
| return (b ? t : null); |
| } |
| '''); |
| |
| var nullable_t = decoratedTypeAnnotation('T t').node; |
| var nullable_conditional = decoratedExpressionType('(b ?')!.node!; |
| assertLUB(nullable_conditional, nullable_t, inSet(alwaysPlus)); |
| } |
| |
| Future<void> test_conditionalExpression_true_guard() async { |
| await analyze('int f(int x, int y, int z) => x == null ? y = z : null;'); |
| var guard = decoratedTypeAnnotation('int x').node; |
| assertEdge(decoratedTypeAnnotation('int z').node, |
| decoratedTypeAnnotation('int y').node, |
| hard: false, guards: [guard]); |
| var conditionalDiscard = |
| variables!.conditionalDiscard(findNode.conditionalExpression('=='))!; |
| expect(conditionalDiscard, isNotNull); |
| expect(conditionalDiscard.trueGuard, same(guard)); |
| expect(conditionalDiscard.falseGuard, isNull); |
| } |
| |
| Future<void> test_conditionalExpression_typeParameter_bound() async { |
| await analyze(''' |
| num f<T extends num>(bool b, num x, T y) { |
| return (b ? x : y); |
| } |
| '''); |
| var aType = decoratedTypeAnnotation('num x'); |
| var bType = decoratedTypeAnnotation('T y'); |
| var bBound = decoratedTypeAnnotation('num>'); |
| var resultType = decoratedExpressionType('(b ?')!; |
| assertLUB(resultType.node!, aType.node, |
| substitutionNode(bBound.node, bType.node)); |
| } |
| |
| Future<void> test_conditionalExpression_typeParameter_bound_bound() async { |
| await analyze(''' |
| num f<T extends R, R extends num>(bool b, num x, T y) { |
| return (b ? x : y); |
| } |
| '''); |
| var aType = decoratedTypeAnnotation('num x'); |
| var bType = decoratedTypeAnnotation('T y'); |
| var bBound = decoratedTypeAnnotation('R,'); |
| var bBoundBound = decoratedTypeAnnotation('num>'); |
| var resultType = decoratedExpressionType('(b ?')!; |
| assertLUB( |
| resultType.node!, |
| aType.node, |
| substitutionNode( |
| bBoundBound.node, substitutionNode(bBound.node, bType.node))); |
| } |
| |
| Future<void> test_conditionalExpression_typeParameter_dynamic() async { |
| // "dynamic" can short circuit LUB, incorrectly we may lose nullabilities. |
| await analyze(''' |
| dynamic f<T extends num>(bool b, dynamic x, T y) { |
| return (b ? x : y); |
| } |
| '''); |
| var aType = decoratedTypeAnnotation('dynamic x'); |
| var bType = decoratedTypeAnnotation('T y'); |
| var bBound = decoratedTypeAnnotation('num>'); |
| var resultType = decoratedExpressionType('(b ?')!; |
| assertLUB(resultType.node!, aType.node, |
| substitutionNode(bBound.node, bType.node)); |
| } |
| |
| Future<void> test_conditionalExpression_typeParameters_bound() async { |
| await analyze(''' |
| num f<T extends num, R extends num>(bool b, R x, T y) { |
| return (b ? x : y); |
| } |
| '''); |
| var aType = decoratedTypeAnnotation('R x'); |
| var bType = decoratedTypeAnnotation('T y'); |
| var aBound = decoratedTypeAnnotation('num>'); |
| var bBound = decoratedTypeAnnotation('num,'); |
| var resultType = decoratedExpressionType('(b ?')!; |
| assertLUB(resultType.node!, substitutionNode(aBound.node, aType.node), |
| substitutionNode(bBound.node, bType.node)); |
| } |
| |
| Future<void> |
| test_conditionalExpression_typeParameters_bound_left_to_right() async { |
| await analyze(''' |
| R f<T extends R, R>(bool b, R x, T y) { |
| return (b ? x : y); |
| } |
| '''); |
| var aType = decoratedTypeAnnotation('R x'); |
| var bType = decoratedTypeAnnotation('T y'); |
| var bBound = decoratedTypeAnnotation('R,'); |
| var resultType = decoratedExpressionType('(b ?')!; |
| assertLUB(resultType.node!, aType.node, |
| substitutionNode(bBound.node, bType.node)); |
| } |
| |
| Future<void> test_constructor_default_parameter_value_bool() async { |
| await analyze(''' |
| class C { |
| C([bool b = true]); |
| } |
| '''); |
| assertNoUpstreamNullability(decoratedTypeAnnotation('bool b').node); |
| } |
| |
| Future<void> test_constructor_named() async { |
| await analyze(''' |
| class C { |
| C.named(); |
| } |
| '''); |
| // No assertions; just need to make sure that the test doesn't cause an |
| // exception to be thrown. |
| } |
| |
| Future<void> test_constructor_superInitializer() async { |
| await analyze(''' |
| class C { |
| C.named(int i); |
| } |
| class D extends C { |
| D(int j) : super.named(j); |
| } |
| '''); |
| |
| var namedConstructor = findElement.constructor('named', of: 'C'); |
| var constructorType = variables!.decoratedElementType(namedConstructor); |
| var constructorParameterType = constructorType.positionalParameters![0]!; |
| assertEdge( |
| decoratedTypeAnnotation('int j').node, constructorParameterType.node, |
| hard: true); |
| } |
| |
| Future<void> test_constructor_superInitializer_withTypeArgument() async { |
| await analyze(''' |
| class C<T> { |
| C.named(T/*1*/ i); |
| } |
| class D extends C<int/*2*/> { |
| D(int/*3*/ j) : super.named(j); |
| } |
| '''); |
| |
| var nullable_t1 = decoratedTypeAnnotation('T/*1*/').node; |
| var nullable_int2 = decoratedTypeAnnotation('int/*2*/').node; |
| var nullable_int3 = decoratedTypeAnnotation('int/*3*/').node; |
| assertEdge(nullable_int3, substitutionNode(nullable_int2, nullable_t1), |
| hard: true); |
| } |
| |
| Future<void> test_constructor_superInitializer_withTypeVariable() async { |
| await analyze(''' |
| class C<T> { |
| C.named(T/*1*/ i); |
| } |
| class D<U> extends C<U/*2*/> { |
| D(U/*3*/ j) : super.named(j); |
| } |
| '''); |
| |
| var nullable_t1 = decoratedTypeAnnotation('T/*1*/').node; |
| var nullable_u2 = decoratedTypeAnnotation('U/*2*/').node; |
| var nullable_u3 = decoratedTypeAnnotation('U/*3*/').node; |
| assertEdge(nullable_u3, substitutionNode(nullable_u2, nullable_t1), |
| hard: true); |
| } |
| |
| Future<void> test_constructorDeclaration_returnType_generic() async { |
| await analyze(''' |
| class C<T, U> { |
| C(); |
| } |
| '''); |
| var constructor = findElement.unnamedConstructor('C'); |
| var constructorDecoratedType = variables!.decoratedElementType(constructor); |
| _assertType(constructorDecoratedType.type!, 'C<T, U> Function()'); |
| expect(constructorDecoratedType.node, same(never)); |
| expect(constructorDecoratedType.typeFormals, isEmpty); |
| expect(constructorDecoratedType.returnType!.node, same(never)); |
| _assertType(constructorDecoratedType.returnType!.type!, 'C<T, U>'); |
| var typeArguments = constructorDecoratedType.returnType!.typeArguments; |
| expect(typeArguments, hasLength(2)); |
| _assertType(typeArguments[0]!.type!, 'T'); |
| expect(typeArguments[0]!.node, same(never)); |
| _assertType(typeArguments[1]!.type!, 'U'); |
| expect(typeArguments[1]!.node, same(never)); |
| } |
| |
| Future<void> test_constructorDeclaration_returnType_generic_implicit() async { |
| await analyze(''' |
| class C<T, U> {} |
| '''); |
| var constructor = findElement.unnamedConstructor('C'); |
| var constructorDecoratedType = variables!.decoratedElementType(constructor); |
| _assertType(constructorDecoratedType.type!, 'C<T, U> Function()'); |
| expect(constructorDecoratedType.node, same(never)); |
| expect(constructorDecoratedType.typeFormals, isEmpty); |
| expect(constructorDecoratedType.returnType!.node, same(never)); |
| _assertType(constructorDecoratedType.returnType!.type!, 'C<T, U>'); |
| var typeArguments = constructorDecoratedType.returnType!.typeArguments; |
| expect(typeArguments, hasLength(2)); |
| _assertType(typeArguments[0]!.type!, 'T'); |
| expect(typeArguments[0]!.node, same(never)); |
| _assertType(typeArguments[1]!.type!, 'U'); |
| expect(typeArguments[1]!.node, same(never)); |
| } |
| |
| Future<void> test_constructorDeclaration_returnType_simple() async { |
| await analyze(''' |
| class C { |
| C(); |
| } |
| '''); |
| var constructorDecoratedType = |
| variables!.decoratedElementType(findElement.unnamedConstructor('C')); |
| _assertType(constructorDecoratedType.type!, 'C Function()'); |
| expect(constructorDecoratedType.node, same(never)); |
| expect(constructorDecoratedType.typeFormals, isEmpty); |
| expect(constructorDecoratedType.returnType!.node, same(never)); |
| expect(constructorDecoratedType.returnType!.typeArguments, isEmpty); |
| } |
| |
| Future<void> test_constructorDeclaration_returnType_simple_implicit() async { |
| await analyze(''' |
| class C {} |
| '''); |
| var constructorDecoratedType = |
| variables!.decoratedElementType(findElement.unnamedConstructor('C')); |
| _assertType(constructorDecoratedType.type!, 'C Function()'); |
| expect(constructorDecoratedType.node, same(never)); |
| expect(constructorDecoratedType.typeFormals, isEmpty); |
| expect(constructorDecoratedType.returnType!.node, same(never)); |
| expect(constructorDecoratedType.returnType!.typeArguments, isEmpty); |
| } |
| |
| Future<void> test_constructorFieldInitializer_generic() async { |
| await analyze(''' |
| class C<T> { |
| C(T/*1*/ x) : f = x; |
| T/*2*/ f; |
| } |
| '''); |
| assertEdge(decoratedTypeAnnotation('T/*1*/').node, |
| decoratedTypeAnnotation('T/*2*/').node, |
| hard: true); |
| } |
| |
| Future<void> test_constructorFieldInitializer_simple() async { |
| await analyze(''' |
| class C { |
| C(int/*1*/ i) : f = i; |
| int/*2*/ f; |
| } |
| '''); |
| assertEdge(decoratedTypeAnnotation('int/*1*/').node, |
| decoratedTypeAnnotation('int/*2*/').node, |
| hard: true); |
| } |
| |
| Future<void> test_constructorFieldInitializer_via_this() async { |
| await analyze(''' |
| class C { |
| C(int/*1*/ i) : this.f = i; |
| int/*2*/ f; |
| } |
| '''); |
| assertEdge(decoratedTypeAnnotation('int/*1*/').node, |
| decoratedTypeAnnotation('int/*2*/').node, |
| hard: true); |
| } |
| |
| Future<void> test_do_while_condition() async { |
| await analyze(''' |
| void f(bool b) { |
| do {} while (b); |
| } |
| '''); |
| |
| assertNullCheck(checkExpression('b);')!, |
| assertEdge(decoratedTypeAnnotation('bool b').node, never, hard: true)); |
| } |
| |
| Future<void> test_doubleLiteral() async { |
| await analyze(''' |
| double f() { |
| return 1.0; |
| } |
| '''); |
| assertNoUpstreamNullability(decoratedTypeAnnotation('double').node); |
| } |
| |
| Future<void> test_dummyNode_fromEqualityComparison_left() async { |
| await analyze(''' |
| f() { |
| int i; |
| if (i == 7) {} |
| } |
| '''); |
| var nullable_i = decoratedTypeAnnotation('int i').node; |
| assertDummyEdge(nullable_i); |
| } |
| |
| Future<void> test_dummyNode_fromEqualityComparison_right() async { |
| await analyze(''' |
| f() { |
| int i; |
| if (7 == i) {} |
| } |
| '''); |
| var nullable_i = decoratedTypeAnnotation('int i').node; |
| assertDummyEdge(nullable_i); |
| } |
| |
| Future<void> test_dummyNode_fromExpressionStatement() async { |
| await analyze(''' |
| f() { |
| int i; |
| i; |
| } |
| '''); |
| var nullable_i = decoratedTypeAnnotation('int i').node; |
| assertDummyEdge(nullable_i); |
| } |
| |
| Future<void> test_dummyNode_fromForLoopUpdaters() async { |
| await analyze(''' |
| f() { |
| int i; |
| int j; |
| for (;; i, j) {} |
| } |
| '''); |
| var nullable_i = decoratedTypeAnnotation('int i').node; |
| var nullable_j = decoratedTypeAnnotation('int j').node; |
| assertDummyEdge(nullable_i); |
| assertDummyEdge(nullable_j); |
| } |
| |
| Future<void> test_dummyNode_fromForLoopVariables() async { |
| await analyze(''' |
| f() { |
| int i; |
| for (i;;) {} |
| } |
| '''); |
| var nullable_i = decoratedTypeAnnotation('int i').node; |
| assertDummyEdge(nullable_i); |
| } |
| |
| Future<void> test_edgeOrigin_call_from_function() async { |
| await analyze(''' |
| void f(int i) {} |
| void g(int j) { |
| f(j); |
| } |
| '''); |
| assertEdge(decoratedTypeAnnotation('int j').node, |
| decoratedTypeAnnotation('int i').node, |
| hard: true, |
| codeReference: |
| matchCodeRef(offset: findNode.simple('j);').offset, function: 'g')); |
| } |
| |
| Future<void> test_edgeOrigin_call_from_method() async { |
| await analyze(''' |
| class C { |
| void f(int i) {} |
| void g(int j) { |
| f(j); |
| } |
| } |
| '''); |
| assertEdge(decoratedTypeAnnotation('int j').node, |
| decoratedTypeAnnotation('int i').node, |
| hard: true, |
| codeReference: matchCodeRef( |
| offset: findNode.simple('j);').offset, function: 'C.g')); |
| } |
| |
| Future<void> test_export_metadata() async { |
| await analyze(''' |
| @deprecated |
| export 'dart:async'; |
| '''); |
| // No assertions needed; the AnnotationTracker mixin verifies that the |
| // metadata was visited. |
| } |
| |
| Future<void> test_extension_metadata() async { |
| await analyze(''' |
| @deprecated |
| extension E on String {} |
| '''); |
| // No assertions needed; the AnnotationTracker mixin verifies that the |
| // metadata was visited. |
| } |
| |
| Future<void> test_extension_on_class_with_generic_type_arguments() async { |
| await analyze(''' |
| class C<T> {} |
| void f(C<List> x) {} |
| extension E on C<List> { |
| g() => f(this); |
| } |
| '''); |
| // No assertions yet. This test crashes. When it stops crashing, consider |
| // adding assertion(s). |
| } |
| |
| Future<void> test_extension_on_function_type() async { |
| await analyze(''' |
| extension CurryFunction<R, S, T> on R Function(S, T) { |
| /// Curry a binary function with its first argument. |
| R Function(T) curry(S first) => (T second) => this(first, second); |
| } |
| '''); |
| // No assertions yet. This test crashes. When it stops crashing, consider |
| // adding assertion(s). |
| } |
| |
| Future<void> test_extension_this_non_null_intent_explicit_direct() async { |
| await analyze(''' |
| extension on int { |
| f() => g(this); |
| } |
| void g(int i) {} |
| '''); |
| assertEdge(decoratedTypeAnnotation('int {').node, |
| decoratedTypeAnnotation('int i').node, |
| hard: true); |
| } |
| |
| Future<void> test_extension_this_non_null_intent_explicit_method() async { |
| await analyze(''' |
| extension on int { |
| f() => this.abs(); |
| } |
| '''); |
| assertEdge(decoratedTypeAnnotation('int').node, never, hard: true); |
| } |
| |
| Future<void> |
| test_extension_this_non_null_intent_explicit_property_get() async { |
| await analyze(''' |
| extension on int { |
| f() => this.isEven; |
| } |
| '''); |
| assertEdge(decoratedTypeAnnotation('int').node, never, hard: true); |
| } |
| |
| Future<void> |
| test_extension_this_non_null_intent_explicit_property_set() async { |
| await analyze(''' |
| class C { |
| int x; |
| } |
| extension on C /*reference*/ { |
| f() { |
| this.x = 0; |
| } |
| } |
| '''); |
| assertEdge(decoratedTypeAnnotation('C /*reference*/').node, never, |
| hard: true); |
| } |
| |
| Future<void> test_extension_this_non_null_intent_implicit_method() async { |
| await analyze(''' |
| extension on int { |
| f() => abs(); |
| } |
| '''); |
| assertEdge(decoratedTypeAnnotation('int').node, never, hard: true); |
| } |
| |
| Future<void> test_extension_this_non_null_intent_implicit_property() async { |
| await analyze(''' |
| extension on int { |
| f() => isEven; |
| } |
| '''); |
| assertEdge(decoratedTypeAnnotation('int').node, never, hard: true); |
| } |
| |
| Future<void> |
| test_extension_this_non_null_intent_implicit_property_set() async { |
| await analyze(''' |
| class C { |
| int x; |
| } |
| extension on C /*reference*/ { |
| f() { |
| x = 0; |
| } |
| } |
| '''); |
| assertEdge(decoratedTypeAnnotation('C /*reference*/').node, never, |
| hard: true); |
| } |
| |
| Future<void> test_field_final_does_not_override_setter() async { |
| await analyze(''' |
| abstract class A { |
| void set i(int value); |
| } |
| abstract class C implements A { |
| final int i; |
| C(this.i); |
| } |
| '''); |
| var baseNode = decoratedTypeAnnotation('int value').node; |
| var derivedNode = decoratedTypeAnnotation('int i').node; |
| assertNoEdge(derivedNode, baseNode); |
| assertNoEdge(baseNode, derivedNode); |
| } |
| |
| Future<void> test_field_initialized_in_constructor() async { |
| await analyze(''' |
| class C { |
| int i; |
| C() : i = 0; |
| } |
| '''); |
| // There is no edge from always to the type of i, because it is initialized |
| // in the constructor. |
| assertNoEdge(always, decoratedTypeAnnotation('int').node); |
| } |
| |
| Future<void> test_field_metadata() async { |
| await analyze(''' |
| class A { |
| const A(); |
| } |
| class C { |
| @A() |
| int f; |
| } |
| '''); |
| // No assertions needed; the AnnotationTracker mixin verifies that the |
| // metadata was visited. |
| } |
| |
| Future<void> test_field_overrides_field() async { |
| await analyze(''' |
| abstract class A { |
| int i; // A |
| } |
| class C implements A { |
| int i; // C |
| } |
| '''); |
| var baseNode = decoratedTypeAnnotation('int i; // A').node; |
| var derivedNode = decoratedTypeAnnotation('int i; // C').node; |
| assertEdge(baseNode, derivedNode, hard: true); |
| assertEdge(derivedNode, baseNode, hard: true); |
| } |
| |
| Future<void> test_field_overrides_field_final() async { |
| await analyze(''' |
| abstract class A { |
| final int i; // A |
| A(this.i); |
| } |
| class C implements A { |
| int i; // C |
| } |
| '''); |
| var baseNode = decoratedTypeAnnotation('int i; // A').node; |
| var derivedNode = decoratedTypeAnnotation('int i; // C').node; |
| assertEdge(derivedNode, baseNode, hard: true); |
| assertNoEdge(baseNode, derivedNode); |
| } |
| |
| Future<void> test_field_overrides_getter() async { |
| await analyze(''' |
| abstract class A { |
| int get i; |
| } |
| class C implements A { |
| int i; |
| } |
| '''); |
| var baseNode = decoratedTypeAnnotation('int get i').node; |
| var derivedNode = decoratedTypeAnnotation('int i').node; |
| assertEdge(derivedNode, baseNode, hard: true); |
| assertNoEdge(baseNode, derivedNode); |
| } |
| |
| Future<void> test_field_overrides_setter() async { |
| await analyze(''' |
| abstract class A { |
| void set i(int value); |
| } |
| class C implements A { |
| int i; |
| } |
| '''); |
| var baseNode = decoratedTypeAnnotation('int value').node; |
| var derivedNode = decoratedTypeAnnotation('int i').node; |
| assertEdge(baseNode, derivedNode, hard: true); |
| assertNoEdge(derivedNode, baseNode); |
| } |
| |
| Future<void> test_field_static_implicitInitializer() async { |
| await analyze(''' |
| class C { |
| static int i; |
| } |
| '''); |
| assertEdge(always, decoratedTypeAnnotation('int').node, hard: false); |
| } |
| |
| Future<void> test_field_type_inferred() async { |
| await analyze(''' |
| int f() => 1; |
| class C { |
| var x = f(); |
| } |
| '''); |
| var xType = |
| |