mirror of
https://github.com/dart-lang/sdk
synced 2024-09-19 23:51:47 +00:00
Issue 1182. Use the initial type for extension instantiation.
Bug: https://github.com/dart-lang/sdk/issues/43590 Bug: https://github.com/dart-lang/language/issues/1182 Change-Id: I80366ad4f777eec299143a002bbdc9d92ba61a5b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/160883 Commit-Queue: Konstantin Shcheglov <scheglov@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
parent
d63d5d93a5
commit
ba9355624c
|
@ -755,6 +755,56 @@ class TypeSystemImpl implements TypeSystem {
|
|||
return false;
|
||||
}
|
||||
|
||||
/// A dynamic bounded type is either `dynamic` itself, or a type variable
|
||||
/// whose bound is dynamic bounded, or an intersection (promoted type
|
||||
/// parameter type) whose second operand is dynamic bounded.
|
||||
bool isDynamicBounded(DartType type) {
|
||||
if (identical(type, DynamicTypeImpl.instance)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type is TypeParameterTypeImpl) {
|
||||
var bound = type.element.bound;
|
||||
if (bound != null && isDynamicBounded(bound)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var promotedBound = type.promotedBound;
|
||||
if (promotedBound != null && isDynamicBounded(promotedBound)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// A function bounded type is either `Function` itself, or a type variable
|
||||
/// whose bound is function bounded, or an intersection (promoted type
|
||||
/// parameter type) whose second operand is function bounded.
|
||||
bool isFunctionBounded(DartType type) {
|
||||
if (type is FunctionType) {
|
||||
return type.nullabilitySuffix != NullabilitySuffix.question;
|
||||
}
|
||||
|
||||
if (type is InterfaceType && type.isDartCoreFunction) {
|
||||
return type.nullabilitySuffix != NullabilitySuffix.question;
|
||||
}
|
||||
|
||||
if (type is TypeParameterTypeImpl) {
|
||||
var bound = type.element.bound;
|
||||
if (bound != null && isFunctionBounded(bound)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var promotedBound = type.promotedBound;
|
||||
if (promotedBound != null && isFunctionBounded(promotedBound)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Defines an (almost) total order on bottom and `Null` types. This does not
|
||||
/// currently consistently order two different type variables with the same
|
||||
/// bound.
|
||||
|
|
|
@ -136,27 +136,14 @@ class MethodInvocationResolver {
|
|||
}
|
||||
|
||||
DartType receiverType = receiver.staticType;
|
||||
receiverType = _resolveTypeParameter(receiverType);
|
||||
|
||||
if (_migratableAstInfoProvider.isMethodInvocationNullAware(node) &&
|
||||
_typeSystem.isNonNullableByDefault) {
|
||||
receiverType = _typeSystem.promoteToNonNull(receiverType);
|
||||
}
|
||||
|
||||
if (receiverType is InterfaceType) {
|
||||
_resolveReceiverInterfaceType(
|
||||
node, receiver, receiverType, nameNode, name);
|
||||
if (_typeSystem.isDynamicBounded(receiverType)) {
|
||||
_resolveReceiverDynamicBounded(node);
|
||||
return;
|
||||
}
|
||||
|
||||
if (receiverType is DynamicTypeImpl) {
|
||||
_resolveReceiverDynamic(node);
|
||||
return;
|
||||
}
|
||||
|
||||
if (receiverType is FunctionType) {
|
||||
_resolveReceiverFunctionType(
|
||||
node, receiver, receiverType, nameNode, name);
|
||||
if (receiverType is NeverTypeImpl) {
|
||||
_resolveReceiverNever(node, receiver, receiverType);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -165,10 +152,25 @@ class MethodInvocationResolver {
|
|||
return;
|
||||
}
|
||||
|
||||
if (receiverType is NeverTypeImpl) {
|
||||
_resolveReceiverNever(node, receiver, receiverType);
|
||||
if (_migratableAstInfoProvider.isMethodInvocationNullAware(node) &&
|
||||
_typeSystem.isNonNullableByDefault) {
|
||||
receiverType = _typeSystem.promoteToNonNull(receiverType);
|
||||
}
|
||||
|
||||
if (_typeSystem.isFunctionBounded(receiverType)) {
|
||||
_resolveReceiverFunctionBounded(
|
||||
node, receiver, receiverType, nameNode, name);
|
||||
return;
|
||||
}
|
||||
|
||||
_resolveReceiverType(
|
||||
node: node,
|
||||
receiver: receiver,
|
||||
receiverType: receiverType,
|
||||
nameNode: nameNode,
|
||||
name: name,
|
||||
receiverErrorNode: receiver,
|
||||
);
|
||||
}
|
||||
|
||||
bool _isCoreFunction(DartType type) {
|
||||
|
@ -382,7 +384,7 @@ class MethodInvocationResolver {
|
|||
_setResolution(node, member.type);
|
||||
}
|
||||
|
||||
void _resolveReceiverDynamic(MethodInvocationImpl node) {
|
||||
void _resolveReceiverDynamicBounded(MethodInvocation node) {
|
||||
var nameNode = node.methodName;
|
||||
|
||||
var objectElement = _typeSystem.typeProvider.objectElement;
|
||||
|
@ -411,8 +413,13 @@ class MethodInvocationResolver {
|
|||
node.argumentList.accept(_resolver);
|
||||
}
|
||||
|
||||
void _resolveReceiverFunctionType(MethodInvocation node, Expression receiver,
|
||||
FunctionType receiverType, SimpleIdentifier nameNode, String name) {
|
||||
void _resolveReceiverFunctionBounded(
|
||||
MethodInvocation node,
|
||||
Expression receiver,
|
||||
DartType receiverType,
|
||||
SimpleIdentifier nameNode,
|
||||
String name,
|
||||
) {
|
||||
if (name == FunctionElement.CALL_METHOD_NAME) {
|
||||
_setResolution(node, receiverType);
|
||||
// TODO(scheglov) Replace this with using FunctionType directly.
|
||||
|
@ -432,18 +439,6 @@ class MethodInvocationResolver {
|
|||
);
|
||||
}
|
||||
|
||||
void _resolveReceiverInterfaceType(MethodInvocation node, Expression receiver,
|
||||
InterfaceType receiverType, SimpleIdentifier nameNode, String name) {
|
||||
_resolveReceiverType(
|
||||
node: node,
|
||||
receiver: receiver,
|
||||
receiverType: receiverType,
|
||||
nameNode: nameNode,
|
||||
name: name,
|
||||
receiverErrorNode: receiver,
|
||||
);
|
||||
}
|
||||
|
||||
void _resolveReceiverNever(
|
||||
MethodInvocation node,
|
||||
Expression receiver,
|
||||
|
|
|
@ -328,7 +328,6 @@ class PropertyElementResolver {
|
|||
}
|
||||
|
||||
var targetType = target.staticType;
|
||||
targetType = _resolveTypeParameter(targetType);
|
||||
|
||||
if (targetType.isVoid) {
|
||||
_errorReporter.reportErrorForNode(
|
||||
|
|
|
@ -8,7 +8,6 @@ import 'package:analyzer/dart/element/element.dart';
|
|||
import 'package:analyzer/dart/element/type.dart';
|
||||
import 'package:analyzer/src/dart/element/element.dart';
|
||||
import 'package:analyzer/src/dart/element/inheritance_manager3.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';
|
||||
import 'package:analyzer/src/dart/resolver/extension_member_resolver.dart';
|
||||
|
@ -71,9 +70,9 @@ class TypePropertyResolver {
|
|||
_nameErrorEntity = nameErrorEntity;
|
||||
_resetResult();
|
||||
|
||||
receiverType = _resolveTypeParameter(receiverType);
|
||||
receiverType = _resolveTypeParameter(receiverType, ifLegacy: true);
|
||||
|
||||
if (receiverType is DynamicTypeImpl) {
|
||||
if (_typeSystem.isDynamicBounded(receiverType)) {
|
||||
_lookupInterfaceType(_typeProvider.objectType);
|
||||
_needsGetterError = false;
|
||||
_needsSetterError = false;
|
||||
|
@ -100,29 +99,33 @@ class TypePropertyResolver {
|
|||
_reportedSetterError = true;
|
||||
|
||||
// Recovery, get some resolution.
|
||||
receiverType = _resolveTypeParameter(receiverType, ifNullSafe: true);
|
||||
if (receiverType is InterfaceType) {
|
||||
_lookupInterfaceType(receiverType);
|
||||
}
|
||||
|
||||
return _toResult();
|
||||
} else {
|
||||
if (receiverType is InterfaceType) {
|
||||
_lookupInterfaceType(receiverType);
|
||||
var receiverTypeResolved =
|
||||
_resolveTypeParameter(receiverType, ifNullSafe: true);
|
||||
|
||||
if (receiverTypeResolved is InterfaceType) {
|
||||
_lookupInterfaceType(receiverTypeResolved);
|
||||
if (_hasGetterOrSetter) {
|
||||
return _toResult();
|
||||
}
|
||||
if (receiverType.isDartCoreFunction && _name == 'call') {
|
||||
if (receiverTypeResolved.isDartCoreFunction && _name == 'call') {
|
||||
_needsGetterError = false;
|
||||
_needsSetterError = false;
|
||||
return _toResult();
|
||||
}
|
||||
}
|
||||
|
||||
if (receiverType is FunctionType && _name == 'call') {
|
||||
if (receiverTypeResolved is FunctionType && _name == 'call') {
|
||||
return _toResult();
|
||||
}
|
||||
|
||||
if (receiverType is NeverType) {
|
||||
if (receiverTypeResolved is NeverType) {
|
||||
_lookupInterfaceType(_typeProvider.objectType);
|
||||
_needsGetterError = false;
|
||||
_needsSetterError = false;
|
||||
|
@ -200,8 +203,22 @@ class TypePropertyResolver {
|
|||
|
||||
/// If the given [type] is a type parameter, replace it with its bound.
|
||||
/// Otherwise, return the original type.
|
||||
DartType _resolveTypeParameter(DartType type) {
|
||||
return type?.resolveToBound(_typeProvider.objectType);
|
||||
///
|
||||
/// See https://github.com/dart-lang/language/issues/1182
|
||||
/// There was a bug in the analyzer (and CFE) - we were always resolving
|
||||
/// types to bounds before searching for a property. But extensions should
|
||||
/// be applied to original types. Fixing this would be a breaking change,
|
||||
/// so we fix it together with null safety.
|
||||
DartType _resolveTypeParameter(
|
||||
DartType type, {
|
||||
bool ifLegacy = false,
|
||||
bool ifNullSafe = false,
|
||||
}) {
|
||||
if (_typeSystem.isNonNullableByDefault ? ifNullSafe : ifLegacy) {
|
||||
return type?.resolveToBound(_typeProvider.objectType);
|
||||
} else {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
ResolutionResult _toResult() {
|
||||
|
|
|
@ -22,6 +22,7 @@ import 'runtime_type_equality_test.dart' as runtime_type_equality;
|
|||
import 'subtype_test.dart' as subtype;
|
||||
import 'top_merge_test.dart' as top_merge;
|
||||
import 'type_algebra_test.dart' as type_algebra;
|
||||
import 'type_bounded_test.dart' as type_bounded;
|
||||
import 'type_constraint_gatherer_test.dart' as type_constraint_gatherer;
|
||||
import 'type_parameter_element_test.dart' as type_parameter_element;
|
||||
import 'type_visitor_test.dart' as type_visitor;
|
||||
|
@ -48,6 +49,7 @@ main() {
|
|||
subtype.main();
|
||||
top_merge.main();
|
||||
type_algebra.main();
|
||||
type_bounded.main();
|
||||
type_constraint_gatherer.main();
|
||||
type_parameter_element.main();
|
||||
type_visitor.main();
|
||||
|
|
240
pkg/analyzer/test/src/dart/element/type_bounded_test.dart
Normal file
240
pkg/analyzer/test/src/dart/element/type_bounded_test.dart
Normal file
|
@ -0,0 +1,240 @@
|
|||
// 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/element/type.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
import '../../../generated/type_system_test.dart';
|
||||
|
||||
main() {
|
||||
defineReflectiveSuite(() {
|
||||
defineReflectiveTests(DynamicBoundedTest);
|
||||
defineReflectiveTests(FunctionBoundedTest);
|
||||
});
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class DynamicBoundedTest extends AbstractTypeSystemNullSafetyTest {
|
||||
test_dynamic() {
|
||||
_assertDynamicBounded(dynamicNone);
|
||||
}
|
||||
|
||||
test_dynamic_typeParameter_hasBound_dynamic() {
|
||||
var T = typeParameter('T', bound: dynamicNone);
|
||||
|
||||
_assertDynamicBounded(
|
||||
typeParameterTypeNone(T),
|
||||
);
|
||||
}
|
||||
|
||||
test_dynamic_typeParameter_hasBound_notDynamic() {
|
||||
var T = typeParameter('T', bound: intNone);
|
||||
|
||||
_assertNotDynamicBounded(
|
||||
typeParameterTypeNone(T),
|
||||
);
|
||||
}
|
||||
|
||||
test_dynamic_typeParameter_hasPromotedBound_dynamic() {
|
||||
var T = typeParameter('T');
|
||||
|
||||
_assertDynamicBounded(
|
||||
typeParameterTypeNone(T, promotedBound: dynamicNone),
|
||||
);
|
||||
}
|
||||
|
||||
test_dynamic_typeParameter_hasPromotedBound_notDynamic() {
|
||||
var T = typeParameter('T');
|
||||
|
||||
_assertNotDynamicBounded(
|
||||
typeParameterTypeNone(T, promotedBound: intNone),
|
||||
);
|
||||
}
|
||||
|
||||
test_dynamic_typeParameter_noBound() {
|
||||
var T = typeParameter('T');
|
||||
|
||||
_assertNotDynamicBounded(
|
||||
typeParameterTypeNone(T),
|
||||
);
|
||||
}
|
||||
|
||||
test_functionType() {
|
||||
_assertNotDynamicBounded(
|
||||
functionTypeNone(returnType: voidNone),
|
||||
);
|
||||
|
||||
_assertNotDynamicBounded(
|
||||
functionTypeNone(returnType: dynamicNone),
|
||||
);
|
||||
}
|
||||
|
||||
test_interfaceType() {
|
||||
_assertNotDynamicBounded(intNone);
|
||||
_assertNotDynamicBounded(intQuestion);
|
||||
_assertNotDynamicBounded(intStar);
|
||||
}
|
||||
|
||||
test_never() {
|
||||
_assertNotDynamicBounded(neverNone);
|
||||
_assertNotDynamicBounded(neverQuestion);
|
||||
_assertNotDynamicBounded(neverStar);
|
||||
}
|
||||
|
||||
test_void() {
|
||||
_assertNotDynamicBounded(voidNone);
|
||||
}
|
||||
|
||||
void _assertDynamicBounded(DartType type) {
|
||||
expect(typeSystem.isDynamicBounded(type), isTrue);
|
||||
}
|
||||
|
||||
void _assertNotDynamicBounded(DartType type) {
|
||||
expect(typeSystem.isDynamicBounded(type), isFalse);
|
||||
}
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class FunctionBoundedTest extends AbstractTypeSystemNullSafetyTest {
|
||||
test_dynamic() {
|
||||
_assertNotFunctionBounded(dynamicNone);
|
||||
}
|
||||
|
||||
test_dynamic_typeParameter_hasBound_functionType_none() {
|
||||
var T = typeParameter(
|
||||
'T',
|
||||
bound: functionTypeNone(returnType: voidNone),
|
||||
);
|
||||
|
||||
_assertFunctionBounded(
|
||||
typeParameterTypeNone(T),
|
||||
);
|
||||
}
|
||||
|
||||
test_dynamic_typeParameter_hasBound_functionType_question() {
|
||||
var T = typeParameter(
|
||||
'T',
|
||||
bound: functionTypeQuestion(returnType: voidNone),
|
||||
);
|
||||
|
||||
_assertNotFunctionBounded(
|
||||
typeParameterTypeNone(T),
|
||||
);
|
||||
}
|
||||
|
||||
test_dynamic_typeParameter_hasBound_functionType_star() {
|
||||
var T = typeParameter(
|
||||
'T',
|
||||
bound: functionTypeStar(returnType: voidNone),
|
||||
);
|
||||
|
||||
_assertFunctionBounded(
|
||||
typeParameterTypeStar(T),
|
||||
);
|
||||
}
|
||||
|
||||
test_dynamic_typeParameter_hasBound_notFunction() {
|
||||
var T = typeParameter('T', bound: intNone);
|
||||
|
||||
_assertNotFunctionBounded(
|
||||
typeParameterTypeNone(T),
|
||||
);
|
||||
}
|
||||
|
||||
test_dynamic_typeParameter_hasPromotedBound_functionType_none() {
|
||||
var T = typeParameter('T');
|
||||
|
||||
_assertFunctionBounded(
|
||||
typeParameterTypeNone(
|
||||
T,
|
||||
promotedBound: functionTypeNone(
|
||||
returnType: voidNone,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
test_dynamic_typeParameter_hasPromotedBound_functionType_question() {
|
||||
var T = typeParameter('T');
|
||||
|
||||
_assertNotFunctionBounded(
|
||||
typeParameterTypeStar(
|
||||
T,
|
||||
promotedBound: functionTypeQuestion(
|
||||
returnType: voidNone,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
test_dynamic_typeParameter_hasPromotedBound_functionType_star() {
|
||||
var T = typeParameter('T');
|
||||
|
||||
_assertFunctionBounded(
|
||||
typeParameterTypeStar(
|
||||
T,
|
||||
promotedBound: functionTypeStar(
|
||||
returnType: voidNone,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
test_dynamic_typeParameter_hasPromotedBound_notFunction() {
|
||||
var T = typeParameter('T');
|
||||
|
||||
_assertNotFunctionBounded(
|
||||
typeParameterTypeNone(T, promotedBound: intNone),
|
||||
);
|
||||
}
|
||||
|
||||
test_dynamic_typeParameter_noBound() {
|
||||
var T = typeParameter('T');
|
||||
|
||||
_assertNotFunctionBounded(
|
||||
typeParameterTypeNone(T),
|
||||
);
|
||||
}
|
||||
|
||||
test_functionType() {
|
||||
_assertFunctionBounded(
|
||||
functionTypeNone(returnType: voidNone),
|
||||
);
|
||||
_assertNotFunctionBounded(
|
||||
functionTypeQuestion(returnType: voidNone),
|
||||
);
|
||||
_assertFunctionBounded(
|
||||
functionTypeStar(returnType: voidNone),
|
||||
);
|
||||
|
||||
_assertFunctionBounded(
|
||||
functionTypeNone(returnType: dynamicNone),
|
||||
);
|
||||
}
|
||||
|
||||
test_interfaceType() {
|
||||
_assertNotFunctionBounded(intNone);
|
||||
_assertNotFunctionBounded(intQuestion);
|
||||
_assertNotFunctionBounded(intStar);
|
||||
}
|
||||
|
||||
test_never() {
|
||||
_assertNotFunctionBounded(neverNone);
|
||||
_assertNotFunctionBounded(neverQuestion);
|
||||
_assertNotFunctionBounded(neverStar);
|
||||
}
|
||||
|
||||
test_void() {
|
||||
_assertNotFunctionBounded(voidNone);
|
||||
}
|
||||
|
||||
void _assertFunctionBounded(DartType type) {
|
||||
expect(typeSystem.isFunctionBounded(type), isTrue);
|
||||
}
|
||||
|
||||
void _assertNotFunctionBounded(DartType type) {
|
||||
expect(typeSystem.isFunctionBounded(type), isFalse);
|
||||
}
|
||||
}
|
|
@ -2554,6 +2554,24 @@ main(A? a) {
|
|||
);
|
||||
}
|
||||
|
||||
test_hasReceiver_typeParameter_promotedToNonNullable() async {
|
||||
await assertNoErrorsInCode('''
|
||||
void f<T>(T? t) {
|
||||
if (t is int) {
|
||||
t.abs();
|
||||
}
|
||||
}
|
||||
''');
|
||||
|
||||
assertMethodInvocation2(
|
||||
findNode.methodInvocation('t.abs()'),
|
||||
element: intElement.getMethod('abs'),
|
||||
typeArgumentTypes: [],
|
||||
invokeType: 'int Function()',
|
||||
type: 'int',
|
||||
);
|
||||
}
|
||||
|
||||
test_nullShorting_cascade_firstMethodInvocation() async {
|
||||
await assertNoErrorsInCode(r'''
|
||||
class A {
|
||||
|
|
|
@ -440,6 +440,62 @@ class B extends A {
|
|||
);
|
||||
}
|
||||
|
||||
test_targetTypeParameter_dynamicBounded() async {
|
||||
await assertNoErrorsInCode('''
|
||||
class A<T extends dynamic> {
|
||||
void f(T t) {
|
||||
(t).foo;
|
||||
}
|
||||
}
|
||||
''');
|
||||
|
||||
var propertyAccess = findNode.propertyAccess('.foo');
|
||||
assertPropertyAccess2(
|
||||
propertyAccess,
|
||||
element: null,
|
||||
type: 'dynamic',
|
||||
);
|
||||
|
||||
assertSimpleIdentifier(
|
||||
propertyAccess.propertyName,
|
||||
readElement: null,
|
||||
writeElement: null,
|
||||
type: 'dynamic',
|
||||
);
|
||||
}
|
||||
|
||||
test_targetTypeParameter_noBound() async {
|
||||
await resolveTestCode('''
|
||||
class C<T> {
|
||||
void f(T t) {
|
||||
(t).foo;
|
||||
}
|
||||
}
|
||||
''');
|
||||
assertErrorsInResult(expectedErrorsByNullability(
|
||||
nullable: [
|
||||
error(CompileTimeErrorCode.UNCHECKED_USE_OF_NULLABLE_VALUE, 33, 3),
|
||||
],
|
||||
legacy: [
|
||||
error(CompileTimeErrorCode.UNDEFINED_GETTER, 37, 3),
|
||||
],
|
||||
));
|
||||
|
||||
var propertyAccess = findNode.propertyAccess('.foo');
|
||||
assertPropertyAccess2(
|
||||
propertyAccess,
|
||||
element: null,
|
||||
type: 'dynamic',
|
||||
);
|
||||
|
||||
assertSimpleIdentifier(
|
||||
propertyAccess.propertyName,
|
||||
readElement: null,
|
||||
writeElement: null,
|
||||
type: 'dynamic',
|
||||
);
|
||||
}
|
||||
|
||||
test_tearOff_method() async {
|
||||
await assertNoErrorsInCode('''
|
||||
class A {
|
||||
|
|
|
@ -6,15 +6,20 @@ import 'package:analyzer/src/error/codes.dart';
|
|||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
import '../context_collection_resolution.dart';
|
||||
import '../resolution.dart';
|
||||
|
||||
main() {
|
||||
defineReflectiveSuite(() {
|
||||
defineReflectiveTests(ExtensionMethodsTest);
|
||||
defineReflectiveTests(ExtensionMethodsWithNullSafetyTest);
|
||||
});
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class ExtensionMethodsTest extends PubPackageResolutionTest {
|
||||
class ExtensionMethodsTest extends PubPackageResolutionTest
|
||||
with ExtensionMethodsTestCases {}
|
||||
|
||||
mixin ExtensionMethodsTestCases on ResolutionTest {
|
||||
test_implicit_getter() async {
|
||||
await assertNoErrorsInCode('''
|
||||
class A<T> {}
|
||||
|
@ -150,6 +155,118 @@ void f(A<int> a) {
|
|||
);
|
||||
}
|
||||
|
||||
test_implicit_targetTypeParameter_hasBound_methodInvocation() async {
|
||||
await assertNoErrorsInCode('''
|
||||
extension Test<T> on T {
|
||||
T Function(T) test() => throw 0;
|
||||
}
|
||||
|
||||
void f<S extends num>(S x) {
|
||||
x.test();
|
||||
}
|
||||
''');
|
||||
|
||||
if (result.libraryElement.isNonNullableByDefault) {
|
||||
assertMethodInvocation2(
|
||||
findNode.methodInvocation('test();'),
|
||||
element: elementMatcher(
|
||||
findElement.method('test'),
|
||||
substitution: {'T': 'S'},
|
||||
),
|
||||
typeArgumentTypes: [],
|
||||
invokeType: 'S Function(S) Function()',
|
||||
type: 'S Function(S)',
|
||||
);
|
||||
} else {
|
||||
assertMethodInvocation2(
|
||||
findNode.methodInvocation('test();'),
|
||||
element: elementMatcher(
|
||||
findElement.method('test'),
|
||||
substitution: {'T': 'num'},
|
||||
),
|
||||
typeArgumentTypes: [],
|
||||
invokeType: 'num Function(num) Function()',
|
||||
type: 'num Function(num)',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
test_implicit_targetTypeParameter_hasBound_propertyAccess_getter() async {
|
||||
await assertNoErrorsInCode('''
|
||||
extension Test<T> on T {
|
||||
T Function(T) get test => throw 0;
|
||||
}
|
||||
|
||||
void f<S extends num>(S x) {
|
||||
(x).test;
|
||||
}
|
||||
''');
|
||||
|
||||
if (result.libraryElement.isNonNullableByDefault) {
|
||||
assertPropertyAccess2(
|
||||
findNode.propertyAccess('.test'),
|
||||
element: elementMatcher(
|
||||
findElement.getter('test'),
|
||||
substitution: {'T': 'S'},
|
||||
),
|
||||
type: 'S Function(S)',
|
||||
);
|
||||
} else {
|
||||
assertPropertyAccess2(
|
||||
findNode.propertyAccess('.test'),
|
||||
element: elementMatcher(
|
||||
findElement.getter('test'),
|
||||
substitution: {'T': 'num'},
|
||||
),
|
||||
type: 'num Function(num)',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
test_implicit_targetTypeParameter_hasBound_propertyAccess_setter() async {
|
||||
await assertNoErrorsInCode('''
|
||||
extension Test<T> on T {
|
||||
void set test(T _) {}
|
||||
}
|
||||
|
||||
T g<T>() => throw 0;
|
||||
|
||||
void f<S extends num>(S x) {
|
||||
(x).test = g();
|
||||
}
|
||||
''');
|
||||
|
||||
if (result.libraryElement.isNonNullableByDefault) {
|
||||
assertPropertyAccess2(
|
||||
findNode.propertyAccess('.test'),
|
||||
element: elementMatcher(
|
||||
findElement.setter('test'),
|
||||
substitution: {'T': 'S'},
|
||||
),
|
||||
type: 'S',
|
||||
);
|
||||
|
||||
assertTypeArgumentTypes(
|
||||
findNode.methodInvocation('g()'),
|
||||
['S'],
|
||||
);
|
||||
} else {
|
||||
assertPropertyAccess2(
|
||||
findNode.propertyAccess('.test'),
|
||||
element: elementMatcher(
|
||||
findElement.setter('test'),
|
||||
substitution: {'T': 'num'},
|
||||
),
|
||||
type: 'num',
|
||||
);
|
||||
|
||||
assertTypeArgumentTypes(
|
||||
findNode.methodInvocation('g()'),
|
||||
['num'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
test_override_downward_hasTypeArguments() async {
|
||||
await assertNoErrorsInCode('''
|
||||
extension E<T> on Set<T> {
|
||||
|
@ -425,3 +542,7 @@ void f(A<int> a) {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class ExtensionMethodsWithNullSafetyTest extends PubPackageResolutionTest
|
||||
with WithNullSafetyMixin, ExtensionMethodsTestCases {}
|
||||
|
|
Loading…
Reference in a new issue