blob: f4891343a749315c5f58078975a99cf7b5a3d723 [file] [log] [blame]
// Copyright (c) 2018, 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.
// @dart = 2.9
import 'package:_fe_analyzer_shared/src/flow_analysis/flow_analysis.dart';
import 'package:_fe_analyzer_shared/src/util/link.dart';
import 'package:front_end/src/api_prototype/lowering_predicates.dart';
import 'package:kernel/ast.dart';
import 'package:kernel/src/legacy_erasure.dart';
import 'package:kernel/type_algebra.dart' show Substitution;
import 'package:kernel/type_environment.dart';
import '../../base/instrumentation.dart'
show
InstrumentationValueForMember,
InstrumentationValueForType,
InstrumentationValueForTypeArgs;
import '../fasta_codes.dart';
import '../names.dart';
import '../problems.dart' show unhandled;
import '../source/source_library_builder.dart' show SourceLibraryBuilder;
import '../type_inference/type_inference_engine.dart';
import '../type_inference/type_inferrer.dart';
import '../type_inference/type_schema.dart' show UnknownType;
import 'body_builder.dart' show combineStatements;
import 'collections.dart'
show
ForElement,
ForInElement,
ForInMapEntry,
ForMapEntry,
IfElement,
IfMapEntry,
SpreadElement,
SpreadMapEntry,
convertToElement;
import 'implicit_type_argument.dart' show ImplicitTypeArgument;
import 'internal_ast.dart';
import 'late_lowering.dart' as late_lowering;
class InferenceVisitor
implements
ExpressionVisitor1<ExpressionInferenceResult, DartType>,
StatementVisitor<StatementInferenceResult>,
InitializerVisitor<void> {
final TypeInferrerImpl inferrer;
Class mapEntryClass;
// Stores the offset of the map entry found by inferMapEntry.
int mapEntryOffset = null;
// Stores the offset of the map spread found by inferMapEntry.
int mapSpreadOffset = null;
// Stores the offset of the iterable spread found by inferMapEntry.
int iterableSpreadOffset = null;
// Stores the type of the iterable spread found by inferMapEntry.
DartType iterableSpreadType = null;
InferenceVisitor(this.inferrer);
ExpressionInferenceResult _unhandledExpression(
Expression node, DartType typeContext) {
unhandled("${node.runtimeType}", "InferenceVisitor", node.fileOffset,
inferrer.helper.uri);
}
@override
ExpressionInferenceResult defaultExpression(
Expression node, DartType typeContext) {
return _unhandledExpression(node, typeContext);
}
@override
ExpressionInferenceResult defaultBasicLiteral(
BasicLiteral node, DartType typeContext) {
return _unhandledExpression(node, typeContext);
}
@override
ExpressionInferenceResult visitBlockExpression(
BlockExpression node, DartType typeContext) {
return _unhandledExpression(node, typeContext);
}
@override
ExpressionInferenceResult visitConstantExpression(
ConstantExpression node, DartType typeContext) {
return _unhandledExpression(node, typeContext);
}
@override
ExpressionInferenceResult visitDynamicGet(
DynamicGet node, DartType typeContext) {
return _unhandledExpression(node, typeContext);
}
@override
ExpressionInferenceResult visitInstanceGet(
InstanceGet node, DartType typeContext) {
return _unhandledExpression(node, typeContext);
}
@override
ExpressionInferenceResult visitInstanceTearOff(
InstanceTearOff node, DartType typeContext) {
return _unhandledExpression(node, typeContext);
}
@override
ExpressionInferenceResult visitDynamicInvocation(
DynamicInvocation node, DartType typeContext) {
return _unhandledExpression(node, typeContext);
}
@override
ExpressionInferenceResult visitDynamicSet(
DynamicSet node, DartType typeContext) {
return _unhandledExpression(node, typeContext);
}
@override
ExpressionInferenceResult visitEqualsCall(
EqualsCall node, DartType typeContext) {
return _unhandledExpression(node, typeContext);
}
@override
ExpressionInferenceResult visitEqualsNull(
EqualsNull node, DartType typeContext) {
return _unhandledExpression(node, typeContext);
}
@override
ExpressionInferenceResult visitFunctionInvocation(
FunctionInvocation node, DartType typeContext) {
return _unhandledExpression(node, typeContext);
}
@override
ExpressionInferenceResult visitInstanceInvocation(
InstanceInvocation node, DartType typeContext) {
return _unhandledExpression(node, typeContext);
}
@override
ExpressionInferenceResult visitInstanceGetterInvocation(
InstanceGetterInvocation node, DartType typeContext) {
return _unhandledExpression(node, typeContext);
}
@override
ExpressionInferenceResult visitInstanceSet(
InstanceSet node, DartType typeContext) {
return _unhandledExpression(node, typeContext);
}
@override
ExpressionInferenceResult visitLocalFunctionInvocation(
LocalFunctionInvocation node, DartType typeContext) {
return _unhandledExpression(node, typeContext);
}
@override
ExpressionInferenceResult visitStaticTearOff(
StaticTearOff node, DartType typeContext) {
return _unhandledExpression(node, typeContext);
}
@override
ExpressionInferenceResult visitFunctionTearOff(
FunctionTearOff node, DartType typeContext) {
return _unhandledExpression(node, typeContext);
}
@override
ExpressionInferenceResult visitFileUriExpression(
FileUriExpression node, DartType typeContext) {
return _unhandledExpression(node, typeContext);
}
@override
ExpressionInferenceResult visitInstanceCreation(
InstanceCreation node, DartType typeContext) {
return _unhandledExpression(node, typeContext);
}
@override
ExpressionInferenceResult visitInstantiation(
Instantiation node, DartType typeContext) {
return _unhandledExpression(node, typeContext);
}
@override
ExpressionInferenceResult visitListConcatenation(
ListConcatenation node, DartType typeContext) {
return _unhandledExpression(node, typeContext);
}
@override
ExpressionInferenceResult visitMapConcatenation(
MapConcatenation node, DartType typeContext) {
return _unhandledExpression(node, typeContext);
}
@override
ExpressionInferenceResult visitSetConcatenation(
SetConcatenation node, DartType typeContext) {
return _unhandledExpression(node, typeContext);
}
StatementInferenceResult _unhandledStatement(Statement node) {
return unhandled("${node.runtimeType}", "InferenceVisitor", node.fileOffset,
inferrer.helper.uri);
}
@override
StatementInferenceResult defaultStatement(Statement node) {
return _unhandledStatement(node);
}
@override
StatementInferenceResult visitAssertBlock(AssertBlock node) {
return _unhandledStatement(node);
}
@override
StatementInferenceResult visitTryCatch(TryCatch node) {
return _unhandledStatement(node);
}
@override
StatementInferenceResult visitTryFinally(TryFinally node) {
return _unhandledStatement(node);
}
void _unhandledInitializer(Initializer node) {
unhandled("${node.runtimeType}", "InferenceVisitor", node.fileOffset,
node.location.file);
}
@override
void defaultInitializer(Initializer node) {
_unhandledInitializer(node);
}
@override
void visitInvalidInitializer(Initializer node) {
_unhandledInitializer(node);
}
@override
void visitLocalInitializer(LocalInitializer node) {
_unhandledInitializer(node);
}
@override
ExpressionInferenceResult visitInvalidExpression(
InvalidExpression node, DartType typeContext) {
// TODO(johnniwinther): The inferred type should be an InvalidType. Using
// BottomType leads to cascading errors so we use DynamicType for now.
return new ExpressionInferenceResult(const DynamicType(), node);
}
@override
ExpressionInferenceResult visitIntLiteral(
IntLiteral node, DartType typeContext) {
return new ExpressionInferenceResult(
inferrer.coreTypes.intRawType(inferrer.library.nonNullable), node);
}
@override
ExpressionInferenceResult visitAsExpression(
AsExpression node, DartType typeContext) {
ExpressionInferenceResult operandResult = inferrer.inferExpression(
node.operand, const UnknownType(), !inferrer.isTopLevel,
isVoidAllowed: true);
node.operand = operandResult.expression..parent = node;
inferrer.flowAnalysis.asExpression_end(node.operand, node.type);
return new ExpressionInferenceResult(node.type, node);
}
@override
void visitAssertInitializer(AssertInitializer node) {
StatementInferenceResult result = inferrer.inferStatement(node.statement);
if (result.hasChanged) {
node.statement = result.statement..parent = node;
}
}
@override
StatementInferenceResult visitAssertStatement(AssertStatement node) {
inferrer.flowAnalysis.assert_begin();
InterfaceType expectedType =
inferrer.coreTypes.boolRawType(inferrer.library.nonNullable);
ExpressionInferenceResult conditionResult = inferrer.inferExpression(
node.condition, expectedType, !inferrer.isTopLevel,
isVoidAllowed: true);
Expression condition =
inferrer.ensureAssignableResult(expectedType, conditionResult);
node.condition = condition..parent = node;
inferrer.flowAnalysis.assert_afterCondition(node.condition);
if (node.message != null) {
ExpressionInferenceResult messageResult = inferrer.inferExpression(
node.message, const UnknownType(), !inferrer.isTopLevel,
isVoidAllowed: true);
node.message = messageResult.expression..parent = node;
}
inferrer.flowAnalysis.assert_end();
return const StatementInferenceResult();
}
@override
ExpressionInferenceResult visitAwaitExpression(
AwaitExpression node, DartType typeContext) {
if (!inferrer.typeSchemaEnvironment.isEmptyContext(typeContext)) {
typeContext = inferrer.wrapFutureOrType(typeContext);
}
ExpressionInferenceResult operandResult = inferrer.inferExpression(
node.operand, typeContext, true,
isVoidAllowed: !inferrer.isNonNullableByDefault);
DartType inferredType =
inferrer.typeSchemaEnvironment.flatten(operandResult.inferredType);
node.operand = operandResult.expression..parent = node;
return new ExpressionInferenceResult(inferredType, node);
}
List<Statement> _visitStatements<T extends Statement>(List<T> statements) {
List<Statement> result;
for (int index = 0; index < statements.length; index++) {
T statement = statements[index];
StatementInferenceResult statementResult =
inferrer.inferStatement(statement);
if (statementResult.hasChanged) {
if (result == null) {
result = <T>[];
result.addAll(statements.sublist(0, index));
}
if (statementResult.statementCount == 1) {
result.add(statementResult.statement);
} else {
result.addAll(statementResult.statements);
}
} else if (result != null) {
result.add(statement);
}
}
return result;
}
@override
StatementInferenceResult visitBlock(Block node) {
inferrer.registerIfUnreachableForTesting(node);
List<Statement> result = _visitStatements<Statement>(node.statements);
if (result != null) {
Block block = new Block(result)..fileOffset = node.fileOffset;
inferrer.library.loader.dataForTesting?.registerAlias(node, block);
return new StatementInferenceResult.single(block);
} else {
return const StatementInferenceResult();
}
}
@override
ExpressionInferenceResult visitBoolLiteral(
BoolLiteral node, DartType typeContext) {
inferrer.flowAnalysis.booleanLiteral(node, node.value);
return new ExpressionInferenceResult(
inferrer.coreTypes.boolRawType(inferrer.library.nonNullable), node);
}
@override
StatementInferenceResult visitBreakStatement(
covariant BreakStatementImpl node) {
// TODO(johnniwinther): Refactor break/continue encoding.
assert(node.targetStatement != null);
if (node.isContinue) {
inferrer.flowAnalysis.handleContinue(node.targetStatement);
} else {
inferrer.flowAnalysis.handleBreak(node.targetStatement);
}
return const StatementInferenceResult();
}
ExpressionInferenceResult visitCascade(Cascade node, DartType typeContext) {
ExpressionInferenceResult result = inferrer.inferExpression(
node.variable.initializer, typeContext, true,
isVoidAllowed: false);
if (node.isNullAware) {
reportNonNullableInNullAwareWarningIfNeeded(
result.inferredType, "?..", node.fileOffset);
}
node.variable.initializer = result.expression..parent = node.variable;
node.variable.type = result.inferredType;
NullAwareGuard nullAwareGuard;
if (node.isNullAware) {
nullAwareGuard = inferrer.createNullAwareGuard(node.variable);
}
List<ExpressionInferenceResult> expressionResults =
<ExpressionInferenceResult>[];
for (Expression expression in node.expressions) {
expressionResults.add(inferrer.inferExpression(
expression, const UnknownType(), !inferrer.isTopLevel,
isVoidAllowed: true, forEffect: true));
}
List<Statement> body = [];
for (int index = 0; index < expressionResults.length; index++) {
body.add(_createExpressionStatement(expressionResults[index].expression));
}
Expression replacement = _createBlockExpression(node.variable.fileOffset,
_createBlock(body), createVariableGet(node.variable));
if (node.isNullAware) {
replacement =
nullAwareGuard.createExpression(result.inferredType, replacement);
} else {
replacement = new Let(node.variable, replacement)
..fileOffset = node.fileOffset;
}
return new ExpressionInferenceResult(result.inferredType, replacement);
}
Block _createBlock(List<Statement> statements) {
return new Block(statements);
}
BlockExpression _createBlockExpression(
int fileOffset, Block body, Expression value) {
assert(fileOffset != null);
assert(fileOffset != TreeNode.noOffset);
return new BlockExpression(body, value)..fileOffset = fileOffset;
}
ExpressionStatement _createExpressionStatement(Expression expression) {
assert(expression != null);
assert(expression.fileOffset != TreeNode.noOffset);
return new ExpressionStatement(expression)
..fileOffset = expression.fileOffset;
}
@override
ExpressionInferenceResult visitConditionalExpression(
ConditionalExpression node, DartType typeContext) {
inferrer.flowAnalysis.conditional_conditionBegin();
InterfaceType expectedType =
inferrer.coreTypes.boolRawType(inferrer.library.nonNullable);
ExpressionInferenceResult conditionResult = inferrer.inferExpression(
node.condition, expectedType, !inferrer.isTopLevel,
isVoidAllowed: true);
Expression condition =
inferrer.ensureAssignableResult(expectedType, conditionResult);
node.condition = condition..parent = node;
inferrer.flowAnalysis.conditional_thenBegin(node.condition, node);
bool isThenReachable = inferrer.flowAnalysis.isReachable;
ExpressionInferenceResult thenResult = inferrer
.inferExpression(node.then, typeContext, true, isVoidAllowed: true);
node.then = thenResult.expression..parent = node;
inferrer.registerIfUnreachableForTesting(node.then,
isReachable: isThenReachable);
inferrer.flowAnalysis.conditional_elseBegin(node.then);
bool isOtherwiseReachable = inferrer.flowAnalysis.isReachable;
ExpressionInferenceResult otherwiseResult = inferrer.inferExpression(
node.otherwise, typeContext, true,
isVoidAllowed: true);
node.otherwise = otherwiseResult.expression..parent = node;
inferrer.registerIfUnreachableForTesting(node.otherwise,
isReachable: isOtherwiseReachable);
inferrer.flowAnalysis.conditional_end(node, node.otherwise);
DartType inferredType = inferrer.typeSchemaEnvironment
.getStandardUpperBound(thenResult.inferredType,
otherwiseResult.inferredType, inferrer.library.library);
node.staticType = inferredType;
return new ExpressionInferenceResult(inferredType, node);
}
@override
ExpressionInferenceResult visitConstructorInvocation(
ConstructorInvocation node, DartType typeContext) {
inferrer.inferConstructorParameterTypes(node.target);
bool hadExplicitTypeArguments = hasExplicitTypeArguments(node.arguments);
FunctionType functionType = replaceReturnType(
node.target.function
.computeThisFunctionType(inferrer.library.nonNullable),
computeConstructorReturnType(node.target, inferrer.coreTypes));
InvocationInferenceResult result = inferrer.inferInvocation(
typeContext, node.fileOffset, functionType, node.arguments,
isConst: node.isConst, staticTarget: node.target);
if (!inferrer.isTopLevel) {
SourceLibraryBuilder library = inferrer.library;
if (!hadExplicitTypeArguments) {
library.checkBoundsInConstructorInvocation(
node, inferrer.typeSchemaEnvironment, inferrer.helper.uri,
inferred: true);
}
}
return new ExpressionInferenceResult(
result.inferredType, result.applyResult(node));
}
@override
StatementInferenceResult visitContinueSwitchStatement(
ContinueSwitchStatement node) {
inferrer.flowAnalysis.handleContinue(node.target.body);
return const StatementInferenceResult();
}
ExpressionInferenceResult visitExtensionTearOff(
ExtensionTearOff node, DartType typeContext) {
FunctionType calleeType = node.target != null
? node.target.function.computeFunctionType(inferrer.library.nonNullable)
: new FunctionType(
[], const DynamicType(), inferrer.library.nonNullable);
TypeArgumentsInfo typeArgumentsInfo = getTypeArgumentsInfo(node.arguments);
InvocationInferenceResult result = inferrer.inferInvocation(
typeContext, node.fileOffset, calleeType, node.arguments,
staticTarget: node.target);
Expression replacement = new StaticInvocation(node.target, node.arguments);
if (!inferrer.isTopLevel && node.target != null) {
inferrer.library.checkBoundsInStaticInvocation(
replacement,
inferrer.typeSchemaEnvironment,
inferrer.helper.uri,
typeArgumentsInfo);
}
return inferrer.instantiateTearOff(
result.inferredType, typeContext, result.applyResult(replacement));
}
ExpressionInferenceResult visitExtensionSet(
ExtensionSet node, DartType typeContext) {
ExpressionInferenceResult receiverResult = inferrer.inferExpression(
node.receiver, const UnknownType(), true,
isVoidAllowed: false);
List<DartType> extensionTypeArguments =
inferrer.computeExtensionTypeArgument(node.extension,
node.explicitTypeArguments, receiverResult.inferredType);
DartType receiverType = inferrer.getExtensionReceiverType(
node.extension, extensionTypeArguments);
Expression receiver =
inferrer.ensureAssignableResult(receiverType, receiverResult);
ObjectAccessTarget target = new ExtensionAccessTarget(
node.target, null, ProcedureKind.Setter, extensionTypeArguments);
DartType valueType =
inferrer.getSetterType(target, receiverResult.inferredType);
ExpressionInferenceResult valueResult = inferrer.inferExpression(
node.value, const UnknownType(), true,
isVoidAllowed: false);
Expression value = inferrer.ensureAssignableResult(valueType, valueResult);
VariableDeclaration valueVariable;
if (node.forEffect) {
// No need for value variable.
} else {
valueVariable = createVariable(value, valueResult.inferredType);
value = createVariableGet(valueVariable);
}
VariableDeclaration receiverVariable;
if (node.forEffect || isPureExpression(receiver)) {
// No need for receiver variable.
} else {
receiverVariable = createVariable(receiver, receiverResult.inferredType);
receiver = createVariableGet(receiverVariable);
}
Expression assignment = new StaticInvocation(
node.target,
new Arguments(<Expression>[receiver, value],
types: extensionTypeArguments)
..fileOffset = node.fileOffset)
..fileOffset = node.fileOffset;
Expression replacement;
if (node.forEffect) {
assert(receiverVariable == null);
assert(valueVariable == null);
replacement = assignment;
} else {
assert(valueVariable != null);
VariableDeclaration assignmentVariable =
createVariable(assignment, const VoidType());
replacement = createLet(valueVariable,
createLet(assignmentVariable, createVariableGet(valueVariable)));
if (receiverVariable != null) {
replacement = createLet(receiverVariable, replacement);
}
}
replacement.fileOffset = node.fileOffset;
return new ExpressionInferenceResult(valueResult.inferredType, replacement);
}
ExpressionInferenceResult visitCompoundExtensionSet(
CompoundExtensionSet node, DartType typeContext) {
ExpressionInferenceResult receiverResult = inferrer.inferExpression(
node.receiver, const UnknownType(), true,
isVoidAllowed: false);
List<DartType> extensionTypeArguments =
inferrer.computeExtensionTypeArgument(node.extension,
node.explicitTypeArguments, receiverResult.inferredType);
DartType receiverType = inferrer.getExtensionReceiverType(
node.extension, extensionTypeArguments);
Expression receiver =
inferrer.ensureAssignableResult(receiverType, receiverResult);
VariableDeclaration receiverVariable;
Expression readReceiver;
Expression writeReceiver;
if (isPureExpression(receiver)) {
readReceiver = receiver;
writeReceiver = clonePureExpression(receiver);
} else {
receiverVariable = createVariable(receiver, receiverType);
readReceiver = createVariableGet(receiverVariable);
writeReceiver = createVariableGet(receiverVariable);
}
ObjectAccessTarget readTarget = node.getter == null
? const ObjectAccessTarget.missing()
: new ExtensionAccessTarget(
node.getter, null, ProcedureKind.Getter, extensionTypeArguments);
DartType readType = inferrer.getGetterType(readTarget, receiverType);
Expression read;
if (readTarget.isMissing) {
read = inferrer.createMissingPropertyGet(
node.readOffset, readReceiver, readType, node.propertyName);
} else {
assert(readTarget.isExtensionMember);
read = new StaticInvocation(
readTarget.member,
new Arguments(<Expression>[
readReceiver,
], types: readTarget.inferredExtensionTypeArguments)
..fileOffset = node.readOffset)
..fileOffset = node.readOffset;
}
ObjectAccessTarget writeTarget = node.setter == null
? const ObjectAccessTarget.missing()
: new ExtensionAccessTarget(
node.setter, null, ProcedureKind.Setter, extensionTypeArguments);
DartType valueType = inferrer.getSetterType(writeTarget, receiverType);
ExpressionInferenceResult binaryResult = _computeBinaryExpression(
node.binaryOffset,
valueType,
read,
readType,
node.binaryName,
node.rhs,
null);
Expression binary = binaryResult.expression;
DartType binaryType = binaryResult.inferredType;
Expression value = inferrer.ensureAssignable(valueType, binaryType, binary,
isVoidAllowed: true);
VariableDeclaration valueVariable;
if (node.forEffect) {
// No need for value variable.
} else {
valueVariable = createVariable(value, valueType);
value = createVariableGet(valueVariable);
}
Expression write;
if (writeTarget.isMissing) {
write = inferrer.createMissingPropertySet(
node.writeOffset, writeReceiver, readType, node.propertyName, value,
forEffect: node.forEffect);
} else {
assert(writeTarget.isExtensionMember);
write = new StaticInvocation(
writeTarget.member,
new Arguments(<Expression>[
writeReceiver,
value,
], types: writeTarget.inferredExtensionTypeArguments)
..fileOffset = node.writeOffset)
..fileOffset = node.writeOffset;
}
Expression replacement;
if (node.forEffect) {
assert(valueVariable == null);
replacement = write;
} else {
assert(valueVariable != null);
VariableDeclaration writeVariable =
createVariable(write, const VoidType());
replacement = createLet(valueVariable,
createLet(writeVariable, createVariableGet(valueVariable)));
}
if (receiverVariable != null) {
replacement = createLet(receiverVariable, replacement);
}
replacement.fileOffset = node.fileOffset;
return new ExpressionInferenceResult(valueType, replacement);
}
ExpressionInferenceResult visitDeferredCheck(
DeferredCheck node, DartType typeContext) {
// Since the variable is not used in the body we don't need to type infer
// it. We can just type infer the body.
ExpressionInferenceResult result = inferrer.inferExpression(
node.expression, typeContext, true,
isVoidAllowed: true);
Expression replacement = new Let(node.variable, result.expression)
..fileOffset = node.fileOffset;
return new ExpressionInferenceResult(result.inferredType, replacement);
}
@override
StatementInferenceResult visitDoStatement(DoStatement node) {
inferrer.flowAnalysis.doStatement_bodyBegin(node);
StatementInferenceResult bodyResult = inferrer.inferStatement(node.body);
if (bodyResult.hasChanged) {
node.body = bodyResult.statement..parent = node;
}
inferrer.flowAnalysis.doStatement_conditionBegin();
InterfaceType boolType =
inferrer.coreTypes.boolRawType(inferrer.library.nonNullable);
ExpressionInferenceResult conditionResult = inferrer.inferExpression(
node.condition, boolType, !inferrer.isTopLevel,
isVoidAllowed: true);
Expression condition =
inferrer.ensureAssignableResult(boolType, conditionResult);
node.condition = condition..parent = node;
inferrer.flowAnalysis.doStatement_end(condition);
return const StatementInferenceResult();
}
ExpressionInferenceResult visitDoubleLiteral(
DoubleLiteral node, DartType typeContext) {
return new ExpressionInferenceResult(
inferrer.coreTypes.doubleRawType(inferrer.library.nonNullable), node);
}
@override
StatementInferenceResult visitEmptyStatement(EmptyStatement node) {
// No inference needs to be done.
return const StatementInferenceResult();
}
@override
StatementInferenceResult visitExpressionStatement(ExpressionStatement node) {
ExpressionInferenceResult result = inferrer.inferExpression(
node.expression, const UnknownType(), !inferrer.isTopLevel,
isVoidAllowed: true, forEffect: true);
node.expression = result.expression..parent = node;
return const StatementInferenceResult();
}
ExpressionInferenceResult visitFactoryConstructorInvocationJudgment(
FactoryConstructorInvocationJudgment node, DartType typeContext) {
bool hadExplicitTypeArguments = hasExplicitTypeArguments(node.arguments);
FunctionType functionType = replaceReturnType(
node.target.function
.computeThisFunctionType(inferrer.library.nonNullable),
computeConstructorReturnType(node.target, inferrer.coreTypes));
InvocationInferenceResult result = inferrer.inferInvocation(
typeContext, node.fileOffset, functionType, node.arguments,
isConst: node.isConst, staticTarget: node.target);
node.hasBeenInferred = true;
Expression resultNode = node;
if (!inferrer.isTopLevel) {
SourceLibraryBuilder library = inferrer.library;
if (!hadExplicitTypeArguments) {
library.checkBoundsInFactoryInvocation(
node, inferrer.typeSchemaEnvironment, inferrer.helper.uri,
inferred: true);
}
if (inferrer.isNonNullableByDefault) {
if (node.target == inferrer.coreTypes.listDefaultConstructor) {
resultNode = inferrer.helper.wrapInProblem(node,
messageDefaultListConstructorError, node.fileOffset, noLength);
}
}
}
return new ExpressionInferenceResult(
result.inferredType, result.applyResult(resultNode));
}
ExpressionInferenceResult visitTypeAliasedConstructorInvocationJudgment(
TypeAliasedConstructorInvocationJudgment node, DartType typeContext) {
assert(getExplicitTypeArguments(node.arguments) == null);
Typedef typedef = node.typeAliasBuilder.typedef;
FunctionType calleeType = node.target.function
.computeAliasedConstructorFunctionType(
typedef, inferrer.library.library);
calleeType = replaceReturnType(calleeType, calleeType.returnType.unalias);
InvocationInferenceResult result = inferrer.inferInvocation(
typeContext, node.fileOffset, calleeType, node.arguments,
isConst: node.isConst, staticTarget: node.target);
node.hasBeenInferred = true;
Expression resultNode = node;
if (!inferrer.isTopLevel) {
if (inferrer.isNonNullableByDefault) {
if (node.target == inferrer.coreTypes.listDefaultConstructor) {
resultNode = inferrer.helper.wrapInProblem(node,
messageDefaultListConstructorError, node.fileOffset, noLength);
}
}
}
return new ExpressionInferenceResult(
result.inferredType, result.applyResult(resultNode));
}
ExpressionInferenceResult visitTypeAliasedFactoryInvocationJudgment(
TypeAliasedFactoryInvocationJudgment node, DartType typeContext) {
assert(getExplicitTypeArguments(node.arguments) == null);
Typedef typedef = node.typeAliasBuilder.typedef;
FunctionType calleeType = node.target.function
.computeAliasedFactoryFunctionType(typedef, inferrer.library.library);
calleeType = replaceReturnType(calleeType, calleeType.returnType.unalias);
InvocationInferenceResult result = inferrer.inferInvocation(
typeContext, node.fileOffset, calleeType, node.arguments,
isConst: node.isConst, staticTarget: node.target);
node.hasBeenInferred = true;
Expression resultNode = node;
if (!inferrer.isTopLevel) {
if (inferrer.isNonNullableByDefault) {
if (node.target == inferrer.coreTypes.listDefaultConstructor) {
resultNode = inferrer.helper.wrapInProblem(node,
messageDefaultListConstructorError, node.fileOffset, noLength);
}
}
}
return new ExpressionInferenceResult(
result.inferredType, result.applyResult(resultNode));
}
@override
void visitFieldInitializer(FieldInitializer node) {
ExpressionInferenceResult initializerResult =
inferrer.inferExpression(node.value, node.field.type, true);
Expression initializer = inferrer.ensureAssignableResult(
node.field.type, initializerResult,
fileOffset: node.fileOffset);
node.value = initializer..parent = node;
}
ForInResult handleForInDeclaringVariable(
TreeNode node,
VariableDeclaration variable,
Expression iterable,
Statement expressionEffects,
{bool isAsync: false}) {
DartType elementType;
bool typeNeeded = false;
bool typeChecksNeeded = !inferrer.isTopLevel;
if (variable is VariableDeclarationImpl && variable.isImplicitlyTyped) {
typeNeeded = true;
elementType = const UnknownType();
} else {
elementType = variable.type;
}
ExpressionInferenceResult iterableResult = inferForInIterable(
iterable, elementType, typeNeeded || typeChecksNeeded,
isAsync: isAsync);
DartType inferredType = iterableResult.inferredType;
if (typeNeeded) {
inferrer.instrumentation?.record(
inferrer.uriForInstrumentation,
variable.fileOffset,
'type',
new InstrumentationValueForType(inferredType));
variable.type = inferredType;
}
// This is matched by the call to [forEach_end] in
// [inferElement], [inferMapEntry] or [inferForInStatement].
inferrer.flowAnalysis.declare(variable, true);
inferrer.flowAnalysis.forEach_bodyBegin(node);
VariableDeclaration tempVariable =
new VariableDeclaration(null, type: inferredType, isFinal: true);
VariableGet variableGet = new VariableGet(tempVariable)
..fileOffset = variable.fileOffset;
TreeNode parent = variable.parent;
Expression implicitDowncast = inferrer.ensureAssignable(
variable.type, inferredType, variableGet,
fileOffset: parent.fileOffset,
errorTemplate: templateForInLoopElementTypeNotAssignable,
nullabilityErrorTemplate:
templateForInLoopElementTypeNotAssignableNullability,
nullabilityPartErrorTemplate:
templateForInLoopElementTypeNotAssignablePartNullability);
Statement expressionEffect;
if (!identical(implicitDowncast, variableGet)) {
variable.initializer = implicitDowncast..parent = variable;
expressionEffect = variable;
variable = tempVariable;
}
if (expressionEffects != null) {
StatementInferenceResult bodyResult =
inferrer.inferStatement(expressionEffects);
if (bodyResult.hasChanged) {
expressionEffects = bodyResult.statement;
}
if (expressionEffect != null) {
expressionEffects =
combineStatements(expressionEffect, expressionEffects);
}
} else {
expressionEffects = expressionEffect;
}
return new ForInResult(
variable, iterableResult.expression, null, expressionEffects);
}
ExpressionInferenceResult inferForInIterable(
Expression iterable, DartType elementType, bool typeNeeded,
{bool isAsync: false}) {
Class iterableClass = isAsync
? inferrer.coreTypes.streamClass
: inferrer.coreTypes.iterableClass;
DartType context = inferrer.wrapType(
elementType, iterableClass, inferrer.library.nonNullable);
ExpressionInferenceResult iterableResult = inferrer
.inferExpression(iterable, context, typeNeeded, isVoidAllowed: false);
DartType iterableType = iterableResult.inferredType;
iterable = iterableResult.expression;
DartType inferredExpressionType =
inferrer.resolveTypeParameter(iterableType);
iterable = inferrer.ensureAssignable(
inferrer.wrapType(
const DynamicType(), iterableClass, inferrer.library.nonNullable),
inferredExpressionType,
iterable,
errorTemplate: templateForInLoopTypeNotIterable,
nullabilityErrorTemplate: templateForInLoopTypeNotIterableNullability,
nullabilityPartErrorTemplate:
templateForInLoopTypeNotIterablePartNullability);
DartType inferredType;
if (typeNeeded) {
inferredType = const DynamicType();
if (inferredExpressionType is InterfaceType) {
// TODO(johnniwinther): Should we use the type of
// `iterable.iterator.current` instead?
List<DartType> supertypeArguments = inferrer.classHierarchy
.getTypeArgumentsAsInstanceOf(
inferredExpressionType, iterableClass);
if (supertypeArguments != null) {
inferredType = supertypeArguments[0];
}
}
}
return new ExpressionInferenceResult(inferredType, iterable);
}
ForInVariable computeForInVariable(
Expression syntheticAssignment, bool hasProblem) {
if (syntheticAssignment is VariableSet) {
return new LocalForInVariable(syntheticAssignment);
} else if (syntheticAssignment is PropertySet) {
return new PropertyForInVariable(syntheticAssignment);
} else if (syntheticAssignment is SuperPropertySet) {
return new SuperPropertyForInVariable(syntheticAssignment);
} else if (syntheticAssignment is StaticSet) {
return new StaticForInVariable(syntheticAssignment);
} else if (syntheticAssignment is InvalidExpression || hasProblem) {
return new InvalidForInVariable(syntheticAssignment);
} else {
return unhandled(
"${syntheticAssignment.runtimeType}",
"handleForInStatementWithoutVariable",
syntheticAssignment.fileOffset,
inferrer.helper.uri);
}
}
ForInResult handleForInWithoutVariable(
TreeNode node,
VariableDeclaration variable,
Expression iterable,
Expression syntheticAssignment,
Statement expressionEffects,
{bool isAsync: false,
bool hasProblem}) {
assert(hasProblem != null);
bool typeChecksNeeded = !inferrer.isTopLevel;
ForInVariable forInVariable =
computeForInVariable(syntheticAssignment, hasProblem);
DartType elementType = forInVariable.computeElementType(inferrer);
ExpressionInferenceResult iterableResult = inferForInIterable(
iterable, elementType, typeChecksNeeded,
isAsync: isAsync);
DartType inferredType = iterableResult.inferredType;
if (typeChecksNeeded) {
variable.type = inferredType;
}
// This is matched by the call to [forEach_end] in
// [inferElement], [inferMapEntry] or [inferForInStatement].
inferrer.flowAnalysis.forEach_bodyBegin(node);
syntheticAssignment = forInVariable.inferAssignment(inferrer, inferredType);
if (syntheticAssignment is VariableSet) {
inferrer.flowAnalysis.write(node, variable, inferredType, null);
}
if (expressionEffects != null) {
StatementInferenceResult result =
inferrer.inferStatement(expressionEffects);
expressionEffects =
result.hasChanged ? result.statement : expressionEffects;
}
return new ForInResult(variable, iterableResult.expression,
syntheticAssignment, expressionEffects);
}
@override
StatementInferenceResult visitForInStatement(ForInStatement node) {
assert(node.variable.name != null);
ForInResult result = handleForInDeclaringVariable(
node, node.variable, node.iterable, null,
isAsync: node.isAsync);
StatementInferenceResult bodyResult = inferrer.inferStatement(node.body);
// This is matched by the call to [forEach_bodyBegin] in
// [handleForInWithoutVariable] or [handleForInDeclaringVariable].
inferrer.flowAnalysis.forEach_end();
Statement body = bodyResult.hasChanged ? bodyResult.statement : node.body;
if (result.expressionSideEffects != null) {
body = combineStatements(result.expressionSideEffects, body);
}
if (result.syntheticAssignment != null) {
body = combineStatements(
createExpressionStatement(result.syntheticAssignment), body);
}
node.variable = result.variable..parent = node;
node.iterable = result.iterable..parent = node;
node.body = body..parent = node;
return const StatementInferenceResult();
}
StatementInferenceResult visitForInStatementWithSynthesizedVariable(
ForInStatementWithSynthesizedVariable node) {
assert(node.variable.name == null);
ForInResult result = handleForInWithoutVariable(node, node.variable,
node.iterable, node.syntheticAssignment, node.expressionEffects,
isAsync: node.isAsync, hasProblem: node.hasProblem);
StatementInferenceResult bodyResult = inferrer.inferStatement(node.body);
// This is matched by the call to [forEach_bodyBegin] in
// [handleForInWithoutVariable] or [handleForInDeclaringVariable].
inferrer.flowAnalysis.forEach_end();
Statement body = bodyResult.hasChanged ? bodyResult.statement : node.body;
if (result.expressionSideEffects != null) {
body = combineStatements(result.expressionSideEffects, body);
}
if (result.syntheticAssignment != null) {
body = combineStatements(
createExpressionStatement(result.syntheticAssignment), body);
}
Statement replacement = new ForInStatement(
result.variable, result.iterable, body,
isAsync: node.isAsync)
..fileOffset = node.fileOffset
..bodyOffset = node.bodyOffset;
inferrer.library.loader.dataForTesting?.registerAlias(node, replacement);
return new StatementInferenceResult.single(replacement);
}
@override
StatementInferenceResult visitForStatement(ForStatement node) {
List<VariableDeclaration> variables;
for (int index = 0; index < node.variables.length; index++) {
VariableDeclaration variable = node.variables[index];
if (variable.name == null) {
if (variable.initializer != null) {
ExpressionInferenceResult result = inferrer.inferExpression(
variable.initializer, const UnknownType(), true,
isVoidAllowed: true);
variable.initializer = result.expression..parent = variable;
variable.type = result.inferredType;
}
} else {
StatementInferenceResult variableResult =
inferrer.inferStatement(variable);
if (variableResult.hasChanged) {
if (variables == null) {
variables = <VariableDeclaration>[];
variables.addAll(node.variables.sublist(0, index));
}
if (variableResult.statementCount == 1) {
variables.add(variableResult.statement);
} else {
for (VariableDeclaration variable in variableResult.statements) {
variables.add(variable);
}
}
} else if (variables != null) {
variables.add(variable);
}
}
}
if (variables != null) {
node.variables.clear();
node.variables.addAll(variables);
setParents(variables, node);
}
inferrer.flowAnalysis.for_conditionBegin(node);
if (node.condition != null) {
InterfaceType expectedType =
inferrer.coreTypes.boolRawType(inferrer.library.nonNullable);
ExpressionInferenceResult conditionResult = inferrer.inferExpression(
node.condition, expectedType, !inferrer.isTopLevel,
isVoidAllowed: true);
Expression condition =
inferrer.ensureAssignableResult(expectedType, conditionResult);
node.condition = condition..parent = node;
}
inferrer.flowAnalysis.for_bodyBegin(node, node.condition);
StatementInferenceResult bodyResult = inferrer.inferStatement(node.body);
if (bodyResult.hasChanged) {
node.body = bodyResult.statement..parent = node;
}
inferrer.flowAnalysis.for_updaterBegin();
for (int index = 0; index < node.updates.length; index++) {
ExpressionInferenceResult updateResult = inferrer.inferExpression(
node.updates[index], const UnknownType(), !inferrer.isTopLevel,
isVoidAllowed: true);
node.updates[index] = updateResult.expression..parent = node;
}
inferrer.flowAnalysis.for_end();
return const StatementInferenceResult();
}
FunctionType visitFunctionNode(FunctionNode node, DartType typeContext,
DartType returnContext, int returnTypeInstrumentationOffset) {
return inferrer.inferLocalFunction(
node, typeContext, returnTypeInstrumentationOffset, returnContext);
}
@override
StatementInferenceResult visitFunctionDeclaration(
covariant FunctionDeclarationImpl node) {
inferrer.flowAnalysis.declare(node.variable, true);
inferrer.flowAnalysis.functionExpression_begin(node);
inferrer.inferMetadataKeepingHelper(
node.variable, node.variable.annotations);
DartType returnContext =
node.hasImplicitReturnType ? null : node.function.returnType;
FunctionType inferredType =
visitFunctionNode(node.function, null, returnContext, node.fileOffset);
if (inferrer.dataForTesting != null && node.hasImplicitReturnType) {
inferrer.dataForTesting.typeInferenceResult.inferredVariableTypes[node] =
inferredType.returnType;
}
inferrer.library.checkBoundsInFunctionNode(node.function,
inferrer.typeSchemaEnvironment, inferrer.library.fileUri);
node.variable.type = inferredType;
inferrer.flowAnalysis.functionExpression_end();
return const StatementInferenceResult();
}
@override
ExpressionInferenceResult visitFunctionExpression(
FunctionExpression node, DartType typeContext) {
inferrer.flowAnalysis.functionExpression_begin(node);
FunctionType inferredType =
visitFunctionNode(node.function, typeContext, null, node.fileOffset);
if (inferrer.dataForTesting != null) {
inferrer.dataForTesting.typeInferenceResult.inferredVariableTypes[node] =
inferredType.returnType;
}
// In anonymous functions the return type isn't declared, so
// it shouldn't be checked.
inferrer.library.checkBoundsInFunctionNode(
node.function, inferrer.typeSchemaEnvironment, inferrer.library.fileUri,
skipReturnType: true);
inferrer.flowAnalysis.functionExpression_end();
return new ExpressionInferenceResult(inferredType, node);
}
void visitInvalidSuperInitializerJudgment(
InvalidSuperInitializerJudgment node) {
Substitution substitution = Substitution.fromSupertype(
inferrer.classHierarchy.getClassAsInstanceOf(
inferrer.thisType.classNode, node.target.enclosingClass));
FunctionType functionType = replaceReturnType(
substitution.substituteType(node.target.function
.computeThisFunctionType(inferrer.library.nonNullable)
.withoutTypeParameters),
inferrer.thisType);
inferrer.inferInvocation(
null, node.fileOffset, functionType, node.argumentsJudgment,
skipTypeArgumentInference: true);
}
ExpressionInferenceResult visitIfNullExpression(
IfNullExpression node, DartType typeContext) {
// To infer `e0 ?? e1` in context K:
// - Infer e0 in context K to get T0
ExpressionInferenceResult lhsResult = inferrer.inferExpression(
node.left, inferrer.computeNullable(typeContext), true,
isVoidAllowed: false);
reportNonNullableInNullAwareWarningIfNeeded(
lhsResult.inferredType, "??", node.left.fileOffset);
Member equalsMember = inferrer
.findInterfaceMember(
lhsResult.inferredType, equalsName, node.fileOffset)
.member;
// This ends any shorting in `node.left`.
Expression left = lhsResult.expression;
inferrer.flowAnalysis
.ifNullExpression_rightBegin(node.left, lhsResult.inferredType);
// - Let J = T0 if K is `?` else K.
// - Infer e1 in context J to get T1
ExpressionInferenceResult rhsResult;
if (typeContext is UnknownType) {
rhsResult = inferrer.inferExpression(
node.right, lhsResult.inferredType, true,
isVoidAllowed: true);
} else {
rhsResult = inferrer.inferExpression(node.right, typeContext, true,
isVoidAllowed: true);
}
inferrer.flowAnalysis.ifNullExpression_end();
// - Let T = greatest closure of K with respect to `?` if K is not `_`, else
// UP(t0, t1)
// - Then the inferred type is T.
DartType originalLhsType = lhsResult.inferredType;
DartType nonNullableLhsType = originalLhsType.toNonNull();
DartType inferredType = inferrer.typeSchemaEnvironment
.getStandardUpperBound(nonNullableLhsType, rhsResult.inferredType,
inferrer.library.library);
Expression replacement;
if (left is ThisExpression) {
replacement = left;
} else {
VariableDeclaration variable =
createVariable(left, lhsResult.inferredType);
Expression equalsNull = inferrer.createEqualsNull(
lhsResult.expression.fileOffset,
createVariableGet(variable),
equalsMember);
VariableGet variableGet = createVariableGet(variable);
if (inferrer.library.isNonNullableByDefault &&
!identical(nonNullableLhsType, originalLhsType)) {
variableGet.promotedType = nonNullableLhsType;
}
ConditionalExpression conditional = new ConditionalExpression(
equalsNull, rhsResult.expression, variableGet, inferredType)
..fileOffset = node.fileOffset;
replacement = new Let(variable, conditional)
..fileOffset = node.fileOffset;
}
return new ExpressionInferenceResult(inferredType, replacement);
}
@override
StatementInferenceResult visitIfStatement(IfStatement node) {
inferrer.flowAnalysis.ifStatement_conditionBegin();
InterfaceType expectedType =
inferrer.coreTypes.boolRawType(inferrer.library.nonNullable);
ExpressionInferenceResult conditionResult = inferrer.inferExpression(
node.condition, expectedType, !inferrer.isTopLevel,
isVoidAllowed: true);
Expression condition =
inferrer.ensureAssignableResult(expectedType, conditionResult);
node.condition = condition..parent = node;
inferrer.flowAnalysis.ifStatement_thenBegin(condition, node);
StatementInferenceResult thenResult = inferrer.inferStatement(node.then);
if (thenResult.hasChanged) {
node.then = thenResult.statement..parent = node;
}
if (node.otherwise != null) {
inferrer.flowAnalysis.ifStatement_elseBegin();
StatementInferenceResult otherwiseResult =
inferrer.inferStatement(node.otherwise);
if (otherwiseResult.hasChanged) {
node.otherwise = otherwiseResult.statement..parent = node;
}
}
inferrer.flowAnalysis.ifStatement_end(node.otherwise != null);
return const StatementInferenceResult();
}
ExpressionInferenceResult visitIntJudgment(
IntJudgment node, DartType typeContext) {
if (inferrer.isDoubleContext(typeContext)) {
double doubleValue = node.asDouble();
if (doubleValue != null) {
Expression replacement = new DoubleLiteral(doubleValue)
..fileOffset = node.fileOffset;
DartType inferredType =
inferrer.coreTypes.doubleRawType(inferrer.library.nonNullable);
return new ExpressionInferenceResult(inferredType, replacement);
}
}
Expression error = checkWebIntLiteralsErrorIfUnexact(
inferrer, node.value, node.literal, node.fileOffset);
if (error != null) {
return new ExpressionInferenceResult(const DynamicType(), error);
}
DartType inferredType =
inferrer.coreTypes.intRawType(inferrer.library.nonNullable);
return new ExpressionInferenceResult(inferredType, node);
}
ExpressionInferenceResult visitShadowLargeIntLiteral(
ShadowLargeIntLiteral node, DartType typeContext) {
if (inferrer.isDoubleContext(typeContext)) {
double doubleValue = node.asDouble();
if (doubleValue != null) {
Expression replacement = new DoubleLiteral(doubleValue)
..fileOffset = node.fileOffset;
DartType inferredType =
inferrer.coreTypes.doubleRawType(inferrer.library.nonNullable);
return new ExpressionInferenceResult(inferredType, replacement);
}
}
int intValue = node.asInt64();
if (intValue == null) {
Expression replacement = inferrer.helper.buildProblem(
templateIntegerLiteralIsOutOfRange.withArguments(node.literal),
node.fileOffset,
node.literal.length);
return new ExpressionInferenceResult(const DynamicType(), replacement);
}
Expression error = checkWebIntLiteralsErrorIfUnexact(
inferrer, intValue, node.literal, node.fileOffset);
if (error != null) {
return new ExpressionInferenceResult(const DynamicType(), error);
}
Expression replacement = new IntLiteral(intValue);
DartType inferredType =
inferrer.coreTypes.intRawType(inferrer.library.nonNullable);
return new ExpressionInferenceResult(inferredType, replacement);
}
void visitShadowInvalidInitializer(ShadowInvalidInitializer node) {
inferrer.inferExpression(
node.variable.initializer, const UnknownType(), !inferrer.isTopLevel,
isVoidAllowed: false);
}
void visitShadowInvalidFieldInitializer(ShadowInvalidFieldInitializer node) {
ExpressionInferenceResult initializerResult = inferrer.inferExpression(
node.value, node.field.type, !inferrer.isTopLevel,
isVoidAllowed: false);
node.value = initializerResult.expression..parent = node;
}
@override
ExpressionInferenceResult visitIsExpression(
IsExpression node, DartType typeContext) {
ExpressionInferenceResult operandResult = inferrer.inferExpression(
node.operand, const UnknownType(), !inferrer.isTopLevel,
isVoidAllowed: false);
node.operand = operandResult.expression..parent = node;
inferrer.flowAnalysis
.isExpression_end(node, node.operand, /*isNot:*/ false, node.type);
return new ExpressionInferenceResult(
inferrer.coreTypes.boolRawType(inferrer.library.nonNullable), node);
}
@override
StatementInferenceResult visitLabeledStatement(LabeledStatement node) {
bool isSimpleBody = node.body is Block ||
node.body is IfStatement ||
node.body is TryStatement;
if (isSimpleBody) {
inferrer.flowAnalysis.labeledStatement_begin(node);
}
StatementInferenceResult bodyResult = inferrer.inferStatement(node.body);
if (isSimpleBody) {
inferrer.flowAnalysis.labeledStatement_end();
}
if (bodyResult.hasChanged) {
node.body = bodyResult.statement..parent = node;
}
return const StatementInferenceResult();
}
DartType getSpreadElementType(
DartType spreadType, DartType spreadTypeBound, bool isNullAware) {
if (inferrer.coreTypes.isNull(spreadTypeBound)) {
if (inferrer.isNonNullableByDefault) {
return isNullAware ? const NeverType.nonNullable() : null;
} else {
return isNullAware ? const NullType() : null;
}
}
if (spreadTypeBound is InterfaceType) {
List<DartType> supertypeArguments = inferrer.typeSchemaEnvironment
.getTypeArgumentsAsInstanceOf(
spreadTypeBound, inferrer.coreTypes.iterableClass);
if (supertypeArguments == null) {
return null;
}
return supertypeArguments.single;
} else if (spreadType is DynamicType) {
return const DynamicType();
} else if (inferrer.coreTypes.isBottom(spreadType)) {
return const NeverType.nonNullable();
}
return null;
}
ExpressionInferenceResult inferElement(
Expression element,
DartType inferredTypeArgument,
Map<TreeNode, DartType> inferredSpreadTypes,
Map<Expression, DartType> inferredConditionTypes,
bool inferenceNeeded,
bool typeChecksNeeded) {
if (element is SpreadElement) {
ExpressionInferenceResult spreadResult = inferrer.inferExpression(
element.expression,
new InterfaceType(
inferrer.coreTypes.iterableClass,
inferrer.library.nullableIfTrue(element.isNullAware),
<DartType>[inferredTypeArgument]),
inferenceNeeded || typeChecksNeeded,
isVoidAllowed: true);
if (element.isNullAware) {
reportNonNullableInNullAwareWarningIfNeeded(
spreadResult.inferredType, "...?", element.expression.fileOffset);
}
element.expression = spreadResult.expression..parent = element;
DartType spreadType = spreadResult.inferredType;
inferredSpreadTypes[element.expression] = spreadType;
Expression replacement = element;
DartType spreadTypeBound = inferrer.resolveTypeParameter(spreadType);
DartType spreadElementType = getSpreadElementType(
spreadType, spreadTypeBound, element.isNullAware);
if (typeChecksNeeded) {
if (spreadElementType == null) {
if (inferrer.coreTypes.isNull(spreadTypeBound) &&
!element.isNullAware) {
replacement = inferrer.helper.buildProblem(
templateNonNullAwareSpreadIsNull.withArguments(
spreadType, inferrer.isNonNullableByDefault),
element.expression.fileOffset,
1);
} else {
if (inferrer.isNonNullableByDefault &&
spreadType.isPotentiallyNullable &&
spreadType is! DynamicType &&
spreadType is! NullType &&
!element.isNullAware) {
Expression receiver = element.expression;
replacement = inferrer.helper.buildProblem(
messageNullableSpreadError, receiver.fileOffset, 1,
context: inferrer.getWhyNotPromotedContext(
inferrer.flowAnalysis?.whyNotPromoted(receiver)(),
element,
(type) => !type.isPotentiallyNullable));
}
replacement = inferrer.helper.buildProblem(
templateSpreadTypeMismatch.withArguments(
spreadType, inferrer.isNonNullableByDefault),
element.expression.fileOffset,
1);
_copyNonPromotionReasonToReplacement(element, replacement);
}
} else if (spreadTypeBound is InterfaceType) {
if (!inferrer.isAssignable(inferredTypeArgument, spreadElementType)) {
if (inferrer.isNonNullableByDefault) {
IsSubtypeOf subtypeCheckResult = inferrer.typeSchemaEnvironment
.performNullabilityAwareSubtypeCheck(
spreadElementType, inferredTypeArgument);
if (subtypeCheckResult.isSubtypeWhenIgnoringNullabilities()) {
if (spreadElementType == subtypeCheckResult.subtype &&
inferredTypeArgument == subtypeCheckResult.supertype) {
replacement = inferrer.helper.buildProblem(
templateSpreadElementTypeMismatchNullability
.withArguments(
spreadElementType,
inferredTypeArgument,
inferrer.isNonNullableByDefault),
element.expression.fileOffset,
1);
} else {
replacement = inferrer.helper.buildProblem(
templateSpreadElementTypeMismatchPartNullability
.withArguments(
spreadElementType,
inferredTypeArgument,
subtypeCheckResult.subtype,
subtypeCheckResult.supertype,
inferrer.isNonNullableByDefault),
element.expression.fileOffset,
1);
}
} else {
replacement = inferrer.helper.buildProblem(
templateSpreadElementTypeMismatch.withArguments(
spreadElementType,
inferredTypeArgument,
inferrer.isNonNullableByDefault),
element.expression.fileOffset,
1);
}
} else {
replacement = inferrer.helper.buildProblem(
templateSpreadElementTypeMismatch.withArguments(
spreadElementType,
inferredTypeArgument,
inferrer.isNonNullableByDefault),
element.expression.fileOffset,
1);
}
}
if (inferrer.isNonNullableByDefault &&
spreadType.isPotentiallyNullable &&
spreadType is! DynamicType &&
spreadType is! NullType &&
!element.isNullAware) {
Expression receiver = element.expression;
replacement = inferrer.helper.buildProblem(
messageNullableSpreadError, receiver.fileOffset, 1,
context: inferrer.getWhyNotPromotedContext(
inferrer.flowAnalysis?.whyNotPromoted(receiver)(),
element,
(type) => !type.isPotentiallyNullable));
_copyNonPromotionReasonToReplacement(element, replacement);
}
}
}
// Use 'dynamic' for error recovery.
element.elementType = spreadElementType ?? const DynamicType();
return new ExpressionInferenceResult(element.elementType, replacement);
} else if (element is IfElement) {
inferrer.flowAnalysis.ifStatement_conditionBegin();
DartType boolType =
inferrer.coreTypes.boolRawType(inferrer.library.nonNullable);
ExpressionInferenceResult conditionResult = inferrer.inferExpression(
element.condition, boolType, typeChecksNeeded,
isVoidAllowed: false);
Expression condition =
inferrer.ensureAssignableResult(boolType, conditionResult);
element.condition = condition..parent = element;
inferrer.flowAnalysis.ifStatement_thenBegin(condition, element);
ExpressionInferenceResult thenResult = inferElement(
element.then,
inferredTypeArgument,
inferredSpreadTypes,
inferredConditionTypes,
inferenceNeeded,
typeChecksNeeded);
element.then = thenResult.expression..parent = element;
ExpressionInferenceResult otherwiseResult;
if (element.otherwise != null) {
inferrer.flowAnalysis.ifStatement_elseBegin();
otherwiseResult = inferElement(
element.otherwise,
inferredTypeArgument,
inferredSpreadTypes,
inferredConditionTypes,
inferenceNeeded,
typeChecksNeeded);
element.otherwise = otherwiseResult.expression..parent = element;
}
inferrer.flowAnalysis.ifStatement_end(element.otherwise != null);
return new ExpressionInferenceResult(
otherwiseResult == null
? thenResult.inferredType
: inferrer.typeSchemaEnvironment.getStandardUpperBound(
thenResult.inferredType,
otherwiseResult.inferredType,
inferrer.library.library),
element);
} else if (element is ForElement) {
// TODO(johnniwinther): Use _visitStatements instead.
List<VariableDeclaration> variables;
for (int index = 0; index < element.variables.length; index++) {
VariableDeclaration variable = element.variables[index];
if (variable.name == null) {
if (variable.initializer != null) {
ExpressionInferenceResult initializerResult =
inferrer.inferExpression(variable.initializer, variable.type,
inferenceNeeded || typeChecksNeeded,
isVoidAllowed: true);
variable.initializer = initializerResult.expression
..parent = variable;
variable.type = initializerResult.inferredType;
}
} else {
StatementInferenceResult variableResult =
inferrer.inferStatement(variable);
if (variableResult.hasChanged) {
if (variables == null) {
variables = <VariableDeclaration>[];
variables.addAll(element.variables.sublist(0, index));
}
if (variableResult.statementCount == 1) {
variables.add(variableResult.statement);
} else {
for (VariableDeclaration variable in variableResult.statements) {
variables.add(variable);
}
}
} else if (variables != null) {
variables.add(variable);
}
}
}
if (variables != null) {
element.variables.clear();
element.variables.addAll(variables);
setParents(variables, element);
}
inferrer.flowAnalysis.for_conditionBegin(element);
if (element.condition != null) {
ExpressionInferenceResult conditionResult = inferrer.inferExpression(
element.condition,
inferrer.coreTypes.boolRawType(inferrer.library.nonNullable),
inferenceNeeded || typeChecksNeeded,
isVoidAllowed: false);
element.condition = conditionResult.expression..parent = element;
inferredConditionTypes[element.condition] =
conditionResult.inferredType;
}
inferrer.flowAnalysis.for_bodyBegin(null, element.condition);
ExpressionInferenceResult bodyResult = inferElement(
element.body,
inferredTypeArgument,
inferredSpreadTypes,
inferredConditionTypes,
inferenceNeeded,
typeChecksNeeded);
element.body = bodyResult.expression..parent = element;
inferrer.flowAnalysis.for_updaterBegin();
for (int index = 0; index < element.updates.length; index++) {
ExpressionInferenceResult updateResult = inferrer.inferExpression(
element.updates[index],
const UnknownType(),
inferenceNeeded || typeChecksNeeded,
isVoidAllowed: true);
element.updates[index] = updateResult.expression..parent = element;
}
inferrer.flowAnalysis.for_end();
return new ExpressionInferenceResult(bodyResult.inferredType, element);
} else if (element is ForInElement) {
ForInResult result;
if (element.variable.name == null) {
result = handleForInWithoutVariable(
element,
element.variable,
element.iterable,
element.syntheticAssignment,
element.expressionEffects,
isAsync: element.isAsync,
hasProblem: element.problem != null);
} else {
result = handleForInDeclaringVariable(element, element.variable,
element.iterable, element.expressionEffects,
isAsync: element.isAsync);
}
element.variable = result.variable..parent = element;
element.iterable = result.iterable..parent = element;
// TODO(johnniwinther): Use ?.. here instead.
element.syntheticAssignment = result.syntheticAssignment;
result.syntheticAssignment?.parent = element;
// TODO(johnniwinther): Use ?.. here instead.
element.expressionEffects = result.expressionSideEffects;
result.expressionSideEffects?.parent = element;
if (element.problem != null) {
ExpressionInferenceResult problemResult = inferrer.inferExpression(
element.problem,
const UnknownType(),
inferenceNeeded || typeChecksNeeded,
isVoidAllowed: true);
element.problem = problemResult.expression..parent = element;
}
ExpressionInferenceResult bodyResult = inferElement(
element.body,
inferredTypeArgument,
inferredSpreadTypes,
inferredConditionTypes,
inferenceNeeded,
typeChecksNeeded);
element.body = bodyResult.expression..parent = element;
// This is matched by the call to [forEach_bodyBegin] in
// [handleForInWithoutVariable] or [handleForInDeclaringVariable].
inferrer.flowAnalysis.forEach_end();
return new ExpressionInferenceResult(bodyResult.inferredType, element);
} else {
ExpressionInferenceResult result = inferrer.inferExpression(
element, inferredTypeArgument, inferenceNeeded || typeChecksNeeded,
isVoidAllowed: true);
Expression replacement;
if (inferredTypeArgument is! UnknownType) {
replacement = inferrer.ensureAssignableResult(
inferredTypeArgument, result,
isVoidAllowed: inferredTypeArgument is VoidType);
} else {
replacement = result.expression;
}
return new ExpressionInferenceResult(result.inferredType, replacement);
}
}
void _copyNonPromotionReasonToReplacement(
TreeNode oldNode, TreeNode replacement) {
if (!identical(oldNode, replacement) &&
inferrer.dataForTesting?.flowAnalysisResult != null) {
inferrer.dataForTesting.flowAnalysisResult
.nonPromotionReasons[replacement] =
inferrer
.dataForTesting.flowAnalysisResult.nonPromotionReasons[oldNode];
}
}
void checkElement(
Expression item,
Expression parent,
DartType typeArgument,
Map<TreeNode, DartType> inferredSpreadTypes,
Map<Expression, DartType> inferredConditionTypes) {
if (item is SpreadElement) {
DartType spreadType = inferredSpreadTypes[item.expression];
if (spreadType is DynamicType) {
Expression expression = inferrer.ensureAssignable(
inferrer.coreTypes.iterableRawType(
inferrer.library.nullableIfTrue(item.isNullAware)),
spreadType,
item.expression);
item.expression = expression..parent = item;
}
} else if (item is IfElement) {
checkElement(item.then, item, typeArgument, inferredSpreadTypes,
inferredConditionTypes);
if (item.otherwise != null) {
checkElement(item.otherwise, item, typeArgument, inferredSpreadTypes,
inferredConditionTypes);
}
} else if (item is ForElement) {
if (item.condition != null) {
DartType conditionType = inferredConditionTypes[item.condition];
Expression condition = inferrer.ensureAssignable(
inferrer.coreTypes.boolRawType(inferrer.library.nonNullable),
conditionType,
item.condition);
item.condition = condition..parent = item;
}
checkElement(item.body, item, typeArgument, inferredSpreadTypes,
inferredConditionTypes);
} else if (item is ForInElement) {
checkElement(item.body, item, typeArgument, inferredSpreadTypes,
inferredConditionTypes);
} else {
// Do nothing. Assignability checks are done during type inference.
}
}
@override
ExpressionInferenceResult visitListLiteral(
ListLiteral node, DartType typeContext) {
Class listClass = inferrer.coreTypes.listClass;
InterfaceType listType = inferrer.coreTypes
.thisInterfaceType(listClass, inferrer.library.nonNullable);
List<DartType> inferredTypes;
DartType inferredTypeArgument;
List<DartType> formalTypes;
List<DartType> actualTypes;
bool inferenceNeeded = node.typeArgument is ImplicitTypeArgument;
bool typeChecksNeeded = !inferrer.isTopLevel;
Map<TreeNode, DartType> inferredSpreadTypes;
Map<Expression, DartType> inferredConditionTypes;
if (inferenceNeeded || typeChecksNeeded) {
formalTypes = [];
actualTypes = [];
inferredSpreadTypes = new Map<TreeNode, DartType>.identity();
inferredConditionTypes = new Map<Expression, DartType>.identity();
}
if (inferenceNeeded) {
inferredTypes = [const UnknownType()];
inferrer.typeSchemaEnvironment.inferGenericFunctionOrType(
listType,
listClass.typeParameters,
null,
null,
typeContext,
inferredTypes,
inferrer.library.library,
isConst: node.isConst);
inferredTypeArgument = inferredTypes[0];
if (inferrer.dataForTesting != null) {
inferrer.dataForTesting.typeInferenceResult
.inferredTypeArguments[node] = inferredTypes;
}
} else {
inferredTypeArgument = node.typeArgument;
}
if (inferenceNeeded || typeChecksNeeded) {
for (int index = 0; index < node.expressions.length; ++index) {
ExpressionInferenceResult result = inferElement(
node.expressions[index],
inferredTypeArgument,
inferredSpreadTypes,
inferredConditionTypes,
inferenceNeeded,
typeChecksNeeded);
node.expressions[index] = result.expression..parent = node;
actualTypes.add(result.inferredType);
if (inferenceNeeded) {
formalTypes.add(listType.typeArguments[0]);
}
}
}
if (inferenceNeeded) {
inferrer.typeSchemaEnvironment.inferGenericFunctionOrType(
listType,
listClass.typeParameters,
formalTypes,
actualTypes,
typeContext,
inferredTypes,
inferrer.library.library);
inferredTypeArgument = inferredTypes[0];
inferrer.instrumentation?.record(
inferrer.uriForInstrumentation,
node.fileOffset,
'typeArgs',
new InstrumentationValueForTypeArgs([inferredTypeArgument]));
node.typeArgument = inferredTypeArgument;
}
if (typeChecksNeeded) {
for (int i = 0; i < node.expressions.length; i++) {
checkElement(node.expressions[i], node, node.typeArgument,
inferredSpreadTypes, inferredConditionTypes);
}
}
DartType inferredType = new InterfaceType(
listClass, inferrer.library.nonNullable, [inferredTypeArgument]);
if (!inferrer.isTopLevel) {
SourceLibraryBuilder library = inferrer.library;
if (inferenceNeeded) {
library.checkBoundsInListLiteral(
node, inferrer.typeSchemaEnvironment, inferrer.helper.uri,
inferred: true);
}
}
return new ExpressionInferenceResult(inferredType, node);
}
@override
ExpressionInferenceResult visitLogicalExpression(
LogicalExpression node, DartType typeContext) {
InterfaceType boolType =
inferrer.coreTypes.boolRawType(inferrer.library.nonNullable);
inferrer.flowAnalysis.logicalBinaryOp_begin();
ExpressionInferenceResult leftResult = inferrer.inferExpression(
node.left, boolType, !inferrer.isTopLevel,
isVoidAllowed: false);
Expression left = inferrer.ensureAssignableResult(boolType, leftResult);
node.left = left..parent = node;
inferrer.flowAnalysis.logicalBinaryOp_rightBegin(node.left, node,
isAnd: node.operatorEnum == LogicalExpressionOperator.AND);
ExpressionInferenceResult rightResult = inferrer.inferExpression(
node.right, boolType, !inferrer.isTopLevel,
isVoidAllowed: false);
Expression right = inferrer.ensureAssignableResult(boolType, rightResult);
node.right = right..parent = node;
inferrer.flowAnalysis.logicalBinaryOp_end(node, node.right,
isAnd: node.operatorEnum == LogicalExpressionOperator.AND);
return new ExpressionInferenceResult(boolType, node);
}
// Calculates the key and the value type of a spread map entry of type
// spreadMapEntryType and stores them in output in positions offset and offset
// + 1. If the types can't be calculated, for example, if spreadMapEntryType
// is a function type, the original values in output are preserved.
void storeSpreadMapEntryElementTypes(DartType spreadMapEntryType,
bool isNullAware, List<DartType> output, int offset) {
DartType typeBound = inferrer.resolveTypeParameter(spreadMapEntryType);
if (inferrer.coreTypes.isNull(typeBound)) {
if (isNullAware) {
if (inferrer.isNonNullableByDefault) {
output[offset] = output[offset + 1] = const NeverType.nonNullable();
} else {
output[offset] = output[offset + 1] = const NullType();
}
}
} else if (typeBound is InterfaceType) {
List<DartType> supertypeArguments = inferrer.typeSchemaEnvironment
.getTypeArgumentsAsInstanceOf(typeBound, inferrer.coreTypes.mapClass);
if (supertypeArguments != null) {
output[offset] = supertypeArguments[0];
output[offset + 1] = supertypeArguments[1];
}
} else if (spreadMapEntryType is DynamicType) {
output[offset] = output[offset + 1] = const DynamicType();
} else if (inferrer.coreTypes.isBottom(spreadMapEntryType)) {
output[offset] = output[offset + 1] = const NeverType.nonNullable();
}
}
// Note that inferMapEntry adds exactly two elements to actualTypes -- the
// actual types of the key and the value. The same technique is used for
// actualTypesForSet, only inferMapEntry adds exactly one element to that
// list: the actual type of the iterable spread elements in case the map
// literal will be disambiguated as a set literal later.
MapLiteralEntry inferMapEntry(
MapLiteralEntry entry,
TreeNode parent,
DartType inferredKeyType,
DartType inferredValueType,
DartType spreadContext,
List<DartType> actualTypes,
List<DartType> actualTypesForSet,
Map<TreeNode, DartType> inferredSpreadTypes,
Map<Expression, DartType> inferredConditionTypes,
bool inferenceNeeded,
bool typeChecksNeeded) {
if (entry is SpreadMapEntry) {
ExpressionInferenceResult spreadResult = inferrer.inferExpression(
entry.expression, spreadContext, inferenceNeeded || typeChecksNeeded,
isVoidAllowed: true);
if (entry.isNullAware) {
reportNonNullableInNullAwareWarningIfNeeded(
spreadResult.inferredType, "...?", entry.expression.fileOffset);
}
entry.expression = spreadResult.expression..parent = entry;
DartType spreadType = spreadResult.inferredType;
inferredSpreadTypes[entry.expression] = spreadType;
int length = actualTypes.length;
actualTypes.add(null);
actualTypes.add(null);
storeSpreadMapEntryElementTypes(
spreadType, entry.isNullAware, actualTypes, length);
DartType actualKeyType = actualTypes[length];
DartType actualValueType = actualTypes[length + 1];
DartType spreadTypeBound = inferrer.resolveTypeParameter(spreadType);
DartType actualElementType =
getSpreadElementType(spreadType, spreadTypeBound, entry.isNullAware);
MapLiteralEntry replacement = entry;
if (typeChecksNeeded) {
if (actualKeyType == null) {
if (inferrer.coreTypes.isNull(spreadTypeBound) &&
!entry.isNullAware) {
replacement = new MapLiteralEntry(
inferrer.helper.buildProblem(
templateNonNullAwareSpreadIsNull.withArguments(
spreadType, inferrer.isNonNullableByDefault),
entry.expression.fileOffset,
1),
new NullLiteral())
..fileOffset = entry.fileOffset;
} else if (actualElementType != null) {
if (inferrer.isNonNullableByDefault &&
spreadType.isPotentiallyNullable &&
spreadType is! DynamicType &&
spreadType is! NullType &&
!entry.isNullAware) {
Expression receiver = entry.expression;
Expression problem = inferrer.helper.buildProblem(
messageNullableSpreadError, receiver.fileOffset, 1,
context: inferrer.getWhyNotPromotedContext(
inferrer.flowAnalysis?.whyNotPromoted(receiver)(),
entry,
(type) => !type.isPotentiallyNullable));
_copyNonPromotionReasonToReplacement(entry, problem);
replacement = new SpreadMapEntry(problem, isNullAware: false)
..fileOffset = entry.fileOffset;
}
// Don't report the error here, it might be an ambiguous Set. The
// error is reported in checkMapEntry if it's disambiguated as map.
iterableSpreadType = spreadType;
} else {
Expression receiver = entry.expression;
Expression problem = inferrer.helper.buildProblem(
templateSpreadMapEntryTypeMismatch.withArguments(
spreadType, inferrer.isNonNullableByDefault),
receiver.fileOffset,
1,
context: inferrer.getWhyNotPromotedContext(
inferrer.flowAnalysis?.whyNotPromoted(receiver)(),
entry,
(type) => !type.isPotentiallyNullable));
_copyNonPromotionReasonToReplacement(entry, problem);
replacement = new MapLiteralEntry(problem, new NullLiteral())
..fileOffset = entry.fileOffset;
}
} else if (spreadTypeBound is InterfaceType) {
Expression keyError;
Expression valueError;
if (!inferrer.isAssignable(inferredKeyType, actualKeyType)) {
if (inferrer.isNonNullableByDefault) {
IsSubtypeOf subtypeCheckResult = inferrer.typeSchemaEnvironment
.performNullabilityAwareSubtypeCheck(
actualKeyType, inferredKeyType);
if (subtypeCheckResult.isSubtypeWhenIgnoringNullabilities()) {
if (actualKeyType == subtypeCheckResult.subtype &&
inferredKeyType == subtypeCheckResult.supertype) {
keyError = inferrer.helper.buildProblem(
templateSpreadMapEntryElementKeyTypeMismatchNullability
.withArguments(actualKeyType, inferredKeyType,
inferrer.isNonNullableByDefault),
entry.expression.fileOffset,
1);
} else {
keyError = inferrer.helper.buildProblem(
// ignore: lines_longer_than_80_chars
templateSpreadMapEntryElementKeyTypeMismatchPartNullability
.withArguments(
actualKeyType,
inferredKeyType,
subtypeCheckResult.subtype,
subtypeCheckResult.supertype,
inferrer.isNonNullableByDefault),
entry.expression.fileOffset,
1);
}
} else {
keyError = inferrer.helper.buildProblem(
templateSpreadMapEntryElementKeyTypeMismatch.withArguments(
actualKeyType,
inferredKeyType,
inferrer.isNonNullableByDefault),
entry.expression.fileOffset,
1);
}
} else {
keyError = inferrer.helper.buildProblem(
templateSpreadMapEntryElementKeyTypeMismatch.withArguments(
actualKeyType,
inferredKeyType,
inferrer.isNonNullableByDefault),
entry.expression.fileOffset,
1);
}
}
if (!inferrer.isAssignable(inferredValueType, actualValueType)) {
if (inferrer.isNonNullableByDefault) {
IsSubtypeOf subtypeCheckResult = inferrer.typeSchemaEnvironment
.performNullabilityAwareSubtypeCheck(
actualValueType, inferredValueType);
if (subtypeCheckResult.isSubtypeWhenIgnoringNullabilities()) {
if (actualValueType == subtypeCheckResult.subtype &&
inferredValueType == subtypeCheckResult.supertype) {
valueError = inferrer.helper.buildProblem(
templateSpreadMapEntryElementValueTypeMismatchNullability
.withArguments(actualValueType, inferredValueType,
inferrer.isNonNullableByDefault),
entry.expression.fileOffset,
1);
} else {
valueError = inferrer.helper.buildProblem(
// ignore: lines_longer_than_80_chars
templateSpreadMapEntryElementValueTypeMismatchPartNullability
.withArguments(
actualValueType,
inferredValueType,
subtypeCheckResult.subtype,
subtypeCheckResult.supertype,
inferrer.isNonNullableByDefault),
entry.expression.fileOffset,
1);
}
} else {
valueError = inferrer.helper.buildProblem(
templateSpreadMapEntryElementValueTypeMismatch
.withArguments(actualValueType, inferredValueType,
inferrer.isNonNullableByDefault),
entry.expression.fileOffset,
1);
}
} else {
valueError = inferrer.helper.buildProblem(
templateSpreadMapEntryElementValueTypeMismatch.withArguments(
actualValueType,
inferredValueType,
inferrer.isNonNullableByDefault),
entry.expression.fileOffset,
1);
}
}
if (inferrer.isNonNullableByDefault &&
spreadType.isPotentiallyNullable &&
spreadType is! DynamicType &&
spreadType is! NullType &&
!entry.isNullAware) {
Expression receiver = entry.expression;
keyError = inferrer.helper.buildProblem(
messageNullableSpreadError, receiver.fileOffset, 1,
context: inferrer.getWhyNotPromotedContext(
inferrer.flowAnalysis?.whyNotPromoted(receiver)(),
entry,
(type) => !type.isPotentiallyNullable));
_copyNonPromotionReasonToReplacement(entry, keyError);
}
if (keyError != null || valueError != null) {
keyError ??= new NullLiteral();
valueError ??= new NullLiteral();
replacement = new MapLiteralEntry(keyError, valueError)
..fileOffset = entry.fileOffset;
}
}
}
// Use 'dynamic' for error recovery.
if (actualKeyType == null) {
actualKeyType = actualTypes[length] = const DynamicType();
actualValueType = actualTypes[length + 1] = const DynamicType();
}
// Store the type in case of an ambiguous Set. Use 'dynamic' for error
// recovery.
actualTypesForSet.add(actualElementType ?? const DynamicType());
mapEntryClass ??=
inferrer.coreTypes.index.getClass('dart:core', 'MapEntry');
// TODO(dmitryas): Handle the case of an ambiguous Set.
entry.entryType = new InterfaceType(
mapEntryClass,
inferrer.library.nonNullable,
<DartType>[actualKeyType, actualValueType]);
bool isMap = inferrer.typeSchemaEnvironment.isSubtypeOf(
spreadType,
inferrer.coreTypes.mapRawType(inferrer.library.nullable),
SubtypeCheckMode.withNullabilities);
bool isIterable = inferrer.typeSchemaEnvironment.isSubtypeOf(
spreadType,
inferrer.coreTypes.iterableRawType(inferrer.library.nullable),
SubtypeCheckMode.withNullabilities);
if (isMap && !isIterable) {
mapSpreadOffset = entry.fileOffset;
}
if (!isMap && isIterable) {
iterableSpreadOffset = entry.expression.fileOffset;
}
return replacement;
} else if (entry is IfMapEntry) {
inferrer.flowAnalysis.ifStatement_conditionBegin();
DartType boolType =
inferrer.coreTypes.boolRawType(inferrer.library.nonNullable);
ExpressionInferenceResult conditionResult = inferrer.inferExpression(
entry.condition, boolType, typeChecksNeeded,
isVoidAllowed: false);
Expression condition =
inferrer.ensureAssignableResult(boolType, conditionResult);
entry.condition = condition..parent = entry;
inferrer.flowAnalysis.ifStatement_thenBegin(condition, entry);
// Note that this recursive invocation of inferMapEntry will add two types
// to actualTypes; they are the actual types of the current invocation if
// the 'else' branch is empty.
MapLiteralEntry then = inferMapEntry(
entry.then,
entry,
inferredKeyType,
inferredValueType,
spreadContext,
actualTypes,
actualTypesForSet,
inferredSpreadTypes,
inferredConditionTypes,
inferenceNeeded,
typeChecksNeeded);
entry.then = then..parent = entry;
MapLiteralEntry otherwise;
if (entry.otherwise != null) {
inferrer.flowAnalysis.ifStatement_elseBegin();
// We need to modify the actual types added in the recursive call to
// inferMapEntry.
DartType actualValueType = actualTypes.removeLast();
DartType actualKeyType = actualTypes.removeLast();
DartType actualTypeForSet = actualTypesForSet.removeLast();
otherwise = inferMapEntry(
entry.otherwise,
entry,
inferredKeyType,
inferredValueType,
spreadContext,
actualTypes,
actualTypesForSet,
inferredSpreadTypes,
inferredConditionTypes,
inferenceNeeded,
typeChecksNeeded);
int length = actualTypes.length;
actualTypes[length - 2] = inferrer.typeSchemaEnvironment
.getStandardUpperBound(actualKeyType, actualTypes[length - 2],
inferrer.library.library);
actualTypes[length - 1] = inferrer.typeSchemaEnvironment
.getStandardUpperBound(actualValueType, actualTypes[length - 1],
inferrer.library.library);
int lengthForSet = actualTypesForSet.length;
actualTypesForSet[lengthForSet - 1] = inferrer.typeSchemaEnvironment
.getStandardUpperBound(actualTypeForSet,
actualTypesForSet[lengthForSet - 1], inferrer.library.library);
entry.otherwise = otherwise..parent = entry;
}
inferrer.flowAnalysis.ifStatement_end(entry.otherwise != null);
return entry;
} else if (entry is ForMapEntry) {
// TODO(johnniwinther): Use _visitStatements instead.
List<VariableDeclaration> variables;
for (int index = 0; index < entry.variables.length; index++) {
VariableDeclaration variable = entry.variables[index];
if (variable.name == null) {
if (variable.initializer != null) {
ExpressionInferenceResult result = inferrer.inferExpression(
variable.initializer,
variable.type,
inferenceNeeded || typeChecksNeeded,
isVoidAllowed: true);
variable.initializer = result.expression..parent = variable;
variable.type = result.inferredType;
}
} else {
StatementInferenceResult variableResult =
inferrer.inferStatement(variable);
if (variableResult.hasChanged) {
if (variables == null) {
variables = <VariableDeclaration>[];
variables.addAll(entry.variables.sublist(0, index));
}
if (variableResult.statementCount == 1) {
variables.add(variableResult.statement);
} else {
for (VariableDeclaration variable in variableResult.statements) {
variables.add(variable);
}
}
} else if (variables != null) {
variables.add(variable);
}
}
}
if (variables != null) {
entry.variables.clear();
entry.variables.addAll(variables);
setParents(variables, entry);
}
inferrer.flowAnalysis.for_conditionBegin(entry);
if (entry.condition != null) {
ExpressionInferenceResult conditionResult = inferrer.inferExpression(
entry.condition,
inferrer.coreTypes.boolRawType(inferrer.library.nonNullable),
inferenceNeeded || typeChecksNeeded,
isVoidAllowed: false);
entry.condition = conditionResult.expression..parent = entry;
// TODO(johnniwinther): Ensure assignability of condition?
inferredConditionTypes[entry.condition] = conditionResult.inferredType;
}
inferrer.flowAnalysis.for_bodyBegin(null, entry.condition);
// Actual types are added by the recursive call.
MapLiteralEntry body = inferMapEntry(
entry.body,
entry,
inferredKeyType,
inferredValueType,
spreadContext,
actualTypes,
actualTypesForSet,
inferredSpreadTypes,
inferredConditionTypes,
inferenceNeeded,
typeChecksNeeded);
entry.body = body..parent = entry;
inferrer.flowAnalysis.for_updaterBegin();
for (int index = 0; index < entry.updates.length; index++) {
ExpressionInferenceResult updateResult = inferrer.inferExpression(
entry.updates[index],
const UnknownType(),
inferenceNeeded || typeChecksNeeded,
isVoidAllowed: true);
entry.updates[index] = updateResult.expression..parent = entry;
}
inferrer.flowAnalysis.for_end();
return entry;
} else if (entry is ForInMapEntry) {
ForInResult result;
if (entry.variable.name == null) {
result = handleForInWithoutVariable(entry, entry.variable,
entry.iterable, entry.syntheticAssignment, entry.expressionEffects,
isAsync: entry.isAsync, hasProblem: entry.problem != null);
} else {
result = handleForInDeclaringVariable(
entry, entry.variable, entry.iterable, entry.expressionEffects,
isAsync: entry.isAsync);
}
entry.variable = result.variable..parent = entry;
entry.iterable = result.iterable..parent = entry;
// TODO(johnniwinther): Use ?.. here instead.
entry.syntheticAssignment = result.syntheticAssignment;
result.syntheticAssignment?.parent = entry;
// TODO(johnniwinther): Use ?.. here instead.
entry.expressionEffects = result.expressionSideEffects;
result.expressionSideEffects?.parent = entry;
if (entry.problem != null) {
ExpressionInferenceResult problemResult = inferrer.inferExpression(
entry.problem,
const UnknownType(),
inferenceNeeded || typeChecksNeeded,
isVoidAllowed: true);
entry.problem = problemResult.expression..parent = entry;
}
// Actual types are added by the recursive call.
MapLiteralEntry body = inferMapEntry(
entry.body,
entry,
inferredKeyType,
inferredValueType,
spreadContext,
actualTypes,
actualTypesForSet,
inferredSpreadTypes,
inferredConditionTypes,
inferenceNeeded,
typeChecksNeeded);
entry.body = body..parent = entry;
// This is matched by the call to [forEach_bodyBegin] in
// [handleForInWithoutVariable] or [handleForInDeclaringVariable].
inferrer.flowAnalysis.forEach_end();
return entry;
} else {
ExpressionInferenceResult keyResult = inferrer.inferExpression(
entry.key, inferredKeyType, true,
isVoidAllowed: true);
Expression key = inferrer.ensureAssignableResult(
inferredKeyType, keyResult,
isVoidAllowed: inferredKeyType is VoidType);
entry.key = key..parent = entry;
ExpressionInferenceResult valueResult = inferrer.inferExpression(
entry.value, inferredValueType, true,
isVoidAllowed: true);
Expression value = inferrer.ensureAssignableResult(
inferredValueType, valueResult,
isVoidAllowed: inferredValueType is VoidType);
entry.value = value..parent = entry;
actualTypes.add(keyResult.inferredType);
actualTypes.add(valueResult.inferredType);
// Use 'dynamic' for error recovery.
actualTypesForSet.add(const DynamicType());
mapEntryOffset = entry.fileOffset;
return entry;
}
}
MapLiteralEntry checkMapEntry(
MapLiteralEntry entry,
DartType keyType,
DartType valueType,
Map<TreeNode, DartType> inferredSpreadTypes,
Map<Expression, DartType> inferredConditionTypes) {
// It's disambiguated as a map literal.
MapLiteralEntry replacement = entry;
if (iterableSpreadOffset != null) {
replacement = new MapLiteralEntry(
inferrer.helper.buildProblem(
templateSpreadMapEntryTypeMismatch.withArguments(
iterableSpreadType, inferrer.isNonNullableByDefault),
iterableSpreadOffset,
1),
new NullLiteral())
..fileOffset = iterableSpreadOffset;
}
if (entry is SpreadMapEntry) {
DartType spreadType = inferredSpreadTypes[entry.expression];
if (spreadType is DynamicType) {
Expression expression = inferrer.ensureAssignable(
inferrer.coreTypes
.mapRawType(inferrer.library.nullableIfTrue(entry.isNullAware)),
spreadType,
entry.expression);
entry.expression = expression..parent = entry;
}
} else if (entry is IfMapEntry) {
MapLiteralEntry then = checkMapEntry(entry.then, keyType, valueType,
inferredSpreadTypes, inferredConditionTypes);
entry.then = then..parent = entry;
if (entry.otherwise != null) {
MapLiteralEntry otherwise = checkMapEntry(entry.otherwise, keyType,
valueType, inferredSpreadTypes, inferredConditionTypes);
entry.otherwise = otherwise..parent = entry;
}
} else if (entry is ForMapEntry) {
if (entry.condition != null) {
DartType conditionType = inferredConditionTypes[entry.condition];
Expression condition = inferrer.ensureAssignable(
inferrer.coreTypes.boolRawType(inferrer.library.nonNullable),
conditionType,
entry.condition);
entry.condition = condition..parent = entry;
}
MapLiteralEntry body = checkMapEntry(entry.body, keyType, valueType,
inferredSpreadTypes, inferredConditionTypes);
entry.body = body..parent = entry;
} else if (entry is ForInMapEntry) {
MapLiteralEntry body = checkMapEntry(entry.body, keyType, valueType,
inferredSpreadTypes, inferredConditionTypes);
entry.body = body..parent = entry;
} else {
// Do nothing. Assignability checks are done during type inference.
}
return replacement;
}
@override
ExpressionInferenceResult visitMapLiteral(
MapLiteral node, DartType typeContext) {
Class mapClass = inferrer.coreTypes.mapClass;
InterfaceType mapType = inferrer.coreTypes
.thisInterfaceType(mapClass, inferrer.library.nonNullable);
List<DartType> inferredTypes;
DartType inferredKeyType;
DartType inferredValueType;
List<DartType> formalTypes;
List<DartType> actualTypes;
List<DartType> actualTypesForSet;
assert((node.keyType is ImplicitTypeArgument) ==
(node.valueType is ImplicitTypeArgument));
bool inferenceNeeded = node.keyType is ImplicitTypeArgument;
bool typeContextIsMap = node.keyType is! ImplicitTypeArgument;
bool typeContextIsIterable = false;
DartType unfuturedTypeContext =
inferrer.typeSchemaEnvironment.flatten(typeContext);
if (!inferrer.isTopLevel && inferenceNeeded) {
// Ambiguous set/map literal
if (unfuturedTypeContext is InterfaceType) {
typeContextIsMap = typeContextIsMap ||
inferrer.classHierarchy.isSubtypeOf(
unfuturedTypeContext.classNode, inferrer.coreTypes.mapClass);
typeContextIsIterable = typeContextIsIterable ||
inferrer.classHierarchy.isSubtypeOf(unfuturedTypeContext.classNode,
inferrer.coreTypes.iterableClass);
if (node.entries.isEmpty &&
typeContextIsIterable &&
!typeContextIsMap) {
// Set literal
SetLiteral setLiteral = new SetLiteral([],
typeArgument: const ImplicitTypeArgument(), isConst: node.isConst)
..fileOffset = node.fileOffset;
return visitSetLiteral(setLiteral, typeContext);
}
}
}
bool typeChecksNeeded = !inferrer.isTopLevel;
Map<TreeNode, DartType> inferredSpreadTypes;
Map<Expression, DartType> inferredConditionTypes;
if (inferenceNeeded || typeChecksNeeded) {
formalTypes = [];
actualTypes = [];
actualTypesForSet = [];
inferredSpreadTypes = new Map<TreeNode, DartType>.identity();
inferredConditionTypes = new Map<Expression, DartType>.identity();
}
if (inferenceNeeded) {
inferredTypes = [const UnknownType(), const UnknownType()];
inferrer.typeSchemaEnvironment.inferGenericFunctionOrType(
mapType,
mapClass.typeParameters,
null,
null,
typeContext,
inferredTypes,
inferrer.library.library,
isConst: node.isConst);
inferredKeyType = inferredTypes[0];
inferredValueType = inferredTypes[1];
if (inferrer.dataForTesting != null) {
inferrer.dataForTesting.typeInferenceResult
.inferredTypeArguments[node] = inferredTypes;
}
} else {
inferredKeyType = node.keyType;
inferredValueType = node.valueType;
}
bool hasMapEntry = false;
bool hasMapSpread = false;
bool hasIterableSpread = false;
if (inferenceNeeded || typeChecksNeeded) {
mapEntryOffset = null;
mapSpreadOffset = null;
iterableSpreadOffset = null;
iterableSpreadType = null;
DartType spreadTypeContext = const UnknownType();
if (typeContextIsIterable && !typeContextIsMap) {
spreadTypeContext = inferrer.typeSchemaEnvironment.getTypeAsInstanceOf(
unfuturedTypeContext,
inferrer.coreTypes.iterableClass,
inferrer.library.library,
inferrer.coreTypes);
} else if (!typeContextIsIterable && typeContextIsMap) {
spreadTypeContext = new InterfaceType(
inferrer.coreTypes.mapClass,
inferrer.library.nonNullable,
<DartType>[inferredKeyType, inferredValueType]);
}
for (int index = 0; index < node.entries.length; ++index) {
MapLiteralEntry entry = node.entries[index];
entry = inferMapEntry(
entry,
node,
inferredKeyType,
inferredValueType,
spreadTypeContext,
actualTypes,
actualTypesForSet,
inferredSpreadTypes,
inferredConditionTypes,
inferenceNeeded,
typeChecksNeeded);
node.entries[index] = entry..parent = node;
if (inferenceNeeded) {
formalTypes.add(mapType.typeArguments[0]);
formalTypes.add(mapType.typeArguments[1]);
}
}
hasMapEntry = mapEntryOffset != null;
hasMapSpread = mapSpreadOffset != null;
hasIterableSpread = iterableSpreadOffset != null;
}
if (inferenceNeeded) {
bool canBeSet = !hasMapSpread && !hasMapEntry && !typeContextIsMap;
bool canBeMap = !hasIterableSpread && !typeContextIsIterable;
if (canBeSet && !canBeMap) {
List<Expression> setElements = <Expression>[];
List<DartType> formalTypesForSet = <DartType>[];
InterfaceType setType = inferrer.coreTypes.thisInterfaceType(
inferrer.coreTypes.setClass, inferrer.library.nonNullable);
for (int i = 0; i < node.entries.length; ++i) {
setElements.add(convertToElement(node.entries[i], inferrer.helper,
inferrer.assignedVariables.reassignInfo));
formalTypesForSet.add(setType.typeArguments[0]);
}
List<DartType> inferredTypesForSet = <DartType>[const UnknownType()];
inferrer.typeSchemaEnvironment.inferGenericFunctionOrType(
setType,
inferrer.coreTypes.setClass.typeParameters,
null,
null,
typeContext,
inferredTypesForSet,
inferrer.library.library,
isConst: node.isConst);
inferrer.typeSchemaEnvironment.inferGenericFunctionOrType(
inferrer.coreTypes.thisInterfaceType(
inferrer.coreTypes.setClass, inferrer.library.nonNullable),
inferrer.coreTypes.setClass.typeParameters,
formalTypesForSet,
actualTypesForSet,
typeContext,
inferredTypesForSet,
inferrer.library.library);
DartType inferredTypeArgument = inferredTypesForSet[0];
inferrer.instrumentation?.record(
inferrer.uriForInstrumentation,
node.fileOffset,
'typeArgs',
new InstrumentationValueForTypeArgs([inferredTypeArgument]));
SetLiteral setLiteral = new SetLiteral(setElements,
typeArgument: inferredTypeArgument, isConst: node.isConst)
..fileOffset = node.fileOffset;
if (typeChecksNeeded) {
for (int i = 0; i < setLiteral.expressions.length; i++) {
checkElement(
setLiteral.expressions[i],
setLiteral,
setLiteral.typeArgument,
inferredSpreadTypes,
inferredConditionTypes);
}
}
DartType inferredType = new InterfaceType(inferrer.coreTypes.setClass,
inferrer.library.nonNullable, inferredTypesForSet);
return new ExpressionInferenceResult(inferredType, setLiteral);
}
if (canBeSet && canBeMap && node.entries.isNotEmpty) {
Expression replacement = node;
if (!inferrer.isTopLevel) {
replacement = inferrer.helper.buildProblem(
messageCantDisambiguateNotEnoughInformation, node.fileOffset, 1);
}
return new ExpressionInferenceResult(
NeverType.fromNullability(inferrer.library.nonNullable),
replacement);
}
if (!canBeSet && !canBeMap) {
Expression replacement = node;
if (!inferrer.isTopLevel) {
replacement = inferrer.helper.buildProblem(
messageCantDisambiguateAmbiguousInformation, node.fileOffset, 1);
}
return new ExpressionInferenceResult(
NeverType.fromNullability(inferrer.library.nonNullable),
replacement);
}
inferrer.typeSchemaEnvironment.inferGenericFunctionOrType(
mapType,
mapClass.typeParameters,
formalTypes,
actualTypes,
typeContext,
inferredTypes,
inferrer.library.library);
inferredKeyType = inferredTypes[0];
inferredValueType = inferredTypes[1];
inferrer.instrumentation?.record(
inferrer.uriForInstrumentation,
node.fileOffset,
'typeArgs',
new InstrumentationValueForTypeArgs(
[inferredKeyType, inferredValueType]));
node.keyType = inferredKeyType;
node.valueType = inferredValueType;
}
if (typeChecksNeeded) {
for (int index = 0; index < node.entries.length; ++index) {
MapLiteralEntry entry = checkMapEntry(node.entries[index], node.keyType,
node.valueType, inferredSpreadTypes, inferredConditionTypes);
node.entries[index] = entry..parent = node;
}
}
DartType inferredType = new InterfaceType(mapClass,
inferrer.library.nonNullable, [inferredKeyType, inferredValueType]);
if (!inferrer.isTopLevel) {
SourceLibraryBuilder library = inferrer.library;
// Either both [_declaredKeyType] and [_declaredValueType] are omitted or
// none of them, so we may just check one.
if (inferenceNeeded) {
library.checkBoundsInMapLiteral(
node, inferrer.typeSchemaEnvironment, inferrer.helper.uri,
inferred: true);
}
}
return new ExpressionInferenceResult(inferredType, node);
}
@override
ExpressionInferenceResult visitMethodInvocation(
MethodInvocation node, DartType typeContext) {
assert(node.name != unaryMinusName);
ExpressionInferenceResult result = inferrer.inferNullAwareExpression(
node.receiver, const UnknownType(), true);
Link<NullAwareGuard> nullAwareGuards = result.nullAwareGuards;
Expression receiver = result.nullAwareAction;
DartType receiverType = result.nullAwareActionType;
return inferrer.inferMethodInvocation(node.fileOffset, nullAwareGuards,
receiver, receiverType, node.name, node.arguments, typeContext,
isExpressionInvocation: false, isImplicitCall: false);
}
ExpressionInferenceResult visitExpressionInvocation(
ExpressionInvocation node, DartType typeContext) {
ExpressionInferenceResult result = inferrer.inferNullAwareExpression(
node.expression, const UnknownType(), true);
Link<NullAwareGuard> nullAwareGuards = result.nullAwareGuards;
Expression receiver = result.nullAwareAction;
DartType receiverType = result.nullAwareActionType;
return inferrer.inferMethodInvocation(node.fileOffset, nullAwareGuards,
receiver, receiverType, callName, node.arguments, typeContext,
isExpressionInvocation: true, isImplicitCall: true);
}
ExpressionInferenceResult visitNamedFunctionExpressionJudgment(
NamedFunctionExpressionJudgment node, DartType typeContext) {
ExpressionInferenceResult initializerResult =
inferrer.inferExpression(node.variable.initializer, typeContext, true);
node.variable.initializer = initializerResult.expression
..parent = node.variable;
node.variable.type = initializerResult.inferredType;
return new ExpressionInferenceResult(initializerResult.inferredType, node);
}
@override
ExpressionInferenceResult visitNot(Not node, DartType typeContext) {
InterfaceType boolType =
inferrer.coreTypes.boolRawType(inferrer.library.nonNullable);
ExpressionInferenceResult operandResult =
inferrer.inferExpression(node.operand, boolType, !inferrer.isTopLevel);
Expression operand = inferrer.ensureAssignableResult(
boolType, operandResult,
fileOffset: node.fileOffset);
node.operand = operand..parent = node;
inferrer.flowAnalysis.logicalNot_end(node, node.operand);
return new ExpressionInferenceResult(boolType, node);
}
@override
ExpressionInferenceResult visitNullCheck(
NullCheck node, DartType typeContext) {
ExpressionInferenceResult operandResult = inferrer.inferNullAwareExpression(
node.operand, inferrer.computeNullable(typeContext), true);
Link<NullAwareGuard> nullAwareGuards = operandResult.nullAwareGuards;
Expression operand = operandResult.nullAwareAction;
DartType operandType = operandResult.nullAwareActionType;
node.operand = operand..parent = node;
reportNonNullableInNullAwareWarningIfNeeded(
operandType, "!", node.operand.fileOffset);
inferrer.flowAnalysis.nonNullAssert_end(node.operand);
DartType nonNullableResultType = operandType.toNonNull();
return inferrer.createNullAwareExpressionInferenceResult(
nonNullableResultType, node, nullAwareGuards);
}
ExpressionInferenceResult visitNullAwareMethodInvocation(
NullAwareMethodInvocation node, DartType typeContext) {
Link<NullAwareGuard> nullAwareGuards =
inferrer.inferSyntheticVariableNullAware(node.variable);
reportNonNullableInNullAwareWarningIfNeeded(
node.variable.type, "?.", node.variable.fileOffset);
NullAwareGuard nullAwareGuard =
inferrer.createNullAwareGuard(node.variable);
ExpressionInferenceResult invocationResult = inferrer.inferExpression(
node.invocation, typeContext, true,
isVoidAllowed: true);
return inferrer.createNullAwareExpressionInferenceResult(
invocationResult.inferredType,
invocationResult.expression,
nullAwareGuards.prepend(nullAwareGuard));
}
ExpressionInferenceResult visitNullAwarePropertyGet(
NullAwarePropertyGet node, DartType typeContext) {
Link<NullAwareGuard> nullAwareGuards =
inferrer.inferSyntheticVariableNullAware(node.variable);
reportNonNullableInNullAwareWarningIfNeeded(
node.variable.type, "?.", node.variable.fileOffset);
NullAwareGuard nullAwareGuard =
inferrer.createNullAwareGuard(node.variable);
ExpressionInferenceResult readResult =
inferrer.inferExpression(node.read, typeContext, true);
return inferrer.createNullAwareExpressionInferenceResult(
readResult.inferredType,
readResult.expression,
nullAwareGuards.prepend(nullAwareGuard));
}
ExpressionInferenceResult visitNullAwarePropertySet(
NullAwarePropertySet node, DartType typeContext) {
Link<NullAwareGuard> nullAwareGuards =
inferrer.inferSyntheticVariableNullAware(node.variable);
reportNonNullableInNullAwareWarningIfNeeded(
node.variable.type, "?.", node.variable.fileOffset);
NullAwareGuard nullAwareGuard =
inferrer.createNullAwareGuard(node.variable);
ExpressionInferenceResult writeResult =
inferrer.inferExpression(node.write, typeContext, true);
return inferrer.createNullAwareExpressionInferenceResult(
writeResult.inferredType,
writeResult.expression,
nullAwareGuards.prepend(nullAwareGuard));
}
ExpressionInferenceResult visitNullAwareExtension(
NullAwareExtension node, DartType typeContext) {
inferrer.inferSyntheticVariable(node.variable);
reportNonNullableInNullAwareWarningIfNeeded(
node.variable.type, "?.", node.variable.fileOffset);
NullAwareGuard nullAwareGuard =
inferrer.createNullAwareGuard(node.variable);
ExpressionInferenceResult expressionResult =
inferrer.