public KotlinTypeInfo visitWhileExpression( KtWhileExpression 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 condition = expression.getCondition(); // Extract data flow info from condition itself without taking value into account DataFlowInfo dataFlowInfo = checkCondition(context.scope, condition, context); KtExpression body = expression.getBody(); KotlinTypeInfo bodyTypeInfo; DataFlowInfo conditionInfo = components .dataFlowAnalyzer .extractDataFlowInfoFromCondition(condition, true, context) .and(dataFlowInfo); if (body != null) { LexicalWritableScope scopeToExtend = newWritableScopeImpl(context, "Scope extended in while's condition"); bodyTypeInfo = components.expressionTypingServices.getBlockReturnedTypeWithWritableScope( scopeToExtend, Collections.singletonList(body), CoercionStrategy.NO_COERCION, context.replaceDataFlowInfo(conditionInfo)); } else { bodyTypeInfo = TypeInfoFactoryKt.noTypeInfo(conditionInfo); } // Condition is false at this point only if there is no jumps outside if (!containsJumpOutOfLoop(expression, context)) { dataFlowInfo = components .dataFlowAnalyzer .extractDataFlowInfoFromCondition(condition, false, context) .and(dataFlowInfo); } // Special case: while (true) // In this case we must record data flow information at the nearest break / continue and // .and it with entrance data flow information, because while body until break is executed at // least once in this case // See KT-6284 if (body != null && KtPsiUtil.isTrueConstant(condition)) { // We should take data flow info from the first jump point, // but without affecting changing variables dataFlowInfo = dataFlowInfo.and( loopVisitor.clearDataFlowInfoForAssignedLocalVariables( bodyTypeInfo.getJumpFlowInfo())); } return components .dataFlowAnalyzer .checkType( bodyTypeInfo.replaceType(components.builtIns.getUnitType()), expression, contextWithExpectedType) .replaceDataFlowInfo(dataFlowInfo); }
public KotlinTypeInfo visitDoWhileExpression( KtDoWhileExpression expression, ExpressionTypingContext contextWithExpectedType, boolean isStatement) { if (!isStatement) return components.dataFlowAnalyzer.illegalStatementType( expression, contextWithExpectedType, facade); ExpressionTypingContext context = contextWithExpectedType .replaceExpectedType(NO_EXPECTED_TYPE) .replaceContextDependency(INDEPENDENT); KtExpression body = expression.getBody(); LexicalScope conditionScope = context.scope; // Preliminary analysis PreliminaryLoopVisitor loopVisitor = PreliminaryLoopVisitor.visitLoop(expression); context = context.replaceDataFlowInfo( loopVisitor.clearDataFlowInfoForAssignedLocalVariables(context.dataFlowInfo)); // Here we must record data flow information at the end of the body (or at the first jump, to be // precise) and // .and it with entrance data flow information, because do-while body is executed at least once // See KT-6283 KotlinTypeInfo bodyTypeInfo; if (body instanceof KtFunctionLiteralExpression) { // As a matter of fact, function literal is always unused at this point bodyTypeInfo = facade.getTypeInfo(body, context.replaceScope(context.scope)); } else if (body != null) { LexicalWritableScope writableScope = newWritableScopeImpl(context, "do..while body scope"); conditionScope = writableScope; List<KtExpression> block; if (body instanceof KtBlockExpression) { block = ((KtBlockExpression) body).getStatements(); } else { block = Collections.singletonList(body); } bodyTypeInfo = components.expressionTypingServices.getBlockReturnedTypeWithWritableScope( writableScope, block, CoercionStrategy.NO_COERCION, context); } else { bodyTypeInfo = TypeInfoFactoryKt.noTypeInfo(context); } KtExpression condition = expression.getCondition(); DataFlowInfo conditionDataFlowInfo = checkCondition(conditionScope, condition, context); DataFlowInfo dataFlowInfo; // Without jumps out, condition is entered and false, with jumps out, we know nothing about it if (!containsJumpOutOfLoop(expression, context)) { dataFlowInfo = components .dataFlowAnalyzer .extractDataFlowInfoFromCondition(condition, false, context) .and(conditionDataFlowInfo); } else { dataFlowInfo = context.dataFlowInfo; } // Here we must record data flow information at the end of the body (or at the first jump, to be // precise) and // .and it with entrance data flow information, because do-while body is executed at least once // See KT-6283 // NB: it's really important to do it for non-empty body which is not a function literal // If it's a function literal, it appears always unused so it's no matter what we do at this // point if (body != null) { // We should take data flow info from the first jump point, // but without affecting changing variables dataFlowInfo = dataFlowInfo.and( loopVisitor.clearDataFlowInfoForAssignedLocalVariables( bodyTypeInfo.getJumpFlowInfo())); } return components .dataFlowAnalyzer .checkType( bodyTypeInfo.replaceType(components.builtIns.getUnitType()), expression, contextWithExpectedType) .replaceDataFlowInfo(dataFlowInfo); }
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); }