inferUndefinedExpressionType method
DartType?
inferUndefinedExpressionType(
- Expression expression
inherited
Returns an expected DartType of expression, may be null if cannot be
inferred.
Implementation
DartType? inferUndefinedExpressionType(Expression expression) {
var parent = expression.parent;
// `(myFunction(),)` or `(name: myFunction())`.
if (parent case NamedExpression(parent: var grandParent) && var named) {
parent = grandParent;
expression = named;
}
if (parent is RecordLiteral) {
var recordType = inferUndefinedExpressionType(parent);
if (recordType is RecordType) {
if (expression case NamedExpression named) {
return recordType.namedFields
.firstWhere((field) => field.name == named.name.label.name)
.type;
} else {
var index = parent.fields.indexed
.firstWhere((record) => record.$2 == expression)
.$1;
return recordType.positionalFields[index].type;
}
}
}
// `await (v + v2)`
if (parent is ParenthesizedExpression) {
return inferUndefinedExpressionType(parent);
}
// `myFunction();`.
if (expression is MethodInvocation) {
if (parent is CascadeExpression && parent.parent is ExpressionStatement) {
return VoidTypeImpl.instance;
}
if (parent is ExpressionStatement) {
return VoidTypeImpl.instance;
}
}
if (parent case ConditionalExpression conditionalExpression) {
// `v = myFunction() ? 1 : 2;`.
if (conditionalExpression.condition == expression) {
return _coreTypeBool;
} else {
var type = conditionalExpression.correspondingParameter?.type;
if (type is InterfaceType && type.isDartCoreFunction) {
return FunctionTypeImpl(
typeParameters: const [],
parameters: const [],
returnType: DynamicTypeImpl.instance,
nullabilitySuffix: NullabilitySuffix.none,
);
}
return type;
}
}
// `=> myFunction();`.
if (parent is ExpressionFunctionBody) {
if (_closureReturnType(expression) case var returnType?) {
return returnType;
}
var executable = expression.enclosingExecutableElement;
return executable?.returnType;
}
// `return myFunction();`.
if (parent is ReturnStatement) {
if (_closureReturnType(expression) case var returnType?) {
return returnType;
}
var executable = expression.enclosingExecutableElement;
return executable?.returnType;
}
// `int v = myFunction();`.
if (parent is VariableDeclaration) {
var variableDeclaration = parent;
if (variableDeclaration.initializer == expression) {
var variableElement = variableDeclaration.declaredFragment?.element;
if (variableElement case VariableElement(:var type)) {
if (type is InvalidType) {
return typeProvider.dynamicType;
}
return type;
}
}
}
if (parent is AssignmentExpression) {
var assignment = parent;
// `myField = 42;`.
if (assignment.leftHandSide == expression) {
var rhs = assignment.rightHandSide;
return rhs.staticType;
} else if (assignment.rightHandSide == expression) {
if (assignment.operator.type == TokenType.EQ) {
// `v = myFunction();`.
return assignment.writeType;
} else if (assignment.writeType case var expectedType?) {
// `v += myFunction();`.
var method = assignment.element;
if (method case MethodElement(
:var returnType,
formalParameters: List(length: 1, :var first),
)) {
if (typeSystem.isAssignableTo(returnType, expectedType)) {
// The return type is assignable to the expected type, then use
// the expected parameter type.
return first.type;
} else if (typeSystem.isAssignableTo(expectedType, returnType) &&
typeSystem.isAssignableTo(expectedType, first.type)) {
// The expected type is a subtype of the return type and the
// parameter would accept the expected type, then use the
// expected type.
// ---
// Spec (section 17.31) reads:
// The static type of an additive expression is usually
// determined by the signature given in the declaration of the
// operator used. However, invocations of the operators + and -
// of class int, double and num are treated specially by the
// typechecker.
// ---
// This ensures that cases like `int v = 0; v += myFunction();`
// will return `int`.
if (_isSpecialCaseNumTypes(method, expectedType)) {
return expectedType;
}
}
}
return InvalidTypeImpl.instance;
}
}
}
if (parent is BinaryExpression) {
var binary = parent;
var method = binary.element;
// `v + myFunction();`.
if (method != null) {
if (binary.rightOperand == expression) {
var parameters = method.formalParameters;
return parameters.length == 1 ? parameters[0].type : null;
}
} else if (binary.operator.type == TokenType.QUESTION_QUESTION) {
// `v ?? myFunction();`.
// This handles when the expression is being assigned somewhere.
var type = inferUndefinedExpressionType(binary);
if (binary.rightOperand == expression) {
return type ?? binary.leftOperand.staticType;
} else if (binary.leftOperand == expression) {
type ??= binary.rightOperand.staticType;
return switch (type) {
TypeImpl type => type.withNullability(NullabilitySuffix.question),
_ => null,
};
}
}
}
// `foo( myFunction() );`.
if (parent is ArgumentList) {
var parameter = expression.correspondingParameter;
return parameter?.type;
}
// `bool`.
{
// `assert( myFunction() );`.
if (parent is AssertStatement) {
var statement = parent;
if (statement.condition == expression) {
return _coreTypeBool;
}
}
// `if ( myFunction() ) {}`.
if (parent is IfStatement) {
var statement = parent;
if (statement.expression == expression) {
return _coreTypeBool;
}
}
if (parent is WhenClause) {
var clause = parent;
if (clause.expression == expression) {
return _coreTypeBool;
}
}
// `while ( myFunction() ) {}`.
if (parent is WhileStatement) {
var statement = parent;
if (statement.condition == expression) {
return _coreTypeBool;
}
}
// `do {} while ( myFunction() );`.
if (parent is DoStatement) {
var statement = parent;
if (statement.condition == expression) {
return _coreTypeBool;
}
}
// `!myFunction()`.
if (parent is PrefixExpression) {
var prefixExpression = parent;
if (prefixExpression.operator.type == TokenType.BANG) {
return _coreTypeBool;
}
}
// Binary expression `&&` or `||`.
if (parent is BinaryExpression) {
var binaryExpression = parent;
var operatorType = binaryExpression.operator.type;
if (operatorType == TokenType.AMPERSAND_AMPERSAND ||
operatorType == TokenType.BAR_BAR) {
return _coreTypeBool;
}
}
}
// Handle `await`, infer a `Future` type.
if (parent is AwaitExpression) {
var grandParent = parent.parent;
// `await myFunction();`
if (grandParent is ExpressionStatement) {
return typeProvider.futureType(typeProvider.voidType);
}
var inferredParentType =
inferUndefinedExpressionType(parent) ?? typeProvider.dynamicType;
if (inferredParentType is InvalidType) {
inferredParentType = typeProvider.dynamicType;
}
return typeProvider.futureType(inferredParentType);
}
// We don't know.
return null;
}