blob: 82d20e3e8323a0c019db9980ebd7cf5c2a9781f8 [file] [log] [blame]
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/dart/element/type_provider.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/generated/element_type_provider.dart';
import 'package:analyzer/src/generated/testing/test_type_provider.dart';
import 'package:nnbd_migration/src/decorated_type.dart';
import 'package:nnbd_migration/src/nullability_node.dart';
import 'package:nnbd_migration/src/variables.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'migration_visitor_test_base.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(DecoratedTypeTest);
});
}
@reflectiveTest
class DecoratedTypeTest extends Object
with DecoratedTypeTester
implements DecoratedTypeTesterBase {
final NullabilityGraph graph;
final TypeProvider typeProvider;
final Variables _variables;
final _ElementTypeProvider _elementTypeProvider;
@override
final decoratedTypeParameterBounds = DecoratedTypeParameterBounds();
factory DecoratedTypeTest() {
var typeProvider = TestTypeProvider();
var graph = NullabilityGraph();
var variables = Variables(graph, typeProvider);
return DecoratedTypeTest._(graph, typeProvider, variables);
}
DecoratedTypeTest._(this.graph, this.typeProvider, this._variables)
: _elementTypeProvider = _ElementTypeProvider(_variables);
NullabilityNode get always => graph.always;
ClassElement get listElement => typeProvider.listElement;
void assertDartType(DartType type, String expected) {
// Note: by default DartType.getDisplayString doesn't print nullability
// suffixes, so we have to override that behavior in order to make sure the
// nullability suffixes are correct.
expect(type.getDisplayString(withNullability: true), expected);
}
void setUp() {
DecoratedTypeParameterBounds.current = decoratedTypeParameterBounds;
ElementTypeProvider.current = _elementTypeProvider;
}
void tearDown() {
DecoratedTypeParameterBounds.current = null;
ElementTypeProvider.current = const ElementTypeProvider();
}
void test_equal_dynamic_and_void() {
expect(dynamic_ == dynamic_, isTrue);
expect(dynamic_ == void_, isFalse);
expect(void_ == dynamic_, isFalse);
expect(void_ == void_, isTrue);
}
void test_equal_functionType_different_nodes() {
var returnType = int_();
expect(
function(returnType, node: newNode()) ==
function(returnType, node: newNode()),
isFalse);
}
void test_equal_functionType_named_different_names() {
var node = newNode();
var argType = int_();
expect(
function(dynamic_, named: {'x': argType}, node: node) ==
function(dynamic_, named: {'y': argType}, node: node),
isFalse);
}
void test_equal_functionType_named_different_types() {
var node = newNode();
expect(
function(dynamic_, named: {'x': int_()}, node: node) ==
function(dynamic_, named: {'x': int_()}, node: node),
isFalse);
}
void test_equal_functionType_named_extra() {
var node = newNode();
var argType = int_();
var t1 = function(dynamic_, named: {'x': argType}, node: node);
var t2 = function(dynamic_, node: node);
expect(t1 == t2, isFalse);
expect(t2 == t1, isFalse);
}
void test_equal_functionType_named_same() {
var node = newNode();
var argType = int_();
expect(
function(dynamic_, named: {'x': argType}, node: node) ==
function(dynamic_, named: {'x': argType}, node: node),
isTrue);
}
void test_equal_functionType_positional_different() {
var node = newNode();
expect(
function(dynamic_, positional: [int_()], node: node) ==
function(dynamic_, positional: [int_()], node: node),
isFalse);
}
void test_equal_functionType_positional_same() {
var node = newNode();
var argType = int_();
expect(
function(dynamic_, positional: [argType], node: node) ==
function(dynamic_, positional: [argType], node: node),
isTrue);
}
void test_equal_functionType_required_different() {
var node = newNode();
expect(
function(dynamic_, required: [int_()], node: node) ==
function(dynamic_, required: [int_()], node: node),
isFalse);
}
void test_equal_functionType_required_same() {
var node = newNode();
var argType = int_();
expect(
function(dynamic_, required: [argType], node: node) ==
function(dynamic_, required: [argType], node: node),
isTrue);
}
void test_equal_functionType_required_vs_positional() {
var node = newNode();
var argType = int_();
expect(
function(dynamic_, required: [argType], node: node) ==
function(dynamic_, positional: [argType], node: node),
isFalse);
}
void test_equal_functionType_return_different() {
var node = newNode();
expect(
function(int_(), node: node) == function(int_(), node: node), isFalse);
}
void test_equal_functionType_return_same() {
var node = newNode();
var returnType = int_();
expect(function(returnType, node: node) == function(returnType, node: node),
isTrue);
}
void test_equal_functionType_typeFormals_different_bounds() {
var n1 = newNode();
var n2 = newNode();
var t = typeParameter('T', object());
var u = typeParameter('U', int_());
expect(
function(typeParameterType(t, node: n1), typeFormals: [t], node: n2) ==
function(typeParameterType(u, node: n1),
typeFormals: [u], node: n2),
isFalse);
}
void
test_equal_functionType_typeFormals_equivalent_bounds_after_substitution() {
var n1 = newNode();
var n2 = newNode();
var n3 = newNode();
var n4 = newNode();
var bound = object();
var t = typeParameter('T', bound);
var u = typeParameter('U', typeParameterType(t, node: n1));
var v = typeParameter('V', bound);
var w = typeParameter('W', typeParameterType(v, node: n1));
expect(
function(void_,
typeFormals: [t, u],
required: [
typeParameterType(t, node: n2),
typeParameterType(u, node: n3)
],
node: n4) ==
function(void_,
typeFormals: [v, w],
required: [
typeParameterType(v, node: n2),
typeParameterType(w, node: n3)
],
node: n4),
isTrue);
}
void test_equal_functionType_typeFormals_same_bounds_named() {
var n1 = newNode();
var n2 = newNode();
var bound = object();
var t = typeParameter('T', bound);
var u = typeParameter('U', bound);
expect(
function(void_,
typeFormals: [t],
named: {'x': typeParameterType(t, node: n1)},
node: n2) ==
function(void_,
typeFormals: [u],
named: {'x': typeParameterType(u, node: n1)},
node: n2),
isTrue);
}
void test_equal_functionType_typeFormals_same_bounds_positional() {
var n1 = newNode();
var n2 = newNode();
var bound = object();
var t = typeParameter('T', bound);
var u = typeParameter('U', bound);
expect(
function(void_,
typeFormals: [t],
positional: [typeParameterType(t, node: n1)],
node: n2) ==
function(void_,
typeFormals: [u],
positional: [typeParameterType(u, node: n1)],
node: n2),
isTrue);
}
void test_equal_functionType_typeFormals_same_bounds_required() {
var n1 = newNode();
var n2 = newNode();
var bound = object();
var t = typeParameter('T', bound);
var u = typeParameter('U', bound);
expect(
function(void_,
typeFormals: [t],
required: [typeParameterType(t, node: n1)],
node: n2) ==
function(void_,
typeFormals: [u],
required: [typeParameterType(u, node: n1)],
node: n2),
isTrue);
}
void test_equal_functionType_typeFormals_same_bounds_return() {
var n1 = newNode();
var n2 = newNode();
var bound = object();
var t = typeParameter('T', bound);
var u = typeParameter('U', bound);
expect(
function(typeParameterType(t, node: n1), typeFormals: [t], node: n2) ==
function(typeParameterType(u, node: n1),
typeFormals: [u], node: n2),
isTrue);
}
void test_equal_functionType_typeFormals_same_parameters() {
var n1 = newNode();
var n2 = newNode();
var t = typeParameter('T', object());
expect(
function(typeParameterType(t, node: n1), typeFormals: [t], node: n2) ==
function(typeParameterType(t, node: n1),
typeFormals: [t], node: n2),
isTrue);
}
void test_equal_interfaceType_different_args() {
var node = newNode();
expect(list(int_(), node: node) == list(int_(), node: node), isFalse);
}
void test_equal_interfaceType_different_classes() {
var node = newNode();
expect(int_(node: node) == object(node: node), isFalse);
}
void test_equal_interfaceType_different_nodes() {
expect(int_() == int_(), isFalse);
}
void test_equal_interfaceType_same() {
var node = newNode();
expect(int_(node: node) == int_(node: node), isTrue);
}
void test_equal_interfaceType_same_generic() {
var argType = int_();
var node = newNode();
expect(list(argType, node: node) == list(argType, node: node), isTrue);
}
void test_toFinalType_bottom_non_nullable() {
var type =
_variables.toFinalType(DecoratedType(NeverTypeImpl.instance, never));
assertDartType(type, 'Never');
}
void test_toFinalType_bottom_nullable() {
var type =
_variables.toFinalType(DecoratedType(NeverTypeImpl.instance, always));
assertDartType(type, 'Null');
}
void test_toFinalType_dynamic() {
var type = _variables.toFinalType(dynamic_);
assertDartType(type, 'dynamic');
}
void test_toFinalType_function_generic_bound_dynamic() {
var t = typeParameter('T', dynamic_);
var type = _variables.toFinalType(
function(dynamic_, typeFormals: [t], node: never)) as FunctionType;
assertDartType(type, 'dynamic Function<T extends dynamic>()');
assertDartType(
_elementTypeProvider.getTypeParameterBound(type.typeFormals[0])!,
'dynamic');
}
void test_toFinalType_function_generic_bound_num_question() {
var t = typeParameter('T', num_(node: always));
var type = _variables.toFinalType(
function(dynamic_, typeFormals: [t], node: never)) as FunctionType;
assertDartType(type, 'dynamic Function<T extends num?>()');
assertDartType(
_elementTypeProvider.getTypeParameterBound(type.typeFormals[0])!,
'num?');
}
void test_toFinalType_function_generic_bound_object_question() {
var t = typeParameter('T', object(node: always));
var type = _variables.toFinalType(
function(dynamic_, typeFormals: [t], node: never)) as FunctionType;
assertDartType(type, 'dynamic Function<T extends Object?>()');
assertDartType(
_elementTypeProvider.getTypeParameterBound(type.typeFormals[0])!,
'Object?');
}
void test_toFinalType_function_generic_substitute_bounds() {
var u = typeParameter('U', object(node: never));
var t = typeParameter(
'T', list(typeParameterType(u, node: never), node: never));
var v = typeParameter(
'V', list(typeParameterType(u, node: never), node: never));
var type = _variables.toFinalType(
function(dynamic_, typeFormals: [t, u, v], node: never))
as FunctionType;
assertDartType(
type,
'dynamic Function<T extends List<U>, U extends Object, '
'V extends List<U>>()');
expect(type.typeFormals[0], same(t));
expect(type.typeFormals[1], same(u));
expect(type.typeFormals[2], same(v));
expect(
((type.typeFormals[0].bound as InterfaceType).typeArguments[0]
as TypeParameterType)
.element,
same(type.typeFormals[1]));
expect(
((type.typeFormals[2].bound as InterfaceType).typeArguments[0]
as TypeParameterType)
.element,
same(type.typeFormals[1]));
}
void test_toFinalType_function_generic_substitute_named() {
var t = typeParameter('T', object(node: never));
var type = _variables.toFinalType(function(dynamic_,
typeFormals: [t],
named: {'x': list(typeParameterType(t, node: never), node: never)},
node: never)) as FunctionType;
assertDartType(type, 'dynamic Function<T extends Object>({List<T> x})');
expect(type.typeFormals[0], same(t));
expect(
((type.parameters[0].type as InterfaceType).typeArguments[0]
as TypeParameterType)
.element,
same(type.typeFormals[0]));
}
void test_toFinalType_function_generic_substitute_optional() {
var t = typeParameter('T', object(node: never));
var type = _variables.toFinalType(function(dynamic_,
typeFormals: [t],
positional: [list(typeParameterType(t, node: never), node: never)],
node: never)) as FunctionType;
assertDartType(type, 'dynamic Function<T extends Object>([List<T>])');
expect(type.typeFormals[0], same(t));
expect(
((type.parameters[0].type as InterfaceType).typeArguments[0]
as TypeParameterType)
.element,
same(type.typeFormals[0]));
}
void test_toFinalType_function_generic_substitute_required() {
var t = typeParameter('T', object());
var type = _variables.toFinalType(function(dynamic_,
typeFormals: [t],
required: [list(typeParameterType(t, node: never), node: never)],
node: never)) as FunctionType;
assertDartType(type, 'dynamic Function<T extends Object>(List<T>)');
expect(type.typeFormals[0], same(t));
expect(
((type.parameters[0].type as InterfaceType).typeArguments[0]
as TypeParameterType)
.element,
same(type.typeFormals[0]));
}
void test_toFinalType_function_generic_substitute_return_type() {
var t = typeParameter('T', object(node: never));
var type = _variables.toFinalType(function(
list(typeParameterType(t, node: never), node: never),
typeFormals: [t],
node: never)) as FunctionType;
assertDartType(type, 'List<T> Function<T extends Object>()');
expect(type.typeFormals[0], same(t));
expect(
((type.returnType as InterfaceType).typeArguments[0]
as TypeParameterType)
.element,
same(type.typeFormals[0]));
}
void test_toFinalType_function_named_parameter_non_nullable() {
var xType = int_(node: never);
var type = _variables
.toFinalType(function(dynamic_, named: {'x': xType}, node: never));
assertDartType(type, 'dynamic Function({int x})');
}
void test_toFinalType_function_named_parameter_nullable() {
var xType = int_(node: always);
var type = _variables
.toFinalType(function(dynamic_, named: {'x': xType}, node: never));
assertDartType(type, 'dynamic Function({int? x})');
}
void test_toFinalType_function_non_nullable() {
var type = _variables.toFinalType(function(dynamic_, node: never));
assertDartType(type, 'dynamic Function()');
}
void test_toFinalType_function_nullable() {
var type = _variables.toFinalType(function(dynamic_, node: always));
assertDartType(type, 'dynamic Function()?');
}
void test_toFinalType_function_optional_parameter_non_nullable() {
var argType = int_(node: never);
var type = _variables
.toFinalType(function(dynamic_, positional: [argType], node: never));
assertDartType(type, 'dynamic Function([int])');
}
void test_toFinalType_function_optional_parameter_nullable() {
var argType = int_(node: always);
var type = _variables
.toFinalType(function(dynamic_, positional: [argType], node: never));
assertDartType(type, 'dynamic Function([int?])');
}
void test_toFinalType_function_required_parameter_non_nullable() {
var argType = int_(node: never);
var type = _variables
.toFinalType(function(dynamic_, required: [argType], node: never));
assertDartType(type, 'dynamic Function(int)');
}
void test_toFinalType_function_required_parameter_nullable() {
var argType = int_(node: always);
var type = _variables
.toFinalType(function(dynamic_, required: [argType], node: never));
assertDartType(type, 'dynamic Function(int?)');
}
void test_toFinalType_function_return_type_non_nullable() {
var returnType = int_(node: never);
var type = _variables.toFinalType(function(returnType, node: never));
assertDartType(type, 'int Function()');
}
void test_toFinalType_function_return_type_nullable() {
var returnType = int_(node: always);
var type = _variables.toFinalType(function(returnType, node: never));
assertDartType(type, 'int? Function()');
}
void test_toFinalType_interface_non_nullable() {
var type = _variables.toFinalType(int_(node: never));
assertDartType(type, 'int');
}
void test_toFinalType_interface_nullable() {
var type = _variables.toFinalType(int_(node: always));
assertDartType(type, 'int?');
}
void test_toFinalType_interface_type_argument_non_nullable() {
var argType = int_(node: never);
var type = _variables.toFinalType(list(argType, node: never));
assertDartType(type, 'List<int>');
}
void test_toFinalType_interface_type_argument_nullable() {
var argType = int_(node: always);
var type = _variables.toFinalType(list(argType, node: never));
assertDartType(type, 'List<int?>');
}
void test_toFinalType_null_non_nullable() {
// We never change explicit `Null` types to `Never`, even if we can't find
// any reason they need to be nullable.
var type = _variables.toFinalType(DecoratedType(null_.type, never));
assertDartType(type, 'Null');
}
void test_toFinalType_null_nullable() {
var type = _variables.toFinalType(DecoratedType(null_.type, always));
assertDartType(type, 'Null');
}
void test_toFinalType_typeParameter_non_nullable() {
var t = typeParameter('T', object(node: never));
var type = _variables.toFinalType(typeParameterType(t, node: never));
expect(type, TypeMatcher<TypeParameterType>());
assertDartType(type, 'T');
}
void test_toFinalType_typeParameter_nullable() {
var t = typeParameter('T', object(node: never));
var type = _variables.toFinalType(typeParameterType(t, node: always));
expect(type, TypeMatcher<TypeParameterType>());
assertDartType(type, 'T?');
}
void test_toFinalType_void() {
var type = _variables.toFinalType(void_);
assertDartType(type, 'void');
}
void test_toString_bottom() {
var node = newNode();
var decoratedType = DecoratedType(NeverTypeImpl.instance, node);
expect(decoratedType.toString(), 'Never?($node)');
}
void test_toString_interface_type_argument() {
var argType = int_();
var decoratedType = list(argType, node: always);
expect(decoratedType.toString(), 'List<$argType>?');
}
void test_toString_named_parameter() {
var xType = int_();
var decoratedType = function(dynamic_, named: {'x': xType}, node: always);
expect(decoratedType.toString(), 'dynamic Function({x: $xType})?');
}
void test_toString_normal_and_named_parameter() {
var xType = int_();
var yType = int_();
var decoratedType = function(dynamic_,
required: [xType], named: {'y': yType}, node: always);
expect(decoratedType.toString(), 'dynamic Function($xType, {y: $yType})?');
}
void test_toString_normal_and_optional_parameter() {
var xType = int_();
var yType = int_();
var decoratedType = function(dynamic_,
required: [xType], positional: [yType], node: always);
expect(decoratedType.toString(), 'dynamic Function($xType, [$yType])?');
}
void test_toString_normal_parameter() {
var xType = int_();
var decoratedType = function(dynamic_, required: [xType], node: always);
expect(decoratedType.toString(), 'dynamic Function($xType)?');
}
void test_toString_optional_parameter() {
var xType = int_();
var decoratedType = function(dynamic_, positional: [xType], node: always);
expect(decoratedType.toString(), 'dynamic Function([$xType])?');
}
}
class _ElementTypeProvider extends ElementTypeProvider {
final Variables variables;
_ElementTypeProvider(this.variables);
void freshTypeParameterCreated(TypeParameterElement newTypeParameter,
TypeParameterElement oldTypeParameter) {
DecoratedTypeParameterBounds.current!.put(newTypeParameter,
DecoratedTypeParameterBounds.current!.get(oldTypeParameter));
}
DartType? getTypeParameterBound(TypeParameterElement element) {
var decoratedType = variables.decoratedTypeParameterBound(element,
allowNullUnparentedBounds: true);
if (decoratedType == null) return element.bound;
return variables.toFinalType(decoratedType);
}
}