@NotNull private DataFlowInfo checkCondition( @NotNull LexicalScope scope, @Nullable KtExpression condition, ExpressionTypingContext context) { if (condition != null) { KotlinTypeInfo typeInfo = facade.getTypeInfo( condition, context .replaceScope(scope) .replaceExpectedType(components.builtIns.getBooleanType()) .replaceContextDependency(INDEPENDENT)); KotlinType conditionType = typeInfo.getType(); if (conditionType != null && !components.builtIns.isBooleanOrSubtype(conditionType)) { context.trace.report(TYPE_MISMATCH_IN_CONDITION.on(condition, conditionType)); } return typeInfo.getDataFlowInfo(); } return context.dataFlowInfo; }
@NotNull private KotlinTypeInfo getTypeInfoWhenOnlyOneBranchIsPresent( @NotNull KtExpression presentBranch, @NotNull LexicalWritableScope presentScope, @NotNull DataFlowInfo presentInfo, @NotNull DataFlowInfo otherInfo, @NotNull ExpressionTypingContext context, @NotNull KtIfExpression ifExpression, boolean isStatement) { ExpressionTypingContext newContext = context .replaceDataFlowInfo(presentInfo) .replaceExpectedType(NO_EXPECTED_TYPE) .replaceContextDependency(INDEPENDENT); KotlinTypeInfo typeInfo = components.expressionTypingServices.getBlockReturnedTypeWithWritableScope( presentScope, Collections.singletonList(presentBranch), CoercionStrategy.NO_COERCION, newContext); KotlinType type = typeInfo.getType(); DataFlowInfo dataFlowInfo; if (type != null && KotlinBuiltIns.isNothing(type)) { dataFlowInfo = otherInfo; } else { dataFlowInfo = typeInfo.getDataFlowInfo().or(otherInfo); } return components .dataFlowAnalyzer .checkImplicitCast( components.dataFlowAnalyzer.checkType( typeInfo.replaceType(components.builtIns.getUnitType()), ifExpression, context), ifExpression, context, isStatement) .replaceDataFlowInfo(dataFlowInfo); }
/** * Visits block statements propagating data flow information from the first to the last. * Determines block returned type and data flow information at the end of the block AND at the * nearest jump point from the block beginning. */ /*package*/ KotlinTypeInfo getBlockReturnedTypeWithWritableScope( @NotNull LexicalWritableScope scope, @NotNull List<? extends KtElement> block, @NotNull CoercionStrategy coercionStrategyForLastExpression, @NotNull ExpressionTypingContext context) { if (block.isEmpty()) { return TypeInfoFactoryKt.createTypeInfo( expressionTypingComponents.builtIns.getUnitType(), context); } ExpressionTypingInternals blockLevelVisitor = new ExpressionTypingVisitorDispatcher.ForBlock( expressionTypingComponents, annotationChecker, scope); ExpressionTypingContext newContext = context.replaceScope(scope).replaceExpectedType(NO_EXPECTED_TYPE); KotlinTypeInfo result = TypeInfoFactoryKt.noTypeInfo(context); // Jump point data flow info DataFlowInfo beforeJumpInfo = newContext.dataFlowInfo; boolean jumpOutPossible = false; for (Iterator<? extends KtElement> iterator = block.iterator(); iterator.hasNext(); ) { KtElement statement = iterator.next(); if (!(statement instanceof KtExpression)) { continue; } KtExpression statementExpression = (KtExpression) statement; if (!iterator.hasNext()) { result = getTypeOfLastExpressionInBlock( statementExpression, newContext.replaceExpectedType(context.expectedType), coercionStrategyForLastExpression, blockLevelVisitor); if (result.getType() != null && statementExpression.getParent() instanceof KtBlockExpression) { DataFlowValue lastExpressionValue = DataFlowValueFactory.createDataFlowValue( statementExpression, result.getType(), context); DataFlowValue blockExpressionValue = DataFlowValueFactory.createDataFlowValue( (KtBlockExpression) statementExpression.getParent(), result.getType(), context); result = result.replaceDataFlowInfo( result.getDataFlowInfo().assign(blockExpressionValue, lastExpressionValue)); } } else { result = blockLevelVisitor.getTypeInfo( statementExpression, newContext.replaceContextDependency(ContextDependency.INDEPENDENT), true); } DataFlowInfo newDataFlowInfo = result.getDataFlowInfo(); // If jump is not possible, we take new data flow info before jump if (!jumpOutPossible) { beforeJumpInfo = result.getJumpFlowInfo(); jumpOutPossible = result.getJumpOutPossible(); } if (newDataFlowInfo != context.dataFlowInfo) { newContext = newContext.replaceDataFlowInfo(newDataFlowInfo); // We take current data flow info if jump there is not possible } blockLevelVisitor = new ExpressionTypingVisitorDispatcher.ForBlock( expressionTypingComponents, annotationChecker, scope); } return result.replaceJumpOutPossible(jumpOutPossible).replaceJumpFlowInfo(beforeJumpInfo); }
public KotlinTypeInfo visitForExpression( KtForExpression expression, ExpressionTypingContext contextWithExpectedType, boolean isStatement) { if (!isStatement) return components.dataFlowAnalyzer.illegalStatementType( expression, contextWithExpectedType, facade); ExpressionTypingContext context = contextWithExpectedType .replaceExpectedType(NO_EXPECTED_TYPE) .replaceContextDependency(INDEPENDENT); // Preliminary analysis PreliminaryLoopVisitor loopVisitor = PreliminaryLoopVisitor.visitLoop(expression); context = context.replaceDataFlowInfo( loopVisitor.clearDataFlowInfoForAssignedLocalVariables(context.dataFlowInfo)); KtExpression loopRange = expression.getLoopRange(); KotlinType expectedParameterType = null; KotlinTypeInfo loopRangeInfo; if (loopRange != null) { ExpressionReceiver loopRangeReceiver = getExpressionReceiver(facade, loopRange, context.replaceScope(context.scope)); loopRangeInfo = facade.getTypeInfo(loopRange, context); if (loopRangeReceiver != null) { expectedParameterType = components.forLoopConventionsChecker.checkIterableConvention( loopRangeReceiver, context); } } else { loopRangeInfo = TypeInfoFactoryKt.noTypeInfo(context); } LexicalWritableScope loopScope = newWritableScopeImpl(context, "Scope with for-loop index"); KtParameter loopParameter = expression.getLoopParameter(); if (loopParameter != null) { VariableDescriptor variableDescriptor = createLoopParameterDescriptor(loopParameter, expectedParameterType, context); components .modifiersChecker .withTrace(context.trace) .checkModifiersForLocalDeclaration(loopParameter, variableDescriptor); components.identifierChecker.checkDeclaration(loopParameter, context.trace); loopScope.addVariableDescriptor(variableDescriptor); } else { KtMultiDeclaration multiParameter = expression.getMultiParameter(); if (multiParameter != null && loopRange != null) { KotlinType elementType = expectedParameterType == null ? ErrorUtils.createErrorType("Loop range has no type") : expectedParameterType; TransientReceiver iteratorNextAsReceiver = new TransientReceiver(elementType); components.annotationResolver.resolveAnnotationsWithArguments( loopScope, multiParameter.getModifierList(), context.trace); components.multiDeclarationResolver.defineLocalVariablesFromMultiDeclaration( loopScope, multiParameter, iteratorNextAsReceiver, loopRange, context); components .modifiersChecker .withTrace(context.trace) .checkModifiersForMultiDeclaration(multiParameter); components .modifiersChecker .withTrace(context.trace) .checkParameterHasNoValOrVar(multiParameter, VAL_OR_VAR_ON_LOOP_MULTI_PARAMETER); components.identifierChecker.checkDeclaration(multiParameter, context.trace); } } KtExpression body = expression.getBody(); KotlinTypeInfo bodyTypeInfo; if (body != null) { bodyTypeInfo = components.expressionTypingServices.getBlockReturnedTypeWithWritableScope( loopScope, Collections.singletonList(body), CoercionStrategy.NO_COERCION, context.replaceDataFlowInfo(loopRangeInfo.getDataFlowInfo())); } else { bodyTypeInfo = loopRangeInfo; } return components .dataFlowAnalyzer .checkType( bodyTypeInfo.replaceType(components.builtIns.getUnitType()), expression, contextWithExpectedType) .replaceDataFlowInfo(loopRangeInfo.getDataFlowInfo()); }
public KotlinTypeInfo visitIfExpression( KtIfExpression ifExpression, ExpressionTypingContext contextWithExpectedType, boolean isStatement) { ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE); KtExpression condition = ifExpression.getCondition(); DataFlowInfo conditionDataFlowInfo = checkCondition(context.scope, condition, context); KtExpression elseBranch = ifExpression.getElse(); KtExpression thenBranch = ifExpression.getThen(); LexicalWritableScope thenScope = newWritableScopeImpl(context, "Then scope"); LexicalWritableScope elseScope = newWritableScopeImpl(context, "Else scope"); DataFlowInfo thenInfo = components .dataFlowAnalyzer .extractDataFlowInfoFromCondition(condition, true, context) .and(conditionDataFlowInfo); DataFlowInfo elseInfo = components .dataFlowAnalyzer .extractDataFlowInfoFromCondition(condition, false, context) .and(conditionDataFlowInfo); if (elseBranch == null) { if (thenBranch != null) { KotlinTypeInfo result = getTypeInfoWhenOnlyOneBranchIsPresent( thenBranch, thenScope, thenInfo, elseInfo, contextWithExpectedType, ifExpression, isStatement); // If jump was possible, take condition check info as the jump info return result.getJumpOutPossible() ? result.replaceJumpOutPossible(true).replaceJumpFlowInfo(conditionDataFlowInfo) : result; } return TypeInfoFactoryKt.createTypeInfo( components.dataFlowAnalyzer.checkImplicitCast( components.builtIns.getUnitType(), ifExpression, contextWithExpectedType, isStatement), thenInfo.or(elseInfo)); } if (thenBranch == null) { return getTypeInfoWhenOnlyOneBranchIsPresent( elseBranch, elseScope, elseInfo, thenInfo, contextWithExpectedType, ifExpression, isStatement); } KtPsiFactory psiFactory = KtPsiFactoryKt.KtPsiFactory(ifExpression); KtBlockExpression thenBlock = psiFactory.wrapInABlockWrapper(thenBranch); KtBlockExpression elseBlock = psiFactory.wrapInABlockWrapper(elseBranch); Call callForIf = createCallForSpecialConstruction( ifExpression, ifExpression, Lists.newArrayList(thenBlock, elseBlock)); MutableDataFlowInfoForArguments dataFlowInfoForArguments = createDataFlowInfoForArgumentsForIfCall(callForIf, thenInfo, elseInfo); ResolvedCall<FunctionDescriptor> resolvedCall = components.controlStructureTypingUtils.resolveSpecialConstructionAsCall( callForIf, ResolveConstruct.IF, Lists.newArrayList("thenBranch", "elseBranch"), Lists.newArrayList(false, false), contextWithExpectedType, dataFlowInfoForArguments); BindingContext bindingContext = context.trace.getBindingContext(); KotlinTypeInfo thenTypeInfo = BindingContextUtils.getRecordedTypeInfo(thenBranch, bindingContext); KotlinTypeInfo elseTypeInfo = BindingContextUtils.getRecordedTypeInfo(elseBranch, bindingContext); assert thenTypeInfo != null : "'Then' branch of if expression was not processed: " + ifExpression; assert elseTypeInfo != null : "'Else' branch of if expression was not processed: " + ifExpression; boolean loopBreakContinuePossible = thenTypeInfo.getJumpOutPossible() || elseTypeInfo.getJumpOutPossible(); KotlinType thenType = thenTypeInfo.getType(); KotlinType elseType = elseTypeInfo.getType(); DataFlowInfo thenDataFlowInfo = thenTypeInfo.getDataFlowInfo(); DataFlowInfo elseDataFlowInfo = elseTypeInfo.getDataFlowInfo(); boolean jumpInThen = thenType != null && KotlinBuiltIns.isNothing(thenType); boolean jumpInElse = elseType != null && KotlinBuiltIns.isNothing(elseType); DataFlowInfo resultDataFlowInfo; if (thenType == null && elseType == null) { resultDataFlowInfo = thenDataFlowInfo.or(elseDataFlowInfo); } else if (thenType == null || (jumpInThen && !jumpInElse)) { resultDataFlowInfo = elseDataFlowInfo; } else if (elseType == null || (jumpInElse && !jumpInThen)) { resultDataFlowInfo = thenDataFlowInfo; } else { resultDataFlowInfo = thenDataFlowInfo.or(elseDataFlowInfo); } KotlinType resultType = resolvedCall.getResultingDescriptor().getReturnType(); // If break or continue was possible, take condition check info as the jump info return TypeInfoFactoryKt.createTypeInfo( components.dataFlowAnalyzer.checkImplicitCast( resultType, ifExpression, contextWithExpectedType, isStatement), resultDataFlowInfo, loopBreakContinuePossible, conditionDataFlowInfo); }