From 582727a4ec3b4ec3ad145ff421499e54d683763b Mon Sep 17 00:00:00 2001 From: Konstantin Shcheglov Date: Thu, 23 Jan 2020 01:01:00 +0000 Subject: [PATCH] Tests for TypeName resolution, fixes for NNBD opt-in/out mixes. Change-Id: Ifb119a266306dbdf8a712d3110038179ee3cd7f8 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/132965 Reviewed-by: Brian Wilkerson Commit-Queue: Konstantin Shcheglov --- .../lib/src/dart/element/type_algebra.dart | 2 +- .../src/dart/resolver/type_name_resolver.dart | 4 +- .../lib/src/generated/type_system.dart | 22 +- .../lib/src/summary2/named_type_builder.dart | 23 +- .../lib/src/summary2/reference_resolver.dart | 7 +- .../test/src/dart/resolution/test_all.dart | 2 + .../src/dart/resolution/type_name_test.dart | 451 ++++++++++++++++++ 7 files changed, 488 insertions(+), 23 deletions(-) create mode 100644 pkg/analyzer/test/src/dart/resolution/type_name_test.dart diff --git a/pkg/analyzer/lib/src/dart/element/type_algebra.dart b/pkg/analyzer/lib/src/dart/element/type_algebra.dart index bd2f1799c980e..add78eb1b2533 100644 --- a/pkg/analyzer/lib/src/dart/element/type_algebra.dart +++ b/pkg/analyzer/lib/src/dart/element/type_algebra.dart @@ -524,7 +524,7 @@ abstract class _TypeSubstitutor extends DartTypeVisitor { } return NamedTypeBuilder( - type.isNNBD, + type.typeSystem, type.element, arguments, type.nullabilitySuffix, diff --git a/pkg/analyzer/lib/src/dart/resolver/type_name_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/type_name_resolver.dart index ef29a015fa505..982e042af829e 100644 --- a/pkg/analyzer/lib/src/dart/resolver/type_name_resolver.dart +++ b/pkg/analyzer/lib/src/dart/resolver/type_name_resolver.dart @@ -325,10 +325,12 @@ class TypeNameResolver { node, element.typeParameters.length, ); - return element.instantiate( + var type = element.instantiate( typeArguments: typeArguments, nullabilitySuffix: nullability, ); + type = typeSystem.toLegacyType(type); + return type; } else if (element is NeverElementImpl) { _buildTypeArguments(node, 0); return element.instantiate( diff --git a/pkg/analyzer/lib/src/generated/type_system.dart b/pkg/analyzer/lib/src/generated/type_system.dart index 3ab839089e961..f749c43509f8d 100644 --- a/pkg/analyzer/lib/src/generated/type_system.dart +++ b/pkg/analyzer/lib/src/generated/type_system.dart @@ -724,17 +724,21 @@ class Dart2TypeSystem extends TypeSystem { if (classElement != null) { var typeParameters = classElement.typeParameters; var typeArguments = _defaultTypeArguments(typeParameters); - return classElement.instantiate( + var type = classElement.instantiate( typeArguments: typeArguments, nullabilitySuffix: nullabilitySuffix, ); + type = toLegacyType(type); + return type; } else if (functionTypeAliasElement != null) { var typeParameters = functionTypeAliasElement.typeParameters; var typeArguments = _defaultTypeArguments(typeParameters); - return functionTypeAliasElement.instantiate( + var type = functionTypeAliasElement.instantiate( typeArguments: typeArguments, nullabilitySuffix: nullabilitySuffix, ); + type = toLegacyType(type); + return type; } else { throw ArgumentError('Missing element'); } @@ -1744,6 +1748,11 @@ class Dart2TypeSystem extends TypeSystem { .refineBinaryExpressionType(leftType, operator, rightType, currentType); } + DartType toLegacyType(DartType type) { + if (isNonNullableByDefault) return type; + return NullabilityEliminator.perform(typeProvider, type); + } + /** * Merges two types into a single type. * Compute the canonical representation of [T]. @@ -1791,9 +1800,7 @@ class Dart2TypeSystem extends TypeSystem { ) { return typeParameters.map((typeParameter) { var typeParameterImpl = typeParameter as TypeParameterElementImpl; - var typeArgument = typeParameterImpl.defaultType; - typeArgument = _toLegacyType(typeArgument); - return typeArgument; + return typeParameterImpl.defaultType; }).toList(); } @@ -2291,11 +2298,6 @@ class Dart2TypeSystem extends TypeSystem { return false; } - DartType _toLegacyType(DartType type) { - if (isNonNullableByDefault) return type; - return NullabilityEliminator.perform(typeProvider, type); - } - DartType _typeParameterResolveToObjectBounds(DartType type) { var element = type.element; type = type.resolveToBound(typeProvider.objectType); diff --git a/pkg/analyzer/lib/src/summary2/named_type_builder.dart b/pkg/analyzer/lib/src/summary2/named_type_builder.dart index 0f3fba86aa103..62ec33bb64b71 100644 --- a/pkg/analyzer/lib/src/summary2/named_type_builder.dart +++ b/pkg/analyzer/lib/src/summary2/named_type_builder.dart @@ -9,6 +9,7 @@ import 'package:analyzer/dart/element/type.dart'; import 'package:analyzer/src/dart/element/element.dart'; import 'package:analyzer/src/dart/element/type.dart'; import 'package:analyzer/src/dart/element/type_algebra.dart'; +import 'package:analyzer/src/generated/type_system.dart'; import 'package:analyzer/src/summary2/lazy_ast.dart'; import 'package:analyzer/src/summary2/type_builder.dart'; import 'package:meta/meta.dart'; @@ -17,8 +18,8 @@ import 'package:meta/meta.dart'; class NamedTypeBuilder extends TypeBuilder { static DynamicTypeImpl get _dynamicType => DynamicTypeImpl.instance; - /// Indicates whether the library is opted into NNBD. - final bool isNNBD; + /// The type system of the library with the type name. + final TypeSystemImpl typeSystem; @override final Element element; @@ -40,11 +41,11 @@ class NamedTypeBuilder extends TypeBuilder { DartType _type; NamedTypeBuilder( - this.isNNBD, this.element, this.arguments, this.nullabilitySuffix, + this.typeSystem, this.element, this.arguments, this.nullabilitySuffix, {this.node}); factory NamedTypeBuilder.of( - bool isNNBD, + TypeSystemImpl typeSystem, TypeName node, Element element, NullabilitySuffix nullabilitySuffix, @@ -57,7 +58,7 @@ class NamedTypeBuilder extends TypeBuilder { arguments = []; } - return NamedTypeBuilder(isNNBD, element, arguments, nullabilitySuffix, + return NamedTypeBuilder(typeSystem, element, arguments, nullabilitySuffix, node: node); } @@ -71,10 +72,12 @@ class NamedTypeBuilder extends TypeBuilder { if (element is ClassElement) { var parameters = element.typeParameters; var arguments = _buildArguments(parameters); - _type = element.instantiate( + var type = element.instantiate( typeArguments: arguments, nullabilitySuffix: nullabilitySuffix, ); + type = typeSystem.toLegacyType(type); + _type = type; } else if (element is GenericTypeAliasElement) { var rawType = _getRawFunctionType(element); if (rawType is FunctionType) { @@ -82,7 +85,7 @@ class NamedTypeBuilder extends TypeBuilder { var arguments = _buildArguments(parameters); var substitution = Substitution.fromPairs(parameters, arguments); var instantiated = substitution.substituteType(rawType) as FunctionType; - _type = FunctionTypeImpl( + var type = FunctionTypeImpl( typeFormals: instantiated.typeFormals, parameters: instantiated.parameters, returnType: instantiated.returnType, @@ -90,6 +93,8 @@ class NamedTypeBuilder extends TypeBuilder { element: element, typeArguments: arguments, ); + type = typeSystem.toLegacyType(type); + _type = type; } else { _type = _dynamicType; } @@ -126,7 +131,7 @@ class NamedTypeBuilder extends TypeBuilder { return this; } - return NamedTypeBuilder(isNNBD, element, arguments, nullabilitySuffix, + return NamedTypeBuilder(typeSystem, element, arguments, nullabilitySuffix, node: node); } @@ -224,7 +229,7 @@ class NamedTypeBuilder extends TypeBuilder { NullabilitySuffix _getNullabilitySuffix(bool hasQuestion) { if (hasQuestion) { return NullabilitySuffix.question; - } else if (isNNBD) { + } else if (typeSystem.isNonNullableByDefault) { return NullabilitySuffix.none; } else { return NullabilitySuffix.star; diff --git a/pkg/analyzer/lib/src/summary2/reference_resolver.dart b/pkg/analyzer/lib/src/summary2/reference_resolver.dart index 23ffa034b8e18..e4ff77ec44d84 100644 --- a/pkg/analyzer/lib/src/summary2/reference_resolver.dart +++ b/pkg/analyzer/lib/src/summary2/reference_resolver.dart @@ -11,6 +11,7 @@ import 'package:analyzer/src/dart/ast/ast.dart'; import 'package:analyzer/src/dart/element/element.dart'; import 'package:analyzer/src/dart/element/type.dart'; import 'package:analyzer/src/dart/resolver/scope.dart'; +import 'package:analyzer/src/generated/type_system.dart'; import 'package:analyzer/src/summary/idl.dart'; import 'package:analyzer/src/summary2/function_type_builder.dart'; import 'package:analyzer/src/summary2/lazy_ast.dart'; @@ -30,6 +31,7 @@ import 'package:analyzer/src/summary2/types_builder.dart'; /// the type is set, otherwise we keep it empty, so we will attempt to infer /// it later). class ReferenceResolver extends ThrowingAstVisitor { + final TypeSystemImpl _typeSystem; final NodesToBuildType nodesToBuildType; final LinkedElementFactory elementFactory; final LibraryElement _libraryElement; @@ -54,7 +56,8 @@ class ReferenceResolver extends ThrowingAstVisitor { this.unitReference, this.isNNBD, this.scope, - ) : reference = unitReference; + ) : _typeSystem = _libraryElement.typeSystem, + reference = unitReference; @override void visitBlockFunctionBody(BlockFunctionBody node) {} @@ -514,7 +517,7 @@ class ReferenceResolver extends ThrowingAstVisitor { ); } else { var builder = NamedTypeBuilder.of( - isNNBD, + _typeSystem, node, element, nullabilitySuffix, diff --git a/pkg/analyzer/test/src/dart/resolution/test_all.dart b/pkg/analyzer/test/src/dart/resolution/test_all.dart index e9b94a0aa3b4e..4fb7da7af3ff7 100644 --- a/pkg/analyzer/test/src/dart/resolution/test_all.dart +++ b/pkg/analyzer/test/src/dart/resolution/test_all.dart @@ -49,6 +49,7 @@ import 'property_access_test.dart' as property_access; import 'simple_identifier_test.dart' as simple_identifier; import 'top_type_inference_test.dart' as top_type_inference; import 'type_inference/test_all.dart' as type_inference; +import 'type_name_test.dart' as type_name; main() { defineReflectiveSuite(() { @@ -93,6 +94,7 @@ main() { property_access.main(); simple_identifier.main(); top_type_inference.main(); + type_name.main(); type_inference.main(); }, name: 'resolution'); } diff --git a/pkg/analyzer/test/src/dart/resolution/type_name_test.dart b/pkg/analyzer/test/src/dart/resolution/type_name_test.dart new file mode 100644 index 0000000000000..52b5be079b75f --- /dev/null +++ b/pkg/analyzer/test/src/dart/resolution/type_name_test.dart @@ -0,0 +1,451 @@ +// Copyright (c) 2020, 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/src/generated/engine.dart'; +import 'package:analyzer/src/test_utilities/find_element.dart'; +import 'package:test_reflective_loader/test_reflective_loader.dart'; + +import 'driver_resolution.dart'; + +main() { + defineReflectiveSuite(() { + defineReflectiveTests(TypeNameResolutionTest); + defineReflectiveTests(TypeNameResolutionWithNnbdTest); + }); +} + +@reflectiveTest +class TypeNameResolutionTest extends DriverResolutionTest { + test_class() async { + await assertNoErrorsInCode(r''' +class A {} + +f(A a) {} +'''); + + assertTypeName( + findNode.typeName('A a'), + findElement.class_('A'), + 'A', + ); + } + + test_class_generic_toBounds() async { + await assertNoErrorsInCode(r''' +class A {} + +f(A a) {} +'''); + + assertTypeName( + findNode.typeName('A a'), + findElement.class_('A'), + 'A', + ); + } + + test_class_generic_toBounds_dynamic() async { + await assertNoErrorsInCode(r''' +class A {} + +f(A a) {} +'''); + + assertTypeName( + findNode.typeName('A a'), + findElement.class_('A'), + 'A', + ); + } + + test_class_generic_typeArguments() async { + await assertNoErrorsInCode(r''' +class A {} + +f(A a) {} +'''); + + assertTypeName( + findNode.typeName('A a'), + findElement.class_('A'), + 'A', + ); + } + + test_functionTypeAlias() async { + await assertNoErrorsInCode(r''' +typedef F = int Function(); + +f(F a) {} +'''); + + assertTypeName( + findNode.typeName('F a'), + findElement.functionTypeAlias('F'), + 'int Function()', + ); + } + + test_functionTypeAlias_generic_toBounds() async { + await assertNoErrorsInCode(r''' +typedef F = T Function(); + +f(F a) {} +'''); + + assertTypeName( + findNode.typeName('F a'), + findElement.functionTypeAlias('F'), + 'num Function()', + ); + } + + test_functionTypeAlias_generic_toBounds_dynamic() async { + await assertNoErrorsInCode(r''' +typedef F = T Function(); + +f(F a) {} +'''); + + assertTypeName( + findNode.typeName('F a'), + findElement.functionTypeAlias('F'), + 'dynamic Function()', + ); + } + + test_functionTypeAlias_generic_typeArguments() async { + await assertNoErrorsInCode(r''' +typedef F = T Function(); + +f(F a) {} +'''); + + assertTypeName( + findNode.typeName('F a'), + findElement.functionTypeAlias('F'), + 'int Function()', + ); + } +} + +@reflectiveTest +class TypeNameResolutionWithNnbdTest extends TypeNameResolutionTest { + @override + AnalysisOptionsImpl get analysisOptions => AnalysisOptionsImpl() + ..contextFeatures = FeatureSet.forTesting( + sdkVersion: '2.7.0', additionalFeatures: [Feature.non_nullable]); + + ImportFindElement get import_a { + return findElement.importFind('package:test/a.dart'); + } + + @override + bool get typeToStringWithNullability => true; + + test_optIn_fromOptOut_class() async { + newFile('/test/lib/a.dart', content: r''' +class A {} +'''); + + await assertNoErrorsInCode(r''' +// @dart = 2.7 +import 'a.dart'; + +f(A a) {} +'''); + + assertTypeName( + findNode.typeName('A a'), + import_a.class_('A'), + 'A*', + ); + } + + test_optIn_fromOptOut_class_generic_toBounds() async { + newFile('/test/lib/a.dart', content: r''' +class A {} +'''); + + await assertNoErrorsInCode(r''' +// @dart = 2.7 +import 'a.dart'; + +f(A a) {} +'''); + + assertTypeName( + findNode.typeName('A a'), + import_a.class_('A'), + 'A*', + ); + } + + test_optIn_fromOptOut_class_generic_toBounds_dynamic() async { + newFile('/test/lib/a.dart', content: r''' +class A {} +'''); + + await assertNoErrorsInCode(r''' +// @dart = 2.7 +import 'a.dart'; + +f(A a) {} +'''); + + assertTypeName( + findNode.typeName('A a'), + import_a.class_('A'), + 'A*', + ); + } + + test_optIn_fromOptOut_class_generic_typeArguments() async { + newFile('/test/lib/a.dart', content: r''' +class A {} +'''); + + await assertNoErrorsInCode(r''' +// @dart = 2.7 +import 'a.dart'; + +f(A a) {} +'''); + + assertTypeName( + findNode.typeName('A a'), + import_a.class_('A'), + 'A*', + ); + } + + test_optIn_fromOptOut_functionTypeAlias() async { + newFile('/test/lib/a.dart', content: r''' +typedef F = int Function(bool); +'''); + + await assertNoErrorsInCode(r''' +// @dart = 2.7 +import 'a.dart'; + +f(F a) {} +'''); + + assertTypeName( + findNode.typeName('F a'), + import_a.functionTypeAlias('F'), + 'int* Function(bool*)*', + ); + } + + test_optIn_fromOptOut_functionTypeAlias_generic_dynamic() async { + newFile('/test/lib/a.dart', content: r''' +typedef F = T Function(bool); +'''); + + await assertNoErrorsInCode(r''' +// @dart = 2.7 +import 'a.dart'; + +f(F a) {} +'''); + + assertTypeName( + findNode.typeName('F a'), + import_a.functionTypeAlias('F'), + 'dynamic Function(bool*)*', + ); + } + + test_optIn_fromOptOut_functionTypeAlias_generic_toBounds() async { + newFile('/test/lib/a.dart', content: r''' +typedef F = T Function(bool); +'''); + + await assertNoErrorsInCode(r''' +// @dart = 2.7 +import 'a.dart'; + +f(F a) {} +'''); + + assertTypeName( + findNode.typeName('F a'), + import_a.functionTypeAlias('F'), + 'num* Function(bool*)*', + ); + } + + test_optIn_fromOptOut_functionTypeAlias_generic_typeArguments() async { + newFile('/test/lib/a.dart', content: r''' +typedef F = T Function(bool); +'''); + + await assertNoErrorsInCode(r''' +// @dart = 2.7 +import 'a.dart'; + +f(F a) {} +'''); + + assertTypeName( + findNode.typeName('F a'), + import_a.functionTypeAlias('F'), + 'int* Function(bool*)*', + ); + } + + test_optOut_fromOptIn_class() async { + newFile('/test/lib/a.dart', content: r''' +// @dart = 2.7 +class A {} +'''); + + await assertNoErrorsInCode(r''' +import 'a.dart'; + +f(A a) {} +'''); + + assertTypeName( + findNode.typeName('A a'), + import_a.class_('A'), + 'A', + ); + } + + test_optOut_fromOptIn_class_generic_toBounds() async { + newFile('/test/lib/a.dart', content: r''' +// @dart = 2.7 +class A {} +'''); + + await assertNoErrorsInCode(r''' +import 'a.dart'; + +f(A a) {} +'''); + + assertTypeName( + findNode.typeName('A a'), + import_a.class_('A'), + 'A', + ); + } + + test_optOut_fromOptIn_class_generic_toBounds_dynamic() async { + newFile('/test/lib/a.dart', content: r''' +// @dart = 2.7 +class A {} +'''); + + await assertNoErrorsInCode(r''' +import 'a.dart'; + +f(A a) {} +'''); + + assertTypeName( + findNode.typeName('A a'), + import_a.class_('A'), + 'A', + ); + } + + test_optOut_fromOptIn_class_generic_typeArguments() async { + newFile('/test/lib/a.dart', content: r''' +// @dart = 2.7 +class A {} +'''); + + await assertNoErrorsInCode(r''' +import 'a.dart'; + +f(A a) {} +'''); + + assertTypeName( + findNode.typeName('A a'), + import_a.class_('A'), + 'A', + ); + } + + test_optOut_fromOptIn_functionTypeAlias() async { + newFile('/test/lib/a.dart', content: r''' +// @dart = 2.7 +typedef F = int Function(); +'''); + + await assertNoErrorsInCode(r''' +import 'a.dart'; + +f(F a) {} +'''); + + assertTypeName( + findNode.typeName('F a'), + import_a.functionTypeAlias('F'), + 'int* Function()', + ); + } + + test_optOut_fromOptIn_functionTypeAlias_generic_toBounds() async { + newFile('/test/lib/a.dart', content: r''' +// @dart = 2.7 +typedef F = T Function(); +'''); + + await assertNoErrorsInCode(r''' +import 'a.dart'; + +f(F a) {} +'''); + + assertTypeName( + findNode.typeName('F a'), + import_a.functionTypeAlias('F'), + 'num* Function()', + ); + } + + test_optOut_fromOptIn_functionTypeAlias_generic_toBounds_dynamic() async { + newFile('/test/lib/a.dart', content: r''' +// @dart = 2.7 +typedef F = T Function(); +'''); + + await assertNoErrorsInCode(r''' +import 'a.dart'; + +f(F a) {} +'''); + + assertTypeName( + findNode.typeName('F a'), + import_a.functionTypeAlias('F'), + 'dynamic Function()', + ); + } + + test_optOut_fromOptIn_functionTypeAlias_generic_typeArguments() async { + newFile('/test/lib/a.dart', content: r''' +// @dart = 2.7 +typedef F = T Function(); +'''); + + await assertNoErrorsInCode(r''' +import 'a.dart'; + +f(F a) {} +'''); + + assertTypeName( + findNode.typeName('F a'), + import_a.functionTypeAlias('F'), + 'int* Function()', + ); + } +}