Version 2.15.0-238.0.dev
Merge commit '4e822a354058bc0ed8a441e6d59c4355170c2e8d' into 'dev'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d770091..7ae5db7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -494,7 +494,9 @@
#### Linter
-Updated the Linter to `1.13.0`, which includes changes that
+Updated the Linter to `1.14.0`, which includes changes that
+- fix `omit_local_variable_types` to not flag a local type that is
+ required for inference.
- allow `while (true) { ... }` in `literal_only_boolean_expressions`.
- fix `file_names` to report at the start of the file (not the entire
compilation unit).
diff --git a/DEPS b/DEPS
index 3f5007c..72dd637 100644
--- a/DEPS
+++ b/DEPS
@@ -122,7 +122,7 @@
"intl_tag": "0.17.0-nullsafety",
"jinja2_rev": "2222b31554f03e62600cd7e383376a7c187967a1",
"json_rpc_2_rev": "7e00f893440a72de0637970325e4ea44bd1e8c8e",
- "linter_tag": "1.13.0",
+ "linter_tag": "1.14.0",
"lints_tag": "f9670df2a66e0ec12eb51554e70c1cbf56c8f5d0",
"logging_rev": "575781ef196e4fed4fb737e38fb4b73d62727187",
"markupsafe_rev": "8f45f5cfa0009d2a70589bcda0349b8cb2b72783",
diff --git a/pkg/analyzer/lib/dart/ast/ast.dart b/pkg/analyzer/lib/dart/ast/ast.dart
index 362575d..b9efa0a 100644
--- a/pkg/analyzer/lib/dart/ast/ast.dart
+++ b/pkg/analyzer/lib/dart/ast/ast.dart
@@ -3221,7 +3221,7 @@
/// An expression that implicitly makes reference to a method.
///
/// Clients may not extend, implement or mix-in this class.
-abstract class MethodReferenceExpression implements AstNode {
+abstract class MethodReferenceExpression implements Expression {
/// Return the element associated with the expression based on the static
/// types, or `null` if the AST structure has not been resolved, or there is
/// no meaningful static element to return (e.g. because this is a
diff --git a/pkg/analyzer/lib/src/dart/ast/utilities.dart b/pkg/analyzer/lib/src/dart/ast/utilities.dart
index 6e2918a..ec080e8 100644
--- a/pkg/analyzer/lib/src/dart/ast/utilities.dart
+++ b/pkg/analyzer/lib/src/dart/ast/utilities.dart
@@ -3004,11 +3004,15 @@
///
/// Throws an [ArgumentError] if either node is `null`, if the old node does
/// not have a parent node, or if the AST structure has been corrupted.
- static bool replace(AstNode oldNode, AstNode newNode) {
+ ///
+ /// If [newNode] is the parent of [oldNode] already (because [newNode] became
+ /// the parent of [oldNode] in its constructor), this action will loop
+ /// infinitely; pass [oldNode]'s previous parent as [parent] to avoid this.
+ static bool replace(AstNode oldNode, AstNode newNode, {AstNode? parent}) {
if (identical(oldNode, newNode)) {
return true;
}
- var parent = oldNode.parent;
+ parent ??= oldNode.parent;
if (parent == null) {
throw ArgumentError("The old node is not a child of another node");
}
diff --git a/pkg/analyzer/lib/src/dart/element/type_system.dart b/pkg/analyzer/lib/src/dart/element/type_system.dart
index ce90a43..ef329f4 100644
--- a/pkg/analyzer/lib/src/dart/element/type_system.dart
+++ b/pkg/analyzer/lib/src/dart/element/type_system.dart
@@ -312,8 +312,12 @@
.toList();
}
- /// Given a type t, if t is an interface type with a call method defined,
- /// return the function type for the call method, otherwise return null.
+ /// Given a type [t], if [t] is an interface type with a `call` method
+ /// defined, return the function type for the `call` method, otherwise return
+ /// `null`.
+ ///
+ /// This does not find extension methods (which are not defined on an
+ /// interface type); it is meant to find implicit call references.
FunctionType? getCallMethodType(DartType t) {
if (t is InterfaceType) {
return t
@@ -685,7 +689,7 @@
return true;
}
- // A call method tearoff
+ // A 'call' method tearoff.
if (fromType is InterfaceType &&
!isNullable(fromType) &&
acceptsFunctionType(toType)) {
diff --git a/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart
index 53c06aa..718c99b 100644
--- a/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart
@@ -204,9 +204,10 @@
DartType assignedType;
DartType nodeType;
+ var rightHandSide = node.rightHandSide;
var operator = node.operator.type;
if (operator == TokenType.EQ) {
- assignedType = node.rightHandSide.typeOrThrow;
+ assignedType = rightHandSide.typeOrThrow;
nodeType = assignedType;
} else if (operator == TokenType.QUESTION_QUESTION_EQ) {
var leftType = node.readType!;
@@ -216,7 +217,7 @@
leftType = _typeSystem.promoteToNonNull(leftType);
}
- assignedType = node.rightHandSide.typeOrThrow;
+ assignedType = rightHandSide.typeOrThrow;
nodeType = _typeSystem.getLeastUpperBound(leftType, assignedType);
} else if (operator == TokenType.AMPERSAND_AMPERSAND_EQ ||
operator == TokenType.BAR_BAR_EQ) {
@@ -226,7 +227,7 @@
var operatorElement = node.staticElement;
if (operatorElement != null) {
var leftType = node.readType!;
- var rightType = node.rightHandSide.typeOrThrow;
+ var rightType = rightHandSide.typeOrThrow;
assignedType = _typeSystem.refineBinaryExpressionType(
leftType,
operator,
@@ -241,6 +242,10 @@
}
_inferenceHelper.recordStaticType(node, nodeType);
+ var callReference = _resolver.insertImplicitCallReference(rightHandSide);
+ if (callReference != rightHandSide) {
+ assignedType = callReference.typeOrThrow;
+ }
// TODO(scheglov) Remove from ErrorVerifier?
_checkForInvalidAssignment(
diff --git a/pkg/analyzer/lib/src/dart/resolver/exit_detector.dart b/pkg/analyzer/lib/src/dart/resolver/exit_detector.dart
index 7a39ba8..077946d 100644
--- a/pkg/analyzer/lib/src/dart/resolver/exit_detector.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/exit_detector.dart
@@ -369,6 +369,11 @@
}
@override
+ bool visitImplicitCallReference(ImplicitCallReference node) {
+ return _nodeExits(node.expression);
+ }
+
+ @override
bool visitIndexExpression(IndexExpression node) {
Expression target = node.realTarget;
if (_nodeExits(target)) {
diff --git a/pkg/analyzer/lib/src/dart/resolver/function_reference_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/function_reference_resolver.dart
index 515cc1e..192c7e5 100644
--- a/pkg/analyzer/lib/src/dart/resolver/function_reference_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/function_reference_resolver.dart
@@ -78,10 +78,10 @@
} else if (functionType is FunctionType) {
_resolve(node: node, rawType: functionType);
} else {
- var callMethodType =
- _resolver.typeSystem.getCallMethodType(functionType);
- if (callMethodType != null) {
- _resolve(node: node, rawType: callMethodType);
+ var callMethod = _getCallMethod(node, function.staticType);
+ if (callMethod is MethodElement) {
+ _resolveAsImplicitCallReference(node, callMethod);
+ return;
} else {
_resolveDisallowedExpression(node, functionType);
}
@@ -98,7 +98,7 @@
if (prefixElement is VariableElement) {
prefixType = prefixElement.type;
} else if (prefixElement is PropertyAccessorElement) {
- prefixType = prefixElement.returnType;
+ prefixType = prefixElement.variable.type;
}
if (prefixType != null && prefixType.isDynamic) {
@@ -146,6 +146,27 @@
}
}
+ ExecutableElement? _getCallMethod(
+ FunctionReferenceImpl node, DartType? type) {
+ if (type is! InterfaceType) {
+ return null;
+ }
+ if (type.nullabilitySuffix == NullabilitySuffix.question) {
+ // If the interface type is nullable, only an applicable extension method
+ // applies.
+ return _extensionResolver
+ .findExtension(type, node, FunctionElement.CALL_METHOD_NAME)
+ .getter;
+ }
+ // Otherwise, a 'call' method on the interface, or on an applicable
+ // extension method applies.
+ return type.lookUpMethod2(
+ FunctionElement.CALL_METHOD_NAME, type.element.library) ??
+ _extensionResolver
+ .findExtension(type, node, FunctionElement.CALL_METHOD_NAME)
+ .getter;
+ }
+
void _reportInvalidAccessToStaticMember(
SimpleIdentifier nameNode,
ExecutableElement element, {
@@ -245,6 +266,27 @@
}
}
+ void _resolveAsImplicitCallReference(
+ FunctionReferenceImpl node, MethodElement callMethod) {
+ // `node<...>` is to be treated as `node.call<...>`.
+ var callMethodType = callMethod.type;
+ var typeArgumentTypes = _checkTypeArguments(
+ // `node.typeArguments`, coming from the parser, is never null.
+ node.typeArguments!, FunctionElement.CALL_METHOD_NAME,
+ callMethodType.typeFormals,
+ CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_FUNCTION,
+ );
+ var callReference = astFactory.implicitCallReference(
+ expression: node.function,
+ staticElement: callMethod,
+ typeArguments: node.typeArguments,
+ typeArgumentTypes: typeArgumentTypes,
+ );
+ NodeReplacer.replace(node, callReference);
+ var instantiatedType = callMethodType.instantiate(typeArgumentTypes);
+ callReference.staticType = instantiatedType;
+ }
+
void _resolveConstructorReference(FunctionReferenceImpl node) {
// TODO(srawlins): Rewrite and resolve [node] as a constructor reference.
node.function.accept(_resolver);
@@ -427,6 +469,11 @@
void _resolvePropertyAccessFunction(
FunctionReferenceImpl node, PropertyAccessImpl function) {
function.accept(_resolver);
+ var callMethod = _getCallMethod(node, function.staticType);
+ if (callMethod is MethodElement) {
+ _resolveAsImplicitCallReference(node, callMethod);
+ return;
+ }
var target = function.realTarget;
DartType targetType;
@@ -439,7 +486,7 @@
if (targetElement is VariableElement) {
targetType = targetElement.type;
} else if (targetElement is PropertyAccessorElement) {
- targetType = targetElement.returnType;
+ targetType = targetElement.variable.type;
} else {
// TODO(srawlins): Can we get here?
node.staticType = DynamicTypeImpl.instance;
@@ -540,6 +587,11 @@
}
} else if (element is ExecutableElement) {
node.function.accept(_resolver);
+ var callMethod = _getCallMethod(node, node.function.staticType);
+ if (callMethod is MethodElement) {
+ _resolveAsImplicitCallReference(node, callMethod);
+ return;
+ }
_resolve(
node: node,
rawType: node.function.typeOrThrow as FunctionType,
@@ -604,7 +656,7 @@
}
if (method is PropertyAccessorElement) {
- _resolve(node: node, rawType: method.returnType);
+ _resolve(node: node, rawType: method.variable.type);
return;
}
@@ -661,7 +713,12 @@
return;
} else if (element is PropertyAccessorElement) {
function.staticElement = element;
- function.staticType = element.returnType;
+ function.staticType = element.variable.type;
+ var callMethod = _getCallMethod(node, element.variable.type);
+ if (callMethod is MethodElement) {
+ _resolveAsImplicitCallReference(node, callMethod);
+ return;
+ }
_resolve(node: node, rawType: element.returnType);
return;
} else if (element is ExecutableElement) {
@@ -672,6 +729,11 @@
} else if (element is VariableElement) {
function.staticElement = element;
function.staticType = element.type;
+ var callMethod = _getCallMethod(node, element.type);
+ if (callMethod is MethodElement) {
+ _resolveAsImplicitCallReference(node, callMethod);
+ return;
+ }
_resolve(node: node, rawType: element.type);
return;
} else if (element is ExtensionElement) {
diff --git a/pkg/analyzer/lib/src/dart/resolver/typed_literal_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/typed_literal_resolver.dart
index e781de9..a9fce6a 100644
--- a/pkg/analyzer/lib/src/dart/resolver/typed_literal_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/typed_literal_resolver.dart
@@ -98,6 +98,7 @@
}
node.visitChildren(_resolver);
+ _insertImplicitCallReferences(node);
_resolveListLiteral2(node);
}
@@ -154,6 +155,7 @@
}
node.visitChildren(_resolver);
+ _insertImplicitCallReferences(node);
_resolveSetOrMapLiteral2(node);
}
@@ -597,6 +599,34 @@
);
}
+ void _insertImplicitCallReference(CollectionElement? node) {
+ if (node is Expression) {
+ _resolver.insertImplicitCallReference(node);
+ } else if (node is MapLiteralEntry) {
+ _insertImplicitCallReference(node.key);
+ _insertImplicitCallReference(node.value);
+ } else if (node is IfElement) {
+ _insertImplicitCallReference(node.thenElement);
+ _insertImplicitCallReference(node.elseElement);
+ } else if (node is ForElement) {
+ _insertImplicitCallReference(node.body);
+ }
+ // Nothing to do for [SpreadElement] as analyzer does not desugar this
+ // element.
+ }
+
+ void _insertImplicitCallReferences(TypedLiteral node) {
+ if (node is ListLiteral) {
+ for (var element in node.elements) {
+ _insertImplicitCallReference(element);
+ }
+ } else if (node is SetOrMapLiteral) {
+ for (var element in node.elements) {
+ _insertImplicitCallReference(element);
+ }
+ }
+ }
+
void _pushCollectionTypesDown(CollectionElement? element,
{DartType? elementType,
required DartType iterableType,
diff --git a/pkg/analyzer/lib/src/dart/resolver/variable_declaration_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/variable_declaration_resolver.dart
index 1e41759..8542adc 100644
--- a/pkg/analyzer/lib/src/dart/resolver/variable_declaration_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/variable_declaration_resolver.dart
@@ -71,6 +71,11 @@
element.constantInitializer = initializer;
}
+ var callReference = _resolver.insertImplicitCallReference(initializer);
+ if (callReference != initializer) {
+ initializer = callReference;
+ }
+
_resolver.checkForInvalidAssignment(node.name, initializer,
whyNotPromoted: whyNotPromoted);
}
diff --git a/pkg/analyzer/lib/src/error/literal_element_verifier.dart b/pkg/analyzer/lib/src/error/literal_element_verifier.dart
index b233a83..a6962f4 100644
--- a/pkg/analyzer/lib/src/error/literal_element_verifier.dart
+++ b/pkg/analyzer/lib/src/error/literal_element_verifier.dart
@@ -11,6 +11,7 @@
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type_system.dart';
import 'package:analyzer/src/error/codes.dart';
+import 'package:analyzer/src/generated/error_verifier.dart';
/// Verifier for [CollectionElement]s in list, set, or map literals.
class LiteralElementVerifier {
@@ -18,7 +19,7 @@
final TypeSystemImpl typeSystem;
final ErrorReporter errorReporter;
final FeatureSet featureSet;
- final bool Function(Expression) checkForUseOfVoidResult;
+ final ErrorVerifier _errorVerifier;
final bool forList;
final bool forSet;
@@ -32,7 +33,7 @@
this.typeProvider,
this.typeSystem,
this.errorReporter,
- this.checkForUseOfVoidResult, {
+ this._errorVerifier, {
this.forList = false,
this.forSet = false,
this.elementType,
@@ -67,7 +68,8 @@
void _verifyElement(CollectionElement? element) {
if (element is Expression) {
if (forList || forSet) {
- if (!elementType!.isVoid && checkForUseOfVoidResult(element)) {
+ if (!elementType!.isVoid &&
+ _errorVerifier.checkForUseOfVoidResult(element)) {
return;
}
_checkAssignableToElementType(element.typeOrThrow, element);
@@ -102,12 +104,14 @@
/// and [mapValueType].
void _verifyMapLiteralEntry(MapLiteralEntry entry) {
var mapKeyType = this.mapKeyType;
- if (!mapKeyType!.isVoid && checkForUseOfVoidResult(entry.key)) {
+ if (!mapKeyType!.isVoid &&
+ _errorVerifier.checkForUseOfVoidResult(entry.key)) {
return;
}
var mapValueType = this.mapValueType;
- if (!mapValueType!.isVoid && checkForUseOfVoidResult(entry.value)) {
+ if (!mapValueType!.isVoid &&
+ _errorVerifier.checkForUseOfVoidResult(entry.value)) {
return;
}
@@ -180,11 +184,39 @@
var errorCode = forList
? CompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE
: CompileTimeErrorCode.SET_ELEMENT_TYPE_NOT_ASSIGNABLE;
- errorReporter.reportErrorForNode(
- errorCode,
- expression,
- [iterableElementType, elementType],
- );
+ // Also check for an "implicit tear-off conversion" which would be applied
+ // after desugaring a spread element.
+ var implicitCallMethod = _errorVerifier.getImplicitCallMethod(
+ iterableElementType, elementType, expression);
+ if (implicitCallMethod == null) {
+ errorReporter.reportErrorForNode(
+ errorCode,
+ expression,
+ [iterableElementType, elementType],
+ );
+ } else {
+ var tearoffType = implicitCallMethod.type;
+ if (featureSet.isEnabled(Feature.constructor_tearoffs)) {
+ var typeArguments = typeSystem.inferFunctionTypeInstantiation(
+ elementType as FunctionType,
+ tearoffType,
+ errorReporter: errorReporter,
+ errorNode: expression,
+ genericMetadataIsEnabled: true,
+ )!;
+ if (typeArguments.isNotEmpty) {
+ tearoffType = tearoffType.instantiate(typeArguments);
+ }
+ }
+
+ if (!typeSystem.isAssignableTo(tearoffType, elementType)) {
+ errorReporter.reportErrorForNode(
+ errorCode,
+ expression,
+ [iterableElementType, elementType],
+ );
+ }
+ }
}
}
diff --git a/pkg/analyzer/lib/src/generated/element_resolver.dart b/pkg/analyzer/lib/src/generated/element_resolver.dart
index daa682e..af56535 100644
--- a/pkg/analyzer/lib/src/generated/element_resolver.dart
+++ b/pkg/analyzer/lib/src/generated/element_resolver.dart
@@ -499,7 +499,7 @@
}
@override
- void visitVariableDeclaration(VariableDeclaration node) {
+ void visitVariableDeclarationList(VariableDeclarationList node) {
_resolveAnnotations(node.metadata);
}
diff --git a/pkg/analyzer/lib/src/generated/error_detection_helpers.dart b/pkg/analyzer/lib/src/generated/error_detection_helpers.dart
index 7903c73..41ecb65 100644
--- a/pkg/analyzer/lib/src/generated/error_detection_helpers.dart
+++ b/pkg/analyzer/lib/src/generated/error_detection_helpers.dart
@@ -6,6 +6,7 @@
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/syntactic_entity.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/diagnostic/diagnostic.dart';
import 'package:analyzer/error/error.dart';
@@ -29,20 +30,17 @@
/// argument.
void checkForArgumentTypeNotAssignable(
Expression expression,
- DartType? expectedStaticType,
+ DartType expectedStaticType,
DartType actualStaticType,
ErrorCode errorCode,
{Map<DartType, NonPromotionReason> Function()? whyNotPromoted}) {
- // Warning case: test static type information
- if (expectedStaticType != null) {
- if (!expectedStaticType.isVoid && checkForUseOfVoidResult(expression)) {
- return;
- }
-
- _checkForAssignableExpressionAtType(
- expression, actualStaticType, expectedStaticType, errorCode,
- whyNotPromoted: whyNotPromoted);
+ if (!expectedStaticType.isVoid && checkForUseOfVoidResult(expression)) {
+ return;
}
+
+ _checkForAssignableExpressionAtType(
+ expression, actualStaticType, expectedStaticType, errorCode,
+ whyNotPromoted: whyNotPromoted);
}
/// Verify that the given [argument] can be assigned to its corresponding
@@ -55,7 +53,7 @@
void checkForArgumentTypeNotAssignableForArgument(Expression argument,
{bool promoteParameterToNullable = false,
Map<DartType, NonPromotionReason> Function()? whyNotPromoted}) {
- _checkForArgumentTypeNotAssignableForArgument2(
+ _checkForArgumentTypeNotAssignableForArgument(
argument: argument is NamedExpression ? argument.expression : argument,
parameter: argument.staticParameterElement,
promoteParameterToNullable: promoteParameterToNullable,
@@ -160,8 +158,8 @@
return;
}
- _checkForAssignableExpression(
- rhs, leftType, CompileTimeErrorCode.INVALID_ASSIGNMENT,
+ _checkForAssignableExpressionAtType(
+ rhs, rhs.typeOrThrow, leftType, CompileTimeErrorCode.INVALID_ASSIGNMENT,
whyNotPromoted: whyNotPromoted);
}
@@ -196,7 +194,7 @@
if (readElement is MethodElement) {
var parameters = readElement.parameters;
if (parameters.isNotEmpty) {
- _checkForArgumentTypeNotAssignableForArgument2(
+ _checkForArgumentTypeNotAssignableForArgument(
argument: index,
parameter: parameters[0],
promoteParameterToNullable: false,
@@ -208,7 +206,7 @@
if (writeElement is MethodElement) {
var parameters = writeElement.parameters;
if (parameters.isNotEmpty) {
- _checkForArgumentTypeNotAssignableForArgument2(
+ _checkForArgumentTypeNotAssignableForArgument(
argument: index,
parameter: parameters[0],
promoteParameterToNullable: false,
@@ -232,6 +230,27 @@
SyntacticEntity errorEntity,
Map<DartType, NonPromotionReason>? whyNotPromoted);
+ /// If an assignment from [type] to [context] is a case of an implicit 'call'
+ /// method, returns the element of the 'call' method.
+ ///
+ /// From the spec:
+ ///
+ /// > Let `e` be an expression whose static type is an interface type that has
+ /// > a method named `call`. In the case where the context type for `e`
+ /// > is a function type or the type `Function`, `e` is treated as `e.call`.
+ MethodElement? getImplicitCallMethod(
+ DartType type, DartType? context, SyntacticEntity errorNode) {
+ if (context != null &&
+ typeSystem.acceptsFunctionType(context) &&
+ type is InterfaceType &&
+ type.nullabilitySuffix != NullabilitySuffix.question) {
+ return type.lookUpMethod2(
+ FunctionElement.CALL_METHOD_NAME, type.element.library);
+ } else {
+ return null;
+ }
+ }
+
/// Return the variable element represented by the given [expression], or
/// `null` if there is no such element.
VariableElement? getVariableElement(Expression? expression) {
@@ -244,48 +263,25 @@
return null;
}
- void _checkForArgumentTypeNotAssignableForArgument2({
+ void _checkForArgumentTypeNotAssignableForArgument({
required Expression argument,
required ParameterElement? parameter,
required bool promoteParameterToNullable,
Map<DartType, NonPromotionReason> Function()? whyNotPromoted,
}) {
var staticParameterType = parameter?.type;
- if (promoteParameterToNullable && staticParameterType != null) {
- staticParameterType =
- typeSystem.makeNullable(staticParameterType as TypeImpl);
+ if (staticParameterType != null) {
+ if (promoteParameterToNullable) {
+ staticParameterType =
+ typeSystem.makeNullable(staticParameterType as TypeImpl);
+ }
+ checkForArgumentTypeNotAssignable(
+ argument,
+ staticParameterType,
+ argument.typeOrThrow,
+ CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE,
+ whyNotPromoted: whyNotPromoted);
}
- _checkForArgumentTypeNotAssignableWithExpectedTypes(
- argument,
- staticParameterType,
- CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE,
- whyNotPromoted);
- }
-
- /// Verify that the given [expression] can be assigned to its corresponding
- /// parameters.
- ///
- /// See [CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE],
- /// [CompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE],
- /// [CompileTimeErrorCode.MAP_KEY_TYPE_NOT_ASSIGNABLE], and
- /// [CompileTimeErrorCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE].
- void _checkForArgumentTypeNotAssignableWithExpectedTypes(
- Expression expression,
- DartType? expectedStaticType,
- ErrorCode errorCode,
- Map<DartType, NonPromotionReason> Function()? whyNotPromoted) {
- checkForArgumentTypeNotAssignable(
- expression, expectedStaticType, expression.typeOrThrow, errorCode,
- whyNotPromoted: whyNotPromoted);
- }
-
- bool _checkForAssignableExpression(
- Expression expression, DartType expectedStaticType, ErrorCode errorCode,
- {required Map<DartType, NonPromotionReason> Function()? whyNotPromoted}) {
- DartType actualStaticType = expression.typeOrThrow;
- return _checkForAssignableExpressionAtType(
- expression, actualStaticType, expectedStaticType, errorCode,
- whyNotPromoted: whyNotPromoted);
}
bool _checkForAssignableExpressionAtType(
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index af0cfcf..b53caa5 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -2190,9 +2190,6 @@
awaitKeyword = parent.awaitKeyword;
}
- // Use an explicit string instead of [loopType] to remove the "<E>".
- String loopNamedType = awaitKeyword != null ? "Stream" : "Iterable";
-
// The object being iterated has to implement Iterable<T> for some T that
// is assignable to the variable's type.
// TODO(rnystrom): Move this into mostSpecificTypeArgument()?
@@ -2207,6 +2204,8 @@
}
if (!typeSystem.isAssignableTo(iterableType, requiredSequenceType)) {
+ // Use an explicit string instead of [loopType] to remove the "<E>".
+ String loopNamedType = awaitKeyword != null ? 'Stream' : 'Iterable';
errorReporter.reportErrorForNode(
CompileTimeErrorCode.FOR_IN_OF_INVALID_TYPE,
node.iterable,
@@ -2231,11 +2230,48 @@
}
if (!typeSystem.isAssignableTo(sequenceElementType, variableType)) {
- errorReporter.reportErrorForNode(
- CompileTimeErrorCode.FOR_IN_OF_INVALID_ELEMENT_TYPE,
- node.iterable,
- [iterableType, loopNamedType, variableType],
- );
+ // Use an explicit string instead of [loopType] to remove the "<E>".
+ String loopNamedType = awaitKeyword != null ? 'Stream' : 'Iterable';
+
+ // A for-in loop is specified to desugar to a different set of statements
+ // which include an assignment of the sequence element's `iterator`'s
+ // `current` value, at which point "implicit tear-off conversion" may be
+ // performed. We do not perform this desugaring; instead we allow a
+ // special assignability here.
+ var implicitCallMethod = getImplicitCallMethod(
+ sequenceElementType, variableType, node.iterable);
+ if (implicitCallMethod == null) {
+ errorReporter.reportErrorForNode(
+ CompileTimeErrorCode.FOR_IN_OF_INVALID_ELEMENT_TYPE,
+ node.iterable,
+ [iterableType, loopNamedType, variableType],
+ );
+ } else {
+ var tearoffType = implicitCallMethod.type;
+ // An implicit tear-off conversion does occur on the values of the
+ // iterator, but this does not guarantee their assignability.
+
+ if (_featureSet?.isEnabled(Feature.constructor_tearoffs) ?? true) {
+ var typeArguments = typeSystem.inferFunctionTypeInstantiation(
+ variableType as FunctionType,
+ tearoffType,
+ errorReporter: errorReporter,
+ errorNode: node.iterable,
+ genericMetadataIsEnabled: true,
+ )!;
+ if (typeArguments.isNotEmpty) {
+ tearoffType = tearoffType.instantiate(typeArguments);
+ }
+ }
+
+ if (!typeSystem.isAssignableTo(tearoffType, variableType)) {
+ errorReporter.reportErrorForNode(
+ CompileTimeErrorCode.FOR_IN_OF_INVALID_ELEMENT_TYPE,
+ node.iterable,
+ [iterableType, loopNamedType, variableType],
+ );
+ }
+ }
}
return true;
@@ -2674,8 +2710,10 @@
void _checkForIntNotAssignable(Expression argument) {
var staticParameterElement = argument.staticParameterElement;
var staticParameterType = staticParameterElement?.type;
- checkForArgumentTypeNotAssignable(argument, staticParameterType, _intType,
- CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE);
+ if (staticParameterType != null) {
+ checkForArgumentTypeNotAssignable(argument, staticParameterType, _intType,
+ CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE);
+ }
}
/// Verify that the given [annotation] isn't defined in a deferred library.
@@ -2863,7 +2901,7 @@
_typeProvider,
typeSystem,
errorReporter,
- checkForUseOfVoidResult,
+ this,
forList: true,
elementType: listElementType,
featureSet: _featureSet!,
@@ -2951,7 +2989,7 @@
_typeProvider,
typeSystem,
errorReporter,
- checkForUseOfVoidResult,
+ this,
forMap: true,
mapKeyType: keyType,
mapValueType: valueType,
@@ -3911,7 +3949,7 @@
_typeProvider,
typeSystem,
errorReporter,
- checkForUseOfVoidResult,
+ this,
forSet: true,
elementType: setElementType,
featureSet: _featureSet!,
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index 2c44a64..202769a 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -7,6 +7,7 @@
import 'package:_fe_analyzer_shared/src/flow_analysis/flow_analysis.dart';
import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/standard_ast_factory.dart';
import 'package:analyzer/dart/ast/syntactic_entity.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/element.dart';
@@ -19,6 +20,7 @@
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/ast/extensions.dart';
+import 'package:analyzer/src/dart/ast/utilities.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
import 'package:analyzer/src/dart/element/member.dart' show Member;
@@ -454,6 +456,13 @@
/// See [CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE].
void checkForArgumentTypesNotAssignableInList(ArgumentList argumentList,
List<WhyNotPromotedGetter> whyNotPromotedList) {
+ for (var argument in argumentList.arguments) {
+ if (argument is NamedExpression) {
+ insertImplicitCallReference(argument.expression);
+ } else {
+ insertImplicitCallReference(argument);
+ }
+ }
var arguments = argumentList.arguments;
for (int i = 0; i < arguments.length; i++) {
checkForArgumentTypeNotAssignableForArgument(arguments[i],
@@ -619,6 +628,69 @@
return null;
}
+ /// If `expression` should be treated as `expression.call`, inserts an
+ /// [ImplicitCallReferece] node which wraps [expression].
+ ///
+ /// If an [ImplicitCallReferece] is inserted, returns it; otherwise, returns
+ /// [expression].
+ ExpressionImpl insertImplicitCallReference(Expression expression) {
+ expression as ExpressionImpl;
+ if (!isConstructorTearoffsEnabled) {
+ // Temporarily, only create [ImplicitCallReference] nodes under the
+ // 'constructor-tearoffs' feature.
+ // TODO(srawlins): When we are ready to make a breaking change release to
+ // the analyzer package, remove this exception.
+ return expression;
+ }
+
+ var parent = expression.parent;
+ if (parent is CascadeExpression && parent.target == expression) {
+ // Do not perform an "implicit tear-off conversion" here. It should only
+ // be performed on [parent]. See
+ // https://github.com/dart-lang/language/issues/1873.
+ return expression;
+ }
+ var context = InferenceContext.getContext(expression);
+ var callMethod =
+ getImplicitCallMethod(expression.typeOrThrow, context, expression);
+ if (callMethod == null || context == null) {
+ return expression;
+ }
+
+ // `expression` is to be treated as `expression.call`.
+ context = typeSystem.flatten(context);
+ var callMethodType = callMethod.type;
+ List<DartType> typeArgumentTypes;
+ if (isConstructorTearoffsEnabled &&
+ callMethodType.typeFormals.isNotEmpty &&
+ context is FunctionType) {
+ typeArgumentTypes = typeSystem.inferFunctionTypeInstantiation(
+ context,
+ callMethodType,
+ errorNode: expression,
+ // If the constructor-tearoffs feature is enabled, then so is
+ // generic-metadata.
+ genericMetadataIsEnabled: true,
+ )!;
+ if (typeArgumentTypes.isNotEmpty) {
+ callMethodType = callMethodType.instantiate(typeArgumentTypes);
+ }
+ } else {
+ typeArgumentTypes = [];
+ }
+ var callReference = astFactory.implicitCallReference(
+ expression: expression,
+ staticElement: callMethod,
+ typeArguments: null,
+ typeArgumentTypes: typeArgumentTypes,
+ ) as ImplicitCallReferenceImpl;
+ NodeReplacer.replace(expression, callReference, parent: parent);
+
+ callReference.staticType = callMethodType;
+
+ return callReference;
+ }
+
/// If we reached a null-shorting termination, and the [node] has null
/// shorting, make the type of the [node] nullable.
void nullShortingTermination(ExpressionImpl node,
@@ -1224,13 +1296,22 @@
// to be visited in the context of the constructor field initializer node.
//
var fieldElement = enclosingClass!.getField(node.fieldName.name);
- InferenceContext.setType(node.expression, fieldElement?.type);
- node.expression.accept(this);
- var whyNotPromoted = flowAnalysis.flow?.whyNotPromoted(node.expression);
+ var fieldType = fieldElement?.type;
+ var expression = node.expression;
+ InferenceContext.setType(expression, fieldType);
+ expression.accept(this);
+ var whyNotPromoted = flowAnalysis.flow?.whyNotPromoted(expression);
node.accept(elementResolver);
node.accept(typeAnalyzer);
- var enclosingConstructor = enclosingFunction as ConstructorElement;
if (fieldElement != null) {
+ if (fieldType != null && expression.staticType != null) {
+ var callReference = insertImplicitCallReference(expression);
+ if (expression != callReference) {
+ checkForInvalidAssignment(node.fieldName, callReference,
+ whyNotPromoted: whyNotPromoted);
+ }
+ }
+ var enclosingConstructor = enclosingFunction as ConstructorElement;
checkForFieldInitializerNotAssignable(node, fieldElement,
isConstConstructor: enclosingConstructor.isConst,
whyNotPromoted: whyNotPromoted);
@@ -1337,6 +1418,7 @@
_thisAccessTracker.enterFunctionBody(node);
super.visitExpressionFunctionBody(node);
+ insertImplicitCallReference(node.expression);
flowAnalysis.flow?.handleExit();
@@ -1850,6 +1932,11 @@
inferenceContext.bodyContext?.addReturnExpression(node.expression);
flowAnalysis.flow?.handleExit();
+
+ var expression = node.expression;
+ if (expression != null) {
+ insertImplicitCallReference(expression);
+ }
}
@override
diff --git a/pkg/analyzer/lib/src/test_utilities/find_node.dart b/pkg/analyzer/lib/src/test_utilities/find_node.dart
index f07b84c..21b51e7 100644
--- a/pkg/analyzer/lib/src/test_utilities/find_node.dart
+++ b/pkg/analyzer/lib/src/test_utilities/find_node.dart
@@ -203,6 +203,10 @@
return _node(search, (n) => n is IfStatement);
}
+ ImplicitCallReference implicitCallReference(String search) {
+ return _node(search, (n) => n is ImplicitCallReference);
+ }
+
ImportDirective import(String search) {
return _node(search, (n) => n is ImportDirective);
}
diff --git a/pkg/analyzer/test/src/dart/resolution/ast_rewrite_test.dart b/pkg/analyzer/test/src/dart/resolution/ast_rewrite_test.dart
index 800bc3e..e300c65 100644
--- a/pkg/analyzer/test/src/dart/resolution/ast_rewrite_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/ast_rewrite_test.dart
@@ -13,6 +13,7 @@
main() {
defineReflectiveSuite(() {
+ defineReflectiveTests(AstRewriteImplicitCallReferenceTest);
defineReflectiveTests(AstRewriteMethodInvocationTest);
defineReflectiveTests(AstRewritePrefixedIdentifierTest);
@@ -26,6 +27,254 @@
}
@reflectiveTest
+class AstRewriteImplicitCallReferenceTest extends PubPackageResolutionTest {
+ test_assignment_indexExpression() async {
+ await assertNoErrorsInCode('''
+abstract class C {
+ void call(int t) => t;
+}
+
+void Function(int) foo(C c) {
+ var map = <int, C>{};
+ return map[1] = c;
+}
+''');
+
+ assertImplicitCallReference(
+ findNode.implicitCallReference('map[1] = c'),
+ findElement.method('call'),
+ 'void Function(int)',
+ );
+ }
+
+ test_explicitTypeArguments() async {
+ await assertNoErrorsInCode('''
+class C {
+ T call<T>(T t) => t;
+}
+
+void foo() {
+ var c = C();
+ c<int>;
+}
+''');
+
+ assertImplicitCallReference(
+ findNode.implicitCallReference('c<int>'),
+ findElement.method('call'),
+ 'int Function(int)',
+ );
+ }
+
+ test_ifNull() async {
+ await assertNoErrorsInCode('''
+abstract class C {
+ void call(int t) => t;
+}
+
+void Function(int) foo(C? c1, C c2) {
+ return c1 ?? c2;
+}
+''');
+
+ assertImplicitCallReference(
+ findNode.implicitCallReference('c1 ?? c2'),
+ findElement.method('call'),
+ 'void Function(int)',
+ );
+ }
+
+ test_listLiteral_element() async {
+ await assertNoErrorsInCode('''
+abstract class C {
+ void call(int t) => t;
+}
+
+List<void Function(int)> foo(C c) {
+ return [c];
+}
+''');
+
+ assertImplicitCallReference(
+ findNode.implicitCallReference('c]'),
+ findElement.method('call'),
+ 'void Function(int)',
+ );
+ }
+
+ test_listLiteral_forElement() async {
+ await assertNoErrorsInCode('''
+abstract class C {
+ void call(int t) => t;
+}
+
+List<void Function(int)> foo(C c) {
+ return [
+ for (var _ in [1, 2, 3]) c,
+ ];
+}
+''');
+
+ assertImplicitCallReference(
+ findNode.implicitCallReference('c,'),
+ findElement.method('call'),
+ 'void Function(int)',
+ );
+ }
+
+ test_listLiteral_ifElement() async {
+ await assertNoErrorsInCode('''
+abstract class C {
+ void call(int t) => t;
+}
+
+List<void Function(int)> foo(C c) {
+ return [
+ if (1==2) c,
+ ];
+}
+''');
+
+ assertImplicitCallReference(
+ findNode.implicitCallReference('c,'),
+ findElement.method('call'),
+ 'void Function(int)',
+ );
+ }
+
+ test_listLiteral_ifElement_else() async {
+ await assertNoErrorsInCode('''
+abstract class C {
+ void call(int t) => t;
+}
+
+List<void Function(int)> foo(C c1, C c2) {
+ return [
+ if (1==2) c1
+ else c2,
+ ];
+}
+''');
+
+ assertImplicitCallReference(
+ findNode.implicitCallReference('c2,'),
+ findElement.method('call'),
+ 'void Function(int)',
+ );
+ }
+
+ test_prefixedIdentifier() async {
+ await assertNoErrorsInCode('''
+abstract class C {
+ C get c;
+ void call(int t) => t;
+}
+
+void Function(int) foo(C c) {
+ return c.c;
+}
+''');
+
+ assertImplicitCallReference(
+ findNode.implicitCallReference('c.c;'),
+ findElement.method('call'),
+ 'void Function(int)',
+ );
+ }
+
+ test_propertyAccess() async {
+ await assertNoErrorsInCode('''
+abstract class C {
+ C get c;
+ void call(int t) => t;
+}
+
+void Function(int) foo(C c) {
+ return c.c.c;
+}
+''');
+
+ assertImplicitCallReference(
+ findNode.implicitCallReference('c.c.c;'),
+ findElement.method('call'),
+ 'void Function(int)',
+ );
+ }
+
+ test_setOrMapLiteral_element() async {
+ await assertNoErrorsInCode('''
+abstract class C {
+ void call(int t) => t;
+}
+
+Set<void Function(int)> foo(C c) {
+ return {c};
+}
+''');
+
+ assertImplicitCallReference(
+ findNode.implicitCallReference('c}'),
+ findElement.method('call'),
+ 'void Function(int)',
+ );
+ }
+
+ test_setOrMapLiteral_mapLiteralEntry_key() async {
+ await assertNoErrorsInCode('''
+abstract class C {
+ void call(int t) => t;
+}
+
+Map<void Function(int), int> foo(C c) {
+ return {c: 1};
+}
+''');
+
+ assertImplicitCallReference(
+ findNode.implicitCallReference('c:'),
+ findElement.method('call'),
+ 'void Function(int)',
+ );
+ }
+
+ test_setOrMapLiteral_mapLiteralEntry_value() async {
+ await assertNoErrorsInCode('''
+abstract class C {
+ void call(int t) => t;
+}
+
+Map<int, void Function(int)> foo(C c) {
+ return {1: c};
+}
+''');
+
+ assertImplicitCallReference(
+ findNode.implicitCallReference('c}'),
+ findElement.method('call'),
+ 'void Function(int)',
+ );
+ }
+
+ test_simpleIdentifier() async {
+ await assertNoErrorsInCode('''
+abstract class C {
+ void call(int t) => t;
+}
+
+void Function(int) foo(C c) {
+ return c;
+}
+''');
+
+ assertImplicitCallReference(
+ findNode.implicitCallReference('c;'),
+ findElement.method('call'),
+ 'void Function(int)',
+ );
+ }
+}
+
+@reflectiveTest
class AstRewriteMethodInvocationTest extends PubPackageResolutionTest
with AstRewriteMethodInvocationTestCases {}
diff --git a/pkg/analyzer/test/src/dart/resolution/function_reference_test.dart b/pkg/analyzer/test/src/dart/resolution/function_reference_test.dart
index 1a2adbb..9a56b92 100644
--- a/pkg/analyzer/test/src/dart/resolution/function_reference_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/function_reference_test.dart
@@ -466,12 +466,49 @@
}
''');
- // TODO(srawlins): An arbitrary expression with a static type which is
- // callable does not necessarily have an element. However, if we implement
- // some "implicit call tearoff" node, it would have an element referring to
- // the `call` method.
- var functionReference = findNode.functionReference('C()<int>;');
- assertType(functionReference, 'int Function(int)');
+ assertImplicitCallReference(findNode.implicitCallReference('C()<int>;'),
+ findElement.method('call'), 'int Function(int)');
+ }
+
+ test_implicitCallTearoff_extensionOnNullable() async {
+ await assertNoErrorsInCode('''
+Object? v = null;
+extension E on Object? {
+ void call<R, S>(R r, S s) {}
+}
+void foo() {
+ v<int, String>;
+}
+
+''');
+
+ assertImplicitCallReference(
+ findNode.implicitCallReference('v<int, String>;'),
+ findElement.method('call'),
+ 'void Function(int, String)');
+ }
+
+ test_implicitCallTearoff_prefixed() async {
+ newFile('$testPackageLibPath/a.dart', content: '''
+class C {
+ T call<T>(T t) => t;
+}
+C c = C();
+''');
+ await assertNoErrorsInCode('''
+import 'a.dart' as prefix;
+
+bar() {
+ prefix.c<int>;
+}
+''');
+
+ assertImportPrefix(
+ findNode.simple('prefix.'), findElement.prefix('prefix'));
+ assertImplicitCallReference(
+ findNode.implicitCallReference('c<int>;'),
+ findElement.importFind('package:test/a.dart').method('call'),
+ 'int Function(int)');
}
test_implicitCallTearoff_tooFewTypeArguments() async {
@@ -485,18 +522,11 @@
}
''', [
error(
- CompileTimeErrorCode
- .WRONG_NUMBER_OF_TYPE_ARGUMENTS_ANONYMOUS_FUNCTION,
- 57,
- 5),
+ CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_FUNCTION, 57, 5),
]);
- // TODO(srawlins): An arbitrary expression with a static type which is
- // callable does not necessarily have an element. However, if we implement
- // some "implicit call tearoff" node, it would have an element referring to
- // the `call` method.
- var functionReference = findNode.functionReference('C()<int>;');
- assertType(functionReference, 'void Function(dynamic, dynamic)');
+ assertImplicitCallReference(findNode.implicitCallReference('C()<int>;'),
+ findElement.method('call'), 'void Function(dynamic, dynamic)');
}
test_implicitCallTearoff_tooManyTypeArguments() async {
@@ -510,18 +540,11 @@
}
''', [
error(
- CompileTimeErrorCode
- .WRONG_NUMBER_OF_TYPE_ARGUMENTS_ANONYMOUS_FUNCTION,
- 50,
- 5),
+ CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_FUNCTION, 50, 5),
]);
- // TODO(srawlins): An arbitrary expression with a static type which is
- // callable does not necessarily have an element. However, if we implement
- // some "implicit call tearoff" node, it would have an element referring to
- // the `call` method.
- var functionReference = findNode.functionReference('C()<int>;');
- assertType(functionReference, 'int Function(int)');
+ assertImplicitCallReference(findNode.implicitCallReference('C()<int>;'),
+ findElement.method('call'), 'int Function(int)');
}
test_instanceGetter_explicitReceiver() async {
@@ -1348,6 +1371,26 @@
findElement.topFunction('foo'), 'void Function(int)');
}
+ test_topLevelVariable_prefix() async {
+ newFile('$testPackageLibPath/a.dart', content: '''
+void Function<T>(T) foo = <T>(T arg) {}
+''');
+ await assertNoErrorsInCode('''
+import 'a.dart' as prefix;
+
+bar() {
+ prefix.foo<int>;
+}
+''');
+
+ assertImportPrefix(
+ findNode.simple('prefix.'), findElement.prefix('prefix'));
+ assertFunctionReference(
+ findNode.functionReference('foo<int>;'),
+ findElement.importFind('package:test/a.dart').topGet('foo'),
+ 'void Function(int)');
+ }
+
test_topLevelVariable_prefix_unknownIdentifier() async {
newFile('$testPackageLibPath/a.dart', content: '');
await assertErrorsInCode('''
diff --git a/pkg/analyzer/test/src/dart/resolution/metadata_test.dart b/pkg/analyzer/test/src/dart/resolution/metadata_test.dart
index 4dca4cb..7c96192 100644
--- a/pkg/analyzer/test/src/dart/resolution/metadata_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/metadata_test.dart
@@ -151,6 +151,46 @@
''');
}
+ test_onLocalVariable() async {
+ await assertNoErrorsInCode(r'''
+class A {
+ final int a;
+ const A(this.a);
+}
+
+void f() {
+ @A(3)
+ int? x;
+ print(x);
+}
+''');
+
+ var annotation = findNode.annotation('@A');
+ _assertResolvedNodeText(annotation, '''
+Annotation
+ arguments: ArgumentList
+ arguments
+ IntegerLiteral
+ literal: 3
+ staticType: int
+ leftParenthesis: (
+ rightParenthesis: )
+ atSign: @
+ element: self::@class::A::@constructor::•
+ name: SimpleIdentifier
+ staticElement: self::@class::A
+ staticType: null
+ token: A
+''');
+
+ final localVariable = findElement.localVar('x');
+ final annotationOnElement = localVariable.metadata.single;
+ _assertElementAnnotationValueText(annotationOnElement, '''
+A
+ a: int 3
+''');
+ }
+
test_optIn_fromOptOut_class() async {
newFile('$testPackageLibPath/a.dart', content: r'''
class A {
diff --git a/pkg/analyzer/test/src/dart/resolution/resolution.dart b/pkg/analyzer/test/src/dart/resolution/resolution.dart
index 5290a73..6d89730 100644
--- a/pkg/analyzer/test/src/dart/resolution/resolution.dart
+++ b/pkg/analyzer/test/src/dart/resolution/resolution.dart
@@ -384,6 +384,12 @@
assertType(ref, type);
}
+ void assertImplicitCallReference(ImplicitCallReference node,
+ Element? expectedElement, String expectedType) {
+ assertElement(node, expectedElement);
+ assertType(node, expectedType);
+ }
+
/// In valid code [element] must be a [PrefixElement], but for invalid code
/// like `int.double v;` we want to resolve `int` somehow. Still not type.
void assertImportPrefix(Expression? identifier, Element? element) {
@@ -902,6 +908,8 @@
}
} else if (node is Identifier) {
return node.staticElement;
+ } else if (node is ImplicitCallReference) {
+ return node.staticElement;
} else if (node is IndexExpression) {
return node.staticElement;
} else if (node is InstanceCreationExpression) {
diff --git a/pkg/analyzer/test/src/diagnostics/argument_type_not_assignable_test.dart b/pkg/analyzer/test/src/diagnostics/argument_type_not_assignable_test.dart
index f238eba..60ca6a0 100644
--- a/pkg/analyzer/test/src/diagnostics/argument_type_not_assignable_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/argument_type_not_assignable_test.dart
@@ -147,6 +147,18 @@
]);
}
+ test_implicitCallReference_namedAndRequired() async {
+ await assertNoErrorsInCode('''
+class A {
+ void call(int p) {}
+}
+void f({required void Function(int) a}) {}
+void g(A a) {
+ f(a: a);
+}
+''');
+ }
+
test_invocation_functionTypes_optional() async {
await assertErrorsInCode('''
void acceptFunOptBool(void funNumOptBool([bool b])) {}
@@ -199,8 +211,8 @@
const A.fromInt(int p);
}
@A.fromInt('0')
-main() {
-}''', [
+main() {}
+''', [
error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 49, 3),
]);
}
@@ -330,6 +342,44 @@
]);
}
+ test_implicitCallReference() async {
+ await assertNoErrorsInCode('''
+class A {
+ void call(int p) {}
+}
+void f(void Function(int) a) {}
+void g(A a) {
+ f(a);
+}
+''');
+ }
+
+ test_implicitCallReference_named() async {
+ await assertNoErrorsInCode('''
+class A {
+ void call(int p) {}
+}
+void defaultFunc(int p) {}
+void f({void Function(int) a = defaultFunc}) {}
+void g(A a) {
+ f(a: a);
+}
+''');
+ }
+
+ test_implicitCallReference_this() async {
+ await assertNoErrorsInCode('''
+class A {
+ void call(int p) {}
+
+ void f(void Function(int) a) {}
+ void g() {
+ f(this);
+ }
+}
+''');
+ }
+
test_index_invalidRead() async {
await assertErrorsInCode('''
class A {
diff --git a/pkg/analyzer/test/src/diagnostics/field_initializer_not_assignable_test.dart b/pkg/analyzer/test/src/diagnostics/field_initializer_not_assignable_test.dart
index a86aaa2..07abbcd 100644
--- a/pkg/analyzer/test/src/diagnostics/field_initializer_not_assignable_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/field_initializer_not_assignable_test.dart
@@ -16,6 +16,30 @@
@reflectiveTest
class FieldInitializerNotAssignableTest extends PubPackageResolutionTest {
+ test_implicitCallReference() async {
+ await assertNoErrorsInCode('''
+class C {
+ void call(int p) {}
+}
+class A {
+ void Function(int) x;
+ A() : x = C();
+}
+''');
+ }
+
+ test_implicitCallReference_genericFunctionInstantiation() async {
+ await assertNoErrorsInCode('''
+class C {
+ void call<T>(T p) {}
+}
+class A {
+ void Function(int) x;
+ A() : x = C();
+}
+''');
+ }
+
test_unrelated() async {
await assertErrorsInCode('''
class A {
diff --git a/pkg/analyzer/test/src/diagnostics/for_in_of_invalid_element_type_test.dart b/pkg/analyzer/test/src/diagnostics/for_in_of_invalid_element_type_test.dart
index 07478d0..25599a7 100644
--- a/pkg/analyzer/test/src/diagnostics/for_in_of_invalid_element_type_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/for_in_of_invalid_element_type_test.dart
@@ -74,6 +74,32 @@
''');
}
+ test_declaredVariable_implicitCallReference() async {
+ await assertNoErrorsInCode('''
+class C {
+ void call() {}
+}
+void foo(C c) {
+ for (void Function() f in [c]) {
+ f;
+ }
+}
+''');
+ }
+
+ test_declaredVariable_implicitCallReference_genericFunctionInstantiation() async {
+ await assertNoErrorsInCode('''
+class C {
+ void call<T>(T p) {}
+}
+void foo(C c) {
+ for (void Function(int) f in [c]) {
+ f;
+ }
+}
+''');
+ }
+
test_declaredVariable_interfaceTypeTypedef_ok() async {
await assertNoErrorsInCode('''
typedef S = String;
@@ -119,4 +145,48 @@
error(CompileTimeErrorCode.FOR_IN_OF_INVALID_ELEMENT_TYPE, 27, 10),
]);
}
+
+ test_implicitCallReference() async {
+ await assertNoErrorsInCode('''
+class C {
+ void call(int a) {}
+}
+void foo(Iterable<C> iterable) {
+ void Function(int) f;
+ for (f in iterable) {
+ f;
+ }
+}
+''');
+ }
+
+ test_implicitCallReference_genericFunctionInstantiation() async {
+ await assertNoErrorsInCode('''
+class C {
+ void call<T>(T p) {}
+}
+void foo(Iterable<C> iterable) {
+ void Function(int) f;
+ for (f in iterable) {
+ f;
+ }
+}
+''');
+ }
+
+ test_implicitCallReference_unassignableFunctionType() async {
+ await assertErrorsInCode('''
+class C {
+ void call(int a) {}
+}
+void foo(Iterable<C> iterable) {
+ void Function(String) f;
+ for (f in iterable) {
+ f;
+ }
+}
+''', [
+ error(CompileTimeErrorCode.FOR_IN_OF_INVALID_ELEMENT_TYPE, 106, 8),
+ ]);
+ }
}
diff --git a/pkg/analyzer/test/src/diagnostics/invalid_assignment_test.dart b/pkg/analyzer/test/src/diagnostics/invalid_assignment_test.dart
index 63aacca..2de6c7e 100644
--- a/pkg/analyzer/test/src/diagnostics/invalid_assignment_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/invalid_assignment_test.dart
@@ -9,6 +9,7 @@
main() {
defineReflectiveSuite(() {
+ defineReflectiveTests(InvalidAssignment_ImplicitCallReferenceTest);
defineReflectiveTests(InvalidAssignmentTest);
defineReflectiveTests(InvalidAssignmentWithNoImplicitCastsTest);
defineReflectiveTests(InvalidAssignmentWithoutNullSafetyTest);
@@ -16,6 +17,304 @@
}
@reflectiveTest
+class InvalidAssignment_ImplicitCallReferenceTest
+ extends PubPackageResolutionTest {
+ test_invalid_genericBoundedCall_nonGenericContext() async {
+ await assertNoErrorsInCode('''
+class C {
+ T call<T extends num>(T t) => t;
+}
+
+String Function(String) f = C();
+''');
+ }
+
+ test_invalid_genericCall() async {
+ await assertErrorsInCode('''
+class C {
+ T call<T>(T t) => t;
+}
+
+void Function() f = C();
+''', [
+ error(CompileTimeErrorCode.INVALID_ASSIGNMENT, 56, 3),
+ ]);
+ }
+
+ test_invalid_genericCall_genericEnclosingClass_nonGenericContext() async {
+ await assertErrorsInCode('''
+class C<T> {
+ C(T a);
+ void call<U>(T t, U u) {}
+}
+
+void Function(bool, String) f = C(7);
+''', [
+ // The type arguments of the instance of `C` should be accurate and be
+ // taken into account when evaluating the assignment of the implicit call
+ // reference.
+ error(CompileTimeErrorCode.INVALID_ASSIGNMENT, 86, 4),
+ ]);
+ }
+
+ test_invalid_genericCall_nonGenericContext_withoutConstructorTearoffs() async {
+ await assertErrorsInCode('''
+// @dart=2.12
+class C {
+ T call<T>(T t) => t;
+}
+
+int Function(int) f = C();
+''', [
+ error(CompileTimeErrorCode.INVALID_ASSIGNMENT, 72, 3),
+ ]);
+ }
+
+ test_invalid_noCall_functionContext() async {
+ await assertErrorsInCode('''
+class C {}
+
+Function f = C();
+''', [
+ error(CompileTimeErrorCode.INVALID_ASSIGNMENT, 25, 3),
+ ]);
+ }
+
+ test_invalid_noCall_functionTypeContext() async {
+ await assertErrorsInCode('''
+class C {}
+
+String Function(String) f = C();
+''', [
+ error(CompileTimeErrorCode.INVALID_ASSIGNMENT, 40, 3),
+ ]);
+ }
+
+ test_invalid_nonGenericCall() async {
+ await assertErrorsInCode('''
+class C {
+ void call(int a) {}
+}
+
+void Function(String) f = C();
+''', [
+ error(CompileTimeErrorCode.INVALID_ASSIGNMENT, 61, 3),
+ ]);
+ }
+
+ test_invalid_nonGenericCall_typeVariableExtendsFunctionContext() async {
+ await assertErrorsInCode('''
+class C {
+ void call(int a) {}
+}
+class D<U extends Function> {
+ U f = C();
+}
+''', [
+ error(CompileTimeErrorCode.INVALID_ASSIGNMENT, 72, 3),
+ ]);
+ }
+
+ test_invalid_nonGenericCall_typeVariableExtendsFunctionTypeContext() async {
+ await assertErrorsInCode('''
+class C {
+ void call(int a) {}
+}
+class D<U extends void Function(int)> {
+ U f = C();
+}
+''', [
+ error(CompileTimeErrorCode.INVALID_ASSIGNMENT, 82, 3),
+ ]);
+ }
+
+ test_valid_genericBoundedCall_nonGenericContext() async {
+ await assertNoErrorsInCode('''
+class C {
+ T call<T extends num>(T t) => t;
+}
+
+int Function(int) f = C();
+''');
+ }
+
+ test_valid_genericCall_functionContext() async {
+ await assertNoErrorsInCode('''
+class C {
+ T call<T>(T t) => t;
+}
+
+Function f = C();
+''');
+ }
+
+ test_valid_genericCall_futureOrFunctionContext() async {
+ await assertNoErrorsInCode('''
+import 'dart:async';
+class C {
+ T call<T>(T t) => t;
+}
+
+FutureOr<Function> f = C();
+''');
+ }
+
+ test_valid_genericCall_futureOrFunctionTypeContext_generic() async {
+ await assertNoErrorsInCode('''
+import 'dart:async';
+class C {
+ T call<T>(T t) => t;
+}
+
+FutureOr<T Function<T>(T)> f = C();
+''');
+ }
+
+ test_valid_genericCall_futureOrFunctionTypeContext_nonGeneric() async {
+ await assertNoErrorsInCode('''
+import 'dart:async';
+class C {
+ T call<T>(T t) => t;
+}
+
+FutureOr<int Function(int)> f = C();
+''');
+ }
+
+ test_valid_genericCall_genericContext() async {
+ await assertNoErrorsInCode('''
+class C {
+ T call<T>(T t) => t;
+}
+
+T Function<T>(T) f = C();
+''');
+ }
+
+ test_valid_genericCall_genericEnclosingClass_nonGenericContext() async {
+ await assertNoErrorsInCode('''
+class C<T> {
+ C(T a);
+ void call<U>(T t, U u) {}
+}
+
+void Function(int, String) f = C(7);
+''');
+ }
+
+ test_valid_genericCall_genericTypedefContext() async {
+ await assertNoErrorsInCode('''
+class C {
+ T call<T>(T t) => t;
+}
+typedef Fn<T> = T Function(T);
+class D<U> {
+ Fn<U> f = C();
+}
+
+''');
+ }
+
+ test_valid_genericCall_nonGenericContext() async {
+ await assertNoErrorsInCode('''
+class C {
+ T call<T>(T t) => t;
+}
+
+int Function(int) f = C();
+''');
+ }
+
+ test_valid_genericCall_nullableFunctionContext() async {
+ await assertNoErrorsInCode('''
+class C {
+ T call<T>(T t) => t;
+}
+
+Function? f = C();
+''');
+ }
+
+ test_valid_genericCall_nullableNonGenericContext() async {
+ await assertNoErrorsInCode('''
+class C {
+ T call<T>(T t) => t;
+}
+
+int Function(int)? f = C();
+''');
+ }
+
+ test_valid_genericCall_typedefOfGenericContext() async {
+ await assertNoErrorsInCode('''
+class C {
+ T call<T>(T t) => t;
+}
+
+typedef Fn = T Function<T>(T);
+
+Fn f = C();
+''');
+ }
+
+ test_valid_nonGenericCall() async {
+ await assertNoErrorsInCode('''
+class C {
+ void call(int a) {}
+}
+
+void Function(int) f = C();
+''');
+ }
+
+ test_valid_nonGenericCall_declaredOnMixin() async {
+ await assertNoErrorsInCode('''
+mixin M {
+ void call(int a) {}
+}
+class C with M {}
+
+Function f = C();
+''');
+ }
+
+ test_valid_nonGenericCall_inCascade() async {
+ await assertNoErrorsInCode('''
+class C {
+ void call(int a) {}
+}
+class D {
+ late void Function(int) f;
+}
+
+void foo() {
+ D()..f = C();
+}
+''');
+ }
+
+ test_valid_nonGenericCall_subTypeViaParameter() async {
+ await assertNoErrorsInCode('''
+class C {
+ void call(num a) {}
+}
+
+void Function(int) f = C();
+''');
+ }
+
+ test_valid_nonGenericCall_subTypeViaReturnType() async {
+ await assertNoErrorsInCode('''
+class C {
+ int call() => 7;
+}
+
+num Function() f = C();
+''');
+ }
+}
+
+@reflectiveTest
class InvalidAssignmentTest extends PubPackageResolutionTest
with InvalidAssignmentTestCases {
test_constructorTearoff_inferredTypeArgs() async {
diff --git a/pkg/vm/lib/transformations/ffi/native.dart b/pkg/vm/lib/transformations/ffi/native.dart
index dd28967..2919bf1 100644
--- a/pkg/vm/lib/transformations/ffi/native.dart
+++ b/pkg/vm/lib/transformations/ffi/native.dart
@@ -128,7 +128,7 @@
FunctionType _wrapFunctionType(
FunctionType dartFunctionType, FunctionType ffiFunctionType) {
return FunctionType(
- [
+ <DartType>[
for (var i = 0; i < dartFunctionType.positionalParameters.length; i++)
_wrapArgumentType(dartFunctionType.positionalParameters[i],
ffiFunctionType.positionalParameters[i]),
@@ -164,7 +164,7 @@
final resolverInvocation = FunctionInvocation(
FunctionAccessKind.FunctionType,
StaticGet(resolverField),
- Arguments([
+ Arguments(<Expression>[
ConstantExpression(
StringConstant(currentLibrary.importUri.toString())),
ConstantExpression(nativeFunctionName),
@@ -176,11 +176,11 @@
// _fromAddress<NativeFunction<Double Function(Double)>>(...)
final fromAddressInvocation = StaticInvocation(
fromAddressInternal,
- Arguments([
+ Arguments(<Expression>[
resolverInvocation
], types: [
- InterfaceType(
- nativeFunctionClass, Nullability.legacy, [ffiFunctionType])
+ InterfaceType(nativeFunctionClass, Nullability.legacy,
+ <DartType>[ffiFunctionType])
]))
..fileOffset = fileOffset;
@@ -188,9 +188,14 @@
// <Double Function(Double), double Function(double)>(..., isLeaf:true)
final asFunctionInvocation = StaticInvocation(
asFunctionMethod,
- Arguments([fromAddressInvocation],
- types: [ffiFunctionType, dartFunctionType],
- named: [NamedExpression('isLeaf', BoolLiteral(isLeaf))]))
+ Arguments(<Expression>[
+ fromAddressInvocation
+ ], types: <DartType>[
+ ffiFunctionType,
+ dartFunctionType
+ ], named: <NamedExpression>[
+ NamedExpression('isLeaf', BoolLiteral(isLeaf))
+ ]))
..fileOffset = fileOffset;
// static final _doXyz$FfiNative$Ptr = ...
@@ -234,10 +239,10 @@
// Pointer.fromAddress(_getNativeField(#t0))
return StaticInvocation(
fromAddressInternal,
- Arguments([
- StaticInvocation(
- getNativeFieldFunction, Arguments([VariableGet(temporary)]))
- ], types: [
+ Arguments(<Expression>[
+ StaticInvocation(getNativeFieldFunction,
+ Arguments(<Expression>[VariableGet(temporary)]))
+ ], types: <DartType>[
voidType
]));
}
@@ -305,12 +310,12 @@
// reachabilityFence(#t0);
// } => #t1
final resultBlock = BlockExpression(
- Block([
+ Block(<Statement>[
...temporariesForArguments,
result,
for (final argument in fencedArguments)
- ExpressionStatement(StaticInvocation(
- reachabilityFenceFunction, Arguments([VariableGet(argument)])))
+ ExpressionStatement(StaticInvocation(reachabilityFenceFunction,
+ Arguments(<Expression>[VariableGet(argument)])))
]),
VariableGet(result),
);
@@ -467,7 +472,7 @@
for (final parameter in node.function.positionalParameters) parameter.type
], node.function.returnType, Nullability.nonNullable);
- final argumentList = [
+ final argumentList = <Expression>[
ThisExpression(),
for (final parameter in node.function.positionalParameters)
VariableGet(parameter)
@@ -495,7 +500,7 @@
final dartFunctionType =
node.function.computeThisFunctionType(Nullability.nonNullable);
- final argumentList = [
+ final argumentList = <Expression>[
for (final parameter in node.function.positionalParameters)
VariableGet(parameter)
];
diff --git a/runtime/bin/socket.cc b/runtime/bin/socket.cc
index 2333b43..62cc613 100644
--- a/runtime/bin/socket.cc
+++ b/runtime/bin/socket.cc
@@ -383,6 +383,11 @@
}
}
+// This function will abort if sourceAddr is a Unix domain socket.
+// The family ("sa_family") of the socket address is infered from the length
+// of the address. Unix domain sockets addresses are the bytes of their file
+// system path so they have variable length. They cannot, therefore, be
+// differentiated from other address types in this function.
void FUNCTION_NAME(Socket_CreateBindConnect)(Dart_NativeArguments args) {
RawAddr addr;
SocketAddress::GetSockAddr(Dart_GetNativeArgument(args, 1), &addr);
diff --git a/sdk/lib/_internal/vm/bin/socket_patch.dart b/sdk/lib/_internal/vm/bin/socket_patch.dart
index 5f0379e..8ce0776 100644
--- a/sdk/lib/_internal/vm/bin/socket_patch.dart
+++ b/sdk/lib/_internal/vm/bin/socket_patch.dart
@@ -740,7 +740,16 @@
connectionResult = socket.nativeCreateUnixDomainConnect(
address.address, _Namespace._namespace);
} else {
- assert(source.type == InternetAddressType.unix);
+ if (source.type != InternetAddressType.unix) {
+ return SocketException(
+ // Use the same error message as used on Linux for better
+ // searchability...
+ "Address family not supported by protocol family, "
+ // ...and then add some details.
+ "sourceAddress.type must be ${InternetAddressType.unix} but was "
+ "${source.type}",
+ address: address);
+ }
connectionResult = socket.nativeCreateUnixDomainBindConnect(
address.address, source.address, _Namespace._namespace);
}
@@ -753,6 +762,17 @@
connectionResult = socket.nativeCreateConnect(
address_._in_addr, port, address_._scope_id);
} else {
+ if (source.type != InternetAddressType.IPv4 &&
+ source.type != InternetAddressType.IPv6) {
+ return SocketException(
+ // Use the same error message as used on Linux for better
+ // searchability...
+ "Address family not supported by protocol family, "
+ // ...and then add some details.
+ "sourceAddress.type must be ${InternetAddressType.IPv4} or "
+ "${InternetAddressType.IPv6} but was ${source.type}",
+ address: address);
+ }
connectionResult = socket.nativeCreateBindConnect(
address_._in_addr, port, source._in_addr, address_._scope_id);
}
@@ -773,6 +793,8 @@
return createError(
connectionResult, "Connection failed", address, port);
}
+ } else if (connectionResult is SocketException) {
+ return connectionResult;
} else if (connectionResult is Error) {
return connectionResult;
}
diff --git a/tests/standalone/io/issue_30687_test.dart b/tests/standalone/io/issue_30687_test.dart
index e994f09..1b140e1 100644
--- a/tests/standalone/io/issue_30687_test.dart
+++ b/tests/standalone/io/issue_30687_test.dart
@@ -7,7 +7,7 @@
import 'package:expect/expect.dart';
import 'package:path/path.dart' as path;
-import 'unix_socket_test.dart' show withTempDir;
+import 'test_utils.dart' show withTempDir;
main() async {
await withTempDir('issue_30687', (Directory tempDir) async {
diff --git a/tests/standalone/io/socket_source_address_test.dart b/tests/standalone/io/socket_source_address_test.dart
index 4fe8b39..1e7eb34 100644
--- a/tests/standalone/io/socket_source_address_test.dart
+++ b/tests/standalone/io/socket_source_address_test.dart
@@ -6,39 +6,83 @@
import "dart:async";
import "dart:io";
-import 'test_utils.dart' show retry, throws;
+import 'test_utils.dart' show retry, throws, withTempDir;
-Future testArguments(connectFunction) async {
+Future testArguments(connectFunction, String socketDir) async {
var sourceAddress;
- final server = await ServerSocket.bind(InternetAddress.loopbackIPv4, 0);
- server.listen((_) {
+ final serverIPv4 = await ServerSocket.bind(InternetAddress.loopbackIPv4, 0);
+ serverIPv4.listen((_) {
+ throw 'Unexpected connection from address $sourceAddress';
+ });
+
+ final serverIPv6 = await ServerSocket.bind(InternetAddress.loopbackIPv6, 0);
+ serverIPv6.listen((_) {
+ throw 'Unexpected connection from address $sourceAddress';
+ });
+
+ ServerSocket serverUnix = await ServerSocket.bind(
+ InternetAddress('$socketDir/sock', type: InternetAddressType.unix), 0);
+ serverUnix.listen((_) {
throw 'Unexpected connection from address $sourceAddress';
});
// Illegal type for sourceAddress.
for (sourceAddress in ['www.google.com', 'abc']) {
await throws(
- () => connectFunction('127.0.0.1', server.port,
+ () => connectFunction('127.0.0.1', serverIPv4.port,
sourceAddress: sourceAddress),
(e) => e is ArgumentError);
}
// Unsupported local address.
for (sourceAddress in ['8.8.8.8', new InternetAddress('8.8.8.8')]) {
await throws(
- () => connectFunction('127.0.0.1', server.port,
+ () => connectFunction('127.0.0.1', serverIPv4.port,
sourceAddress: sourceAddress),
(e) =>
e is SocketException &&
e.address == new InternetAddress('8.8.8.8'));
}
- // Address family mismatch.
- for (sourceAddress in ['::1', InternetAddress.loopbackIPv6]) {
+ // Address family mismatch for IPv4.
+ for (sourceAddress in [
+ '::1',
+ InternetAddress.loopbackIPv6,
+ InternetAddress('$socketDir/sock', type: InternetAddressType.unix)
+ ]) {
await throws(
- () => connectFunction('127.0.0.1', server.port,
+ () => connectFunction('127.0.0.1', serverIPv4.port,
sourceAddress: sourceAddress),
(e) => e is SocketException);
}
- await server.close();
+ // Address family mismatch for IPv6.
+ for (sourceAddress in [
+ '127.0.0.1',
+ InternetAddress.loopbackIPv4,
+ InternetAddress('$socketDir/sock', type: InternetAddressType.unix)
+ ]) {
+ await throws(
+ () => connectFunction('::1', serverIPv6.port,
+ sourceAddress: sourceAddress),
+ (e) => e is SocketException);
+ }
+ // Address family mismatch for Unix domain sockets.
+ for (sourceAddress in [
+ '127.0.0.1',
+ InternetAddress.loopbackIPv4,
+ '::1',
+ InternetAddress.loopbackIPv6,
+ ]) {
+ await throws(
+ () => connectFunction(
+ InternetAddress("$socketDir/sock", type: InternetAddressType.unix),
+ serverUnix.port,
+ sourceAddress: sourceAddress),
+ (e) =>
+ e is SocketException &&
+ e.toString().contains('Address family not supported'));
+ }
+ await serverIPv4.close();
+ await serverIPv6.close();
+ await serverUnix.close();
}
// IPv4 addresses to use as source address when connecting locally.
@@ -112,10 +156,14 @@
main() async {
await retry(() async {
- await testArguments(RawSocket.connect);
+ await withTempDir('unix_socket_test', (Directory dir) async {
+ await testArguments(RawSocket.connect, "${dir.path}");
+ });
});
await retry(() async {
- await testArguments(Socket.connect);
+ await withTempDir('unix_socket_test', (Directory dir) async {
+ await testArguments(Socket.connect, "${dir.path}");
+ });
});
await retry(() async {
diff --git a/tests/standalone/io/test_utils.dart b/tests/standalone/io/test_utils.dart
index fb3b3ef..9160c18 100644
--- a/tests/standalone/io/test_utils.dart
+++ b/tests/standalone/io/test_utils.dart
@@ -37,3 +37,24 @@
}
Expect.fail('Did not throw');
}
+
+// Create a temporary directory and delete it when the test function exits.
+Future withTempDir(String prefix, Future<void> test(Directory dir)) async {
+ final tempDir = Directory.systemTemp.createTempSync(prefix);
+ try {
+ await runZonedGuarded(() => test(tempDir), (e, st) {
+ try {
+ tempDir.deleteSync(recursive: true);
+ } catch (_) {
+ // ignore errors
+ }
+ throw e;
+ });
+ } finally {
+ try {
+ tempDir.deleteSync(recursive: true);
+ } catch (_) {
+ // ignore errors
+ }
+ }
+}
diff --git a/tests/standalone/io/unix_socket_regress_46634_test.dart b/tests/standalone/io/unix_socket_regress_46634_test.dart
index 31bce79..fca1b2a 100644
--- a/tests/standalone/io/unix_socket_regress_46634_test.dart
+++ b/tests/standalone/io/unix_socket_regress_46634_test.dart
@@ -4,7 +4,8 @@
import 'dart:io';
-import 'unix_socket_test.dart' show withTempDir, testListenCloseListenClose;
+import 'test_utils.dart' show withTempDir;
+import 'unix_socket_test.dart' show testListenCloseListenClose;
void main() async {
if (!Platform.isMacOS && !Platform.isLinux && !Platform.isAndroid) {
diff --git a/tests/standalone/io/unix_socket_test.dart b/tests/standalone/io/unix_socket_test.dart
index a8bf0cb..2a096b9 100644
--- a/tests/standalone/io/unix_socket_test.dart
+++ b/tests/standalone/io/unix_socket_test.dart
@@ -8,6 +8,8 @@
import 'package:expect/expect.dart';
+import 'test_utils.dart' show withTempDir;
+
Future testAddress(String name) async {
var address = InternetAddress('$name/sock', type: InternetAddressType.unix);
var server = await ServerSocket.bind(address, 0);
@@ -821,27 +823,6 @@
});
}
-// Create socket in temp directory
-Future withTempDir(String prefix, Future<void> test(Directory dir)) async {
- final tempDir = Directory.systemTemp.createTempSync(prefix);
- try {
- await runZonedGuarded(() => test(tempDir), (e, st) {
- try {
- tempDir.deleteSync(recursive: true);
- } catch (_) {
- // ignore errors
- }
- throw e;
- });
- } finally {
- try {
- tempDir.deleteSync(recursive: true);
- } catch (_) {
- // ignore errors
- }
- }
-}
-
void main(List<String> args) async {
runZonedGuarded(() async {
if (args.length > 0 && args[0] == '--start-stdio-message-test') {
diff --git a/tests/standalone_2/io/issue_30687_test.dart b/tests/standalone_2/io/issue_30687_test.dart
index 71ee6fd..b814cc7 100644
--- a/tests/standalone_2/io/issue_30687_test.dart
+++ b/tests/standalone_2/io/issue_30687_test.dart
@@ -9,7 +9,7 @@
import 'package:expect/expect.dart';
import 'package:path/path.dart' as path;
-import 'unix_socket_test.dart' show withTempDir;
+import 'test_utils.dart' show withTempDir;
main() async {
await withTempDir('issue_30687', (Directory tempDir) async {
diff --git a/tests/standalone_2/io/socket_source_address_test.dart b/tests/standalone_2/io/socket_source_address_test.dart
index 57efc5af..7e77209 100644
--- a/tests/standalone_2/io/socket_source_address_test.dart
+++ b/tests/standalone_2/io/socket_source_address_test.dart
@@ -8,39 +8,83 @@
import "dart:async";
import "dart:io";
-import 'test_utils.dart' show retry, throws;
+import 'test_utils.dart' show retry, throws, withTempDir;
-Future testArguments(connectFunction) async {
+Future testArguments(connectFunction, String socketDir) async {
var sourceAddress;
- final server = await ServerSocket.bind(InternetAddress.loopbackIPv4, 0);
- server.listen((_) {
+ final serverIPv4 = await ServerSocket.bind(InternetAddress.loopbackIPv4, 0);
+ serverIPv4.listen((_) {
+ throw 'Unexpected connection from address $sourceAddress';
+ });
+
+ final serverIPv6 = await ServerSocket.bind(InternetAddress.loopbackIPv6, 0);
+ serverIPv6.listen((_) {
+ throw 'Unexpected connection from address $sourceAddress';
+ });
+
+ ServerSocket serverUnix = await ServerSocket.bind(
+ InternetAddress('$socketDir/sock', type: InternetAddressType.unix), 0);
+ serverUnix.listen((_) {
throw 'Unexpected connection from address $sourceAddress';
});
// Illegal type for sourceAddress.
for (sourceAddress in ['www.google.com', 'abc']) {
await throws(
- () => connectFunction('127.0.0.1', server.port,
+ () => connectFunction('127.0.0.1', serverIPv4.port,
sourceAddress: sourceAddress),
(e) => e is ArgumentError);
}
// Unsupported local address.
for (sourceAddress in ['8.8.8.8', new InternetAddress('8.8.8.8')]) {
await throws(
- () => connectFunction('127.0.0.1', server.port,
+ () => connectFunction('127.0.0.1', serverIPv4.port,
sourceAddress: sourceAddress),
(e) =>
e is SocketException &&
e.address == new InternetAddress('8.8.8.8'));
}
- // Address family mismatch.
- for (sourceAddress in ['::1', InternetAddress.loopbackIPv6]) {
+ // Address family mismatch for IPv4.
+ for (sourceAddress in [
+ '::1',
+ InternetAddress.loopbackIPv6,
+ InternetAddress('$socketDir/sock', type: InternetAddressType.unix)
+ ]) {
await throws(
- () => connectFunction('127.0.0.1', server.port,
+ () => connectFunction('127.0.0.1', serverIPv4.port,
sourceAddress: sourceAddress),
(e) => e is SocketException);
}
- await server.close();
+ // Address family mismatch for IPv6.
+ for (sourceAddress in [
+ '127.0.0.1',
+ InternetAddress.loopbackIPv4,
+ InternetAddress('$socketDir/sock', type: InternetAddressType.unix)
+ ]) {
+ await throws(
+ () => connectFunction('::1', serverIPv6.port,
+ sourceAddress: sourceAddress),
+ (e) => e is SocketException);
+ }
+ // Address family mismatch for Unix domain sockets.
+ for (sourceAddress in [
+ '127.0.0.1',
+ InternetAddress.loopbackIPv4,
+ '::1',
+ InternetAddress.loopbackIPv6,
+ ]) {
+ await throws(
+ () => connectFunction(
+ InternetAddress("$socketDir/sock", type: InternetAddressType.unix),
+ serverUnix.port,
+ sourceAddress: sourceAddress),
+ (e) =>
+ e is SocketException &&
+ e.toString().contains('Address family not supported'));
+ }
+ await serverIPv4.close();
+ await serverIPv6.close();
+ await serverUnix.close();
}
// IPv4 addresses to use as source address when connecting locally.
@@ -114,10 +158,14 @@
main() async {
await retry(() async {
- await testArguments(RawSocket.connect);
+ await withTempDir('unix_socket_test', (Directory dir) async {
+ await testArguments(RawSocket.connect, "${dir.path}");
+ });
});
await retry(() async {
- await testArguments(Socket.connect);
+ await withTempDir('unix_socket_test', (Directory dir) async {
+ await testArguments(Socket.connect, "${dir.path}");
+ });
});
await retry(() async {
diff --git a/tests/standalone_2/io/test_utils.dart b/tests/standalone_2/io/test_utils.dart
index 0a36318..256740d 100644
--- a/tests/standalone_2/io/test_utils.dart
+++ b/tests/standalone_2/io/test_utils.dart
@@ -39,3 +39,24 @@
}
Expect.fail('Did not throw');
}
+
+// Create a temporary directory and delete it when the test function exits.
+Future withTempDir(String prefix, Future<void> test(Directory dir)) async {
+ final tempDir = Directory.systemTemp.createTempSync(prefix);
+ try {
+ await runZonedGuarded(() => test(tempDir), (e, st) {
+ try {
+ tempDir.deleteSync(recursive: true);
+ } catch (_) {
+ // ignore errors
+ }
+ throw e;
+ });
+ } finally {
+ try {
+ tempDir.deleteSync(recursive: true);
+ } catch (_) {
+ // ignore errors
+ }
+ }
+}
diff --git a/tests/standalone_2/io/unix_socket_regress_46634_test.dart b/tests/standalone_2/io/unix_socket_regress_46634_test.dart
index 8920965..2ac0168b 100644
--- a/tests/standalone_2/io/unix_socket_regress_46634_test.dart
+++ b/tests/standalone_2/io/unix_socket_regress_46634_test.dart
@@ -5,7 +5,8 @@
// @dart = 2.9
import 'dart:io';
-import 'unix_socket_test.dart' show withTempDir, testListenCloseListenClose;
+import 'test_utils.dart' show withTempDir;
+import 'unix_socket_test.dart' show testListenCloseListenClose;
void main() async {
if (!Platform.isMacOS && !Platform.isLinux && !Platform.isAndroid) {
diff --git a/tests/standalone_2/io/unix_socket_test.dart b/tests/standalone_2/io/unix_socket_test.dart
index 04e8cb2..ecc6b3d 100644
--- a/tests/standalone_2/io/unix_socket_test.dart
+++ b/tests/standalone_2/io/unix_socket_test.dart
@@ -10,6 +10,8 @@
import 'package:expect/expect.dart';
+import 'test_utils.dart' show withTempDir;
+
Future testAddress(String name) async {
var address = InternetAddress('$name/sock', type: InternetAddressType.unix);
var server = await ServerSocket.bind(address, 0);
@@ -823,27 +825,6 @@
return completer.future;
}
-// Create socket in temp directory
-Future withTempDir(String prefix, Future<void> test(Directory dir)) async {
- final tempDir = Directory.systemTemp.createTempSync(prefix);
- try {
- await runZonedGuarded(() => test(tempDir), (e, st) {
- try {
- tempDir.deleteSync(recursive: true);
- } catch (_) {
- // ignore errors
- }
- throw e;
- });
- } finally {
- try {
- tempDir.deleteSync(recursive: true);
- } catch (_) {
- // ignore errors
- }
- }
-}
-
void main(List<String> args) async {
runZonedGuarded(() async {
if (args.length > 0 && args[0] == '--start-stdio-message-test') {
diff --git a/tools/VERSION b/tools/VERSION
index 05b635f..542d300 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 15
PATCH 0
-PRERELEASE 237
+PRERELEASE 238
PRERELEASE_PATCH 0
\ No newline at end of file