From 8184bccda1702dc3e6473a29758a8ca00d7bad94 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Mon, 6 Apr 2015 17:52:37 +0300 Subject: [PATCH] Extended loop data flow analysis was implemented. #KT-6283 Fixed. #KT-6284 Fixed. A local descendant of JetTypeInfo added to save separately current data flow info and jump point data flow info together with jump opportunity. Now data flow analysis know about loop bodies that must be executed at least once (do...while, while(true) until the first break/continue). A set of tests for smart casts in and after loops. Existing DoWhile and WhileTrue resolve tests corrected in accordance. --- .../kotlin/resolve/BindingContext.java | 1 + .../calls/smartcasts/DataFlowInfo.java | 3 +- .../ControlStructureTypingVisitor.java | 67 ++++-- .../expressions/ExpressionTypingServices.java | 24 ++- .../ExpressionTypingVisitorDispatcher.java | 19 +- .../types/expressions/LoopTypeInfo.java | 67 ++++++ .../tests/smartCasts/loops/doWhile.kt | 11 + .../tests/smartCasts/loops/doWhile.txt | 4 + .../tests/smartCasts/loops/doWhileBreak.kt | 11 + .../tests/smartCasts/loops/doWhileBreak.txt | 4 + .../tests/smartCasts/loops/doWhileContinue.kt | 11 + .../smartCasts/loops/doWhileContinue.txt | 4 + .../smartCasts/loops/doWhileEarlyBreak.kt | 13 ++ .../smartCasts/loops/doWhileEarlyBreak.txt | 5 + .../smartCasts/loops/doWhileEarlyContinue.kt | 13 ++ .../smartCasts/loops/doWhileEarlyContinue.txt | 5 + .../smartCasts/loops/doWhileInCondition.kt | 5 + .../smartCasts/loops/doWhileInCondition.txt | 3 + .../loops/doWhileInConditionWithBreak.kt | 9 + .../loops/doWhileInConditionWithBreak.txt | 4 + .../tests/smartCasts/loops/doWhileLiteral.kt | 10 + .../tests/smartCasts/loops/doWhileLiteral.txt | 4 + .../smartCasts/loops/doWhileNotNullBreak.kt | 10 + .../smartCasts/loops/doWhileNotNullBreak.txt | 4 + .../tests/smartCasts/loops/doWhileNull.kt | 5 + .../tests/smartCasts/loops/doWhileNull.txt | 3 + .../smartCasts/loops/doWhileNullWithBreak.kt | 9 + .../smartCasts/loops/doWhileNullWithBreak.txt | 4 + .../smartCasts/loops/ifBlockInsideDoWhile.kt | 12 ++ .../smartCasts/loops/ifBlockInsideDoWhile.txt | 3 + .../loops/ifElseBlockInsideDoWhile.kt | 15 ++ .../loops/ifElseBlockInsideDoWhile.txt | 3 + .../tests/smartCasts/loops/ifInsideDoWhile.kt | 9 + .../smartCasts/loops/ifInsideDoWhile.txt | 3 + .../tests/smartCasts/loops/nestedDoWhile.kt | 13 ++ .../tests/smartCasts/loops/nestedDoWhile.txt | 4 + .../loops/nestedDoWhileWithLongContinue.kt | 14 ++ .../loops/nestedDoWhileWithLongContinue.txt | 4 + .../tests/smartCasts/loops/nestedLoops.kt | 17 ++ .../tests/smartCasts/loops/nestedLoops.txt | 4 + .../smartCasts/loops/nestedLoopsShort.kt | 15 ++ .../smartCasts/loops/nestedLoopsShort.txt | 4 + .../smartCasts/loops/nestedLoopsWithBreak.kt | 18 ++ .../smartCasts/loops/nestedLoopsWithBreak.txt | 4 + .../loops/nestedLoopsWithLongBreak.kt | 20 ++ .../loops/nestedLoopsWithLongBreak.txt | 4 + .../loops/nestedLoopsWithLongContinue.kt | 19 ++ .../loops/nestedLoopsWithLongContinue.txt | 4 + .../smartCasts/loops/useInsideDoWhile.kt | 9 + .../smartCasts/loops/useInsideDoWhile.txt | 3 + .../smartCasts/loops/whileInCondition.kt | 6 + .../smartCasts/loops/whileInCondition.txt | 3 + .../loops/whileInConditionWithBreak.kt | 9 + .../loops/whileInConditionWithBreak.txt | 4 + .../tests/smartCasts/loops/whileNull.kt | 5 + .../tests/smartCasts/loops/whileNull.txt | 3 + .../smartCasts/loops/whileNullWithBreak.kt | 9 + .../smartCasts/loops/whileNullWithBreak.txt | 4 + .../tests/smartCasts/loops/whileSimple.kt | 10 + .../tests/smartCasts/loops/whileSimple.txt | 4 + .../tests/smartCasts/loops/whileTrivial.kt | 11 + .../tests/smartCasts/loops/whileTrivial.txt | 4 + .../tests/smartCasts/loops/whileTrue.kt | 12 ++ .../tests/smartCasts/loops/whileTrue.txt | 4 + .../smartCasts/loops/whileTrueBreakReturn.kt | 12 ++ .../smartCasts/loops/whileTrueBreakReturn.txt | 4 + .../smartCasts/loops/whileTrueEarlyBreak.kt | 11 + .../smartCasts/loops/whileTrueEarlyBreak.txt | 4 + .../tests/smartCasts/loops/whileTrueReturn.kt | 13 ++ .../smartCasts/loops/whileTrueReturn.txt | 4 + .../checkers/JetDiagnosticsTestGenerated.java | 201 ++++++++++++++++++ .../resolve/partialBodyResolve/DoWhile.dump | 2 +- .../resolve/partialBodyResolve/WhileTrue.dump | 2 +- 73 files changed, 850 insertions(+), 24 deletions(-) create mode 100644 compiler/frontend/src/org/jetbrains/kotlin/types/expressions/LoopTypeInfo.java create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/doWhile.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/doWhile.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/doWhileBreak.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/doWhileBreak.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/doWhileContinue.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/doWhileContinue.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/doWhileEarlyBreak.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/doWhileEarlyBreak.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/doWhileEarlyContinue.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/doWhileEarlyContinue.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/doWhileInCondition.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/doWhileInCondition.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/doWhileInConditionWithBreak.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/doWhileInConditionWithBreak.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/doWhileLiteral.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/doWhileLiteral.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/doWhileNotNullBreak.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/doWhileNotNullBreak.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/doWhileNull.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/doWhileNull.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/doWhileNullWithBreak.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/doWhileNullWithBreak.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/ifBlockInsideDoWhile.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/ifBlockInsideDoWhile.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/ifElseBlockInsideDoWhile.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/ifElseBlockInsideDoWhile.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/ifInsideDoWhile.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/ifInsideDoWhile.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/nestedDoWhile.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/nestedDoWhile.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/nestedDoWhileWithLongContinue.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/nestedDoWhileWithLongContinue.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoops.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoops.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoopsShort.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoopsShort.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoopsWithBreak.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoopsWithBreak.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoopsWithLongBreak.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoopsWithLongBreak.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoopsWithLongContinue.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoopsWithLongContinue.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/useInsideDoWhile.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/useInsideDoWhile.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/whileInCondition.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/whileInCondition.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/whileInConditionWithBreak.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/whileInConditionWithBreak.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/whileNull.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/whileNull.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/whileNullWithBreak.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/whileNullWithBreak.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/whileSimple.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/whileSimple.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/whileTrivial.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/whileTrivial.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/whileTrue.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/whileTrue.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/whileTrueBreakReturn.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/whileTrueBreakReturn.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/whileTrueEarlyBreak.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/whileTrueEarlyBreak.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/whileTrueReturn.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/whileTrueReturn.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java index 26044c6e63d..66b56a1b3f2 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java @@ -83,6 +83,7 @@ public interface BindingContext { WritableSlice TYPE = Slices.createSimpleSlice(); WritableSlice EXPRESSION_TYPE = new BasicWritableSlice(DO_NOTHING); + WritableSlice EXPRESSION_JUMP_OUT_POSSIBLE = new BasicWritableSlice(DO_NOTHING); WritableSlice EXPECTED_EXPRESSION_TYPE = new BasicWritableSlice(DO_NOTHING); WritableSlice EXPECTED_RETURN_TYPE = new BasicWritableSlice(DO_NOTHING); WritableSlice EXPRESSION_DATA_FLOW_INFO = new BasicWritableSlice(DO_NOTHING); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowInfo.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowInfo.java index 3bf785cf50f..2bdd2726205 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowInfo.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowInfo.java @@ -25,8 +25,7 @@ import java.util.Map; import java.util.Set; public interface DataFlowInfo { - DataFlowInfo EMPTY = new DelegatingDataFlowInfo(null, ImmutableMap.of(), - DelegatingDataFlowInfo.newTypeInfo()); + DataFlowInfo EMPTY = new DelegatingDataFlowInfo(null, ImmutableMap.of(), DelegatingDataFlowInfo.newTypeInfo()); @NotNull Map getCompleteNullabilityInfo(); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java index c571fbeae46..457efa4d49d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java @@ -22,9 +22,11 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.kotlin.JetNodeTypes; import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.diagnostics.Errors; +import org.jetbrains.kotlin.lexer.JetTokens; import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.resolve.*; import org.jetbrains.kotlin.resolve.calls.model.MutableDataFlowInfoForArguments; @@ -100,8 +102,11 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { if (elseBranch == null) { if (thenBranch != null) { - return getTypeInfoWhenOnlyOneBranchIsPresent( + LoopTypeInfo result = getTypeInfoWhenOnlyOneBranchIsPresent( thenBranch, thenScope, thenInfo, elseInfo, contextWithExpectedType, ifExpression, isStatement); + // If jump was possible, take condition check info as the jump info + return result.isJumpOutPossible() ? + new LoopTypeInfo(result.getType(), result.getDataFlowInfo(), true, conditionDataFlowInfo) : result; } return DataFlowUtils.checkImplicitCast(components.builtIns.getUnitType(), ifExpression, contextWithExpectedType, isStatement, thenInfo.or(elseInfo)); @@ -121,8 +126,12 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { Lists.newArrayList(false, false), contextWithExpectedType, dataFlowInfoForArguments); - JetTypeInfo thenTypeInfo = BindingContextUtils.getRecordedTypeInfo(thenBranch, context.trace.getBindingContext()); - JetTypeInfo elseTypeInfo = BindingContextUtils.getRecordedTypeInfo(elseBranch, context.trace.getBindingContext()); + BindingContext bindingContext = context.trace.getBindingContext(); + JetTypeInfo thenTypeInfo = BindingContextUtils.getRecordedTypeInfo(thenBranch, bindingContext); + JetTypeInfo elseTypeInfo = BindingContextUtils.getRecordedTypeInfo(elseBranch, bindingContext); + Boolean thenLoopBreakContinuePossible = bindingContext.get(BindingContext.EXPRESSION_JUMP_OUT_POSSIBLE, thenBranch); + Boolean elseLoopBreakContinuePossible = bindingContext.get(BindingContext.EXPRESSION_JUMP_OUT_POSSIBLE, elseBranch); + boolean loopBreakContinuePossible = (thenLoopBreakContinuePossible == Boolean.TRUE || elseLoopBreakContinuePossible == Boolean.TRUE); assert thenTypeInfo != null : "'Then' branch of if expression was not processed: " + ifExpression; assert elseTypeInfo != null : "'Else' branch of if expression was not processed: " + ifExpression; @@ -149,11 +158,13 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { } JetType resultType = resolvedCall.getResultingDescriptor().getReturnType(); - return DataFlowUtils.checkImplicitCast(resultType, ifExpression, contextWithExpectedType, isStatement, resultDataFlowInfo); + JetTypeInfo result = DataFlowUtils.checkImplicitCast(resultType, ifExpression, contextWithExpectedType, isStatement, resultDataFlowInfo); + // If break or continue was possible, take condition check info as the jump info + return new LoopTypeInfo(result.getType(), result.getDataFlowInfo(), loopBreakContinuePossible, conditionDataFlowInfo); } @NotNull - private JetTypeInfo getTypeInfoWhenOnlyOneBranchIsPresent( + private LoopTypeInfo getTypeInfoWhenOnlyOneBranchIsPresent( @NotNull JetExpression presentBranch, @NotNull WritableScopeImpl presentScope, @NotNull DataFlowInfo presentInfo, @@ -164,7 +175,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { ) { ExpressionTypingContext newContext = context.replaceDataFlowInfo(presentInfo).replaceExpectedType(NO_EXPECTED_TYPE) .replaceContextDependency(INDEPENDENT); - JetTypeInfo typeInfo = components.expressionTypingServices.getBlockReturnedTypeWithWritableScope( + LoopTypeInfo typeInfo = components.expressionTypingServices.getBlockReturnedTypeWithWritableScope( presentScope, Collections.singletonList(presentBranch), CoercionStrategy.NO_COERCION, newContext); JetType type = typeInfo.getType(); DataFlowInfo dataFlowInfo; @@ -174,7 +185,8 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { dataFlowInfo = typeInfo.getDataFlowInfo().or(otherInfo); } JetType typeForIfExpression = DataFlowUtils.checkType(components.builtIns.getUnitType(), ifExpression, context); - return DataFlowUtils.checkImplicitCast(typeForIfExpression, ifExpression, context, isStatement, dataFlowInfo); + JetTypeInfo result = DataFlowUtils.checkImplicitCast(typeForIfExpression, ifExpression, context, isStatement, dataFlowInfo); + return new LoopTypeInfo(result.getType(), result.getDataFlowInfo(), typeInfo.isJumpOutPossible(), typeInfo.getJumpFlowInfo()); } @Override @@ -182,26 +194,42 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { return visitWhileExpression(expression, context, false); } + private static boolean isTrueConstant(JetExpression condition) { + return (condition != null && condition.getNode().getElementType() == JetNodeTypes.BOOLEAN_CONSTANT && + condition.getNode().findChildByType(JetTokens.TRUE_KEYWORD) != null); + } + public JetTypeInfo visitWhileExpression(JetWhileExpression expression, ExpressionTypingContext contextWithExpectedType, boolean isStatement) { if (!isStatement) return DataFlowUtils.illegalStatementType(expression, contextWithExpectedType, facade); ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE).replaceContextDependency( INDEPENDENT); JetExpression condition = expression.getCondition(); + // Extract data flow info from condition itself without taking value into account DataFlowInfo dataFlowInfo = checkCondition(context.scope, condition, context); JetExpression body = expression.getBody(); + LoopTypeInfo bodyTypeInfo = null; if (body != null) { WritableScopeImpl scopeToExtend = newWritableScopeImpl(context, "Scope extended in while's condition"); DataFlowInfo conditionInfo = DataFlowUtils.extractDataFlowInfoFromCondition(condition, true, context).and(dataFlowInfo); - components.expressionTypingServices.getBlockReturnedTypeWithWritableScope( + bodyTypeInfo = components.expressionTypingServices.getBlockReturnedTypeWithWritableScope( scopeToExtend, Collections.singletonList(body), CoercionStrategy.NO_COERCION, context.replaceDataFlowInfo(conditionInfo)); } + // Condition is false at this point only if there is no jumps outside if (!containsJumpOutOfLoop(expression, context)) { dataFlowInfo = DataFlowUtils.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 (bodyTypeInfo != null && isTrueConstant(condition)) { + dataFlowInfo = dataFlowInfo.and(bodyTypeInfo.getJumpFlowInfo()); + } return DataFlowUtils.checkType(components.builtIns.getUnitType(), expression, contextWithExpectedType, dataFlowInfo); } @@ -248,6 +276,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { public JetTypeInfo visitDoWhileExpression(@NotNull JetDoWhileExpression expression, ExpressionTypingContext context) { return visitDoWhileExpression(expression, context, false); } + public JetTypeInfo visitDoWhileExpression(JetDoWhileExpression expression, ExpressionTypingContext contextWithExpectedType, boolean isStatement) { if (!isStatement) return DataFlowUtils.illegalStatementType(expression, contextWithExpectedType, facade); @@ -255,13 +284,14 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE).replaceContextDependency(INDEPENDENT); JetExpression body = expression.getBody(); JetScope conditionScope = context.scope; + LoopTypeInfo bodyTypeInfo = null; if (body instanceof JetFunctionLiteralExpression) { JetFunctionLiteralExpression function = (JetFunctionLiteralExpression) body; JetFunctionLiteral functionLiteral = function.getFunctionLiteral(); if (!functionLiteral.hasParameterSpecification()) { WritableScope writableScope = newWritableScopeImpl(context, "do..while body scope"); conditionScope = writableScope; - components.expressionTypingServices.getBlockReturnedTypeWithWritableScope( + bodyTypeInfo = components.expressionTypingServices.getBlockReturnedTypeWithWritableScope( writableScope, functionLiteral.getBodyExpression().getStatements(), CoercionStrategy.NO_COERCION, context); context.trace.record(BindingContext.BLOCK, function); } @@ -279,18 +309,25 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { else { block = Collections.singletonList(body); } - components.expressionTypingServices.getBlockReturnedTypeWithWritableScope( + bodyTypeInfo = components.expressionTypingServices.getBlockReturnedTypeWithWritableScope( writableScope, block, CoercionStrategy.NO_COERCION, context); } JetExpression 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 = DataFlowUtils.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 + if (bodyTypeInfo != null) { + dataFlowInfo = dataFlowInfo.and(bodyTypeInfo.getJumpFlowInfo()); + } return DataFlowUtils.checkType(components.builtIns.getUnitType(), expression, contextWithExpectedType, dataFlowInfo); } @@ -510,15 +547,17 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { } @Override - public JetTypeInfo visitBreakExpression(@NotNull JetBreakExpression expression, ExpressionTypingContext context) { + public LoopTypeInfo visitBreakExpression(@NotNull JetBreakExpression expression, ExpressionTypingContext context) { LabelResolver.INSTANCE.resolveControlLabel(expression, context); - return DataFlowUtils.checkType(components.builtIns.getNothingType(), expression, context, context.dataFlowInfo); + JetTypeInfo result = DataFlowUtils.checkType(components.builtIns.getNothingType(), expression, context, context.dataFlowInfo); + return new LoopTypeInfo(result.getType(), result.getDataFlowInfo(), true, result.getDataFlowInfo()); } @Override - public JetTypeInfo visitContinueExpression(@NotNull JetContinueExpression expression, ExpressionTypingContext context) { + public LoopTypeInfo visitContinueExpression(@NotNull JetContinueExpression expression, ExpressionTypingContext context) { LabelResolver.INSTANCE.resolveControlLabel(expression, context); - return DataFlowUtils.checkType(components.builtIns.getNothingType(), expression, context, context.dataFlowInfo); + JetTypeInfo result = DataFlowUtils.checkType(components.builtIns.getNothingType(), expression, context, context.dataFlowInfo); + return new LoopTypeInfo(result.getType(), result.getDataFlowInfo(), true, result.getDataFlowInfo()); } @NotNull diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingServices.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingServices.java index db9dc73d048..d777cb479e2 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingServices.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingServices.java @@ -305,22 +305,26 @@ public class ExpressionTypingServices { /** * 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. + * 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*/ JetTypeInfo getBlockReturnedTypeWithWritableScope( + /*package*/ LoopTypeInfo getBlockReturnedTypeWithWritableScope( @NotNull WritableScope scope, @NotNull List block, @NotNull CoercionStrategy coercionStrategyForLastExpression, @NotNull ExpressionTypingContext context ) { if (block.isEmpty()) { - return JetTypeInfo.create(builtIns.getUnitType(), context.dataFlowInfo); + return new LoopTypeInfo(builtIns.getUnitType(), context.dataFlowInfo); } ExpressionTypingInternals blockLevelVisitor = ExpressionTypingVisitorDispatcher.createForBlock(expressionTypingComponents, scope); ExpressionTypingContext newContext = context.replaceScope(scope).replaceExpectedType(NO_EXPECTED_TYPE); JetTypeInfo result = JetTypeInfo.create(null, context.dataFlowInfo); + // Jump point data flow info + DataFlowInfo beforeJumpInfo = newContext.dataFlowInfo; + boolean jumpOutPossible = false; for (Iterator iterator = block.iterator(); iterator.hasNext(); ) { JetElement statement = iterator.next(); if (!(statement instanceof JetExpression)) { @@ -338,12 +342,24 @@ public class ExpressionTypingServices { } DataFlowInfo newDataFlowInfo = result.getDataFlowInfo(); + // If jump is not possible, we take new data flow info before jump + if (!jumpOutPossible) { + if (result instanceof LoopTypeInfo) { + LoopTypeInfo loopTypeInfo = (LoopTypeInfo) result; + beforeJumpInfo = loopTypeInfo.getJumpFlowInfo(); + jumpOutPossible = loopTypeInfo.isJumpOutPossible(); + } + else { + beforeJumpInfo = newDataFlowInfo; + } + } if (newDataFlowInfo != context.dataFlowInfo) { newContext = newContext.replaceDataFlowInfo(newDataFlowInfo); + // We take current data flow info if jump there is not possible } blockLevelVisitor = ExpressionTypingVisitorDispatcher.createForBlock(expressionTypingComponents, scope); } - return result; + return new LoopTypeInfo(result.getType(), result.getDataFlowInfo(), jumpOutPossible, beforeJumpInfo); } private JetTypeInfo getTypeOfLastExpressionInBlock( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorDispatcher.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorDispatcher.java index 01e205d6bc2..05420730df0 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorDispatcher.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorDispatcher.java @@ -28,6 +28,7 @@ import org.jetbrains.kotlin.resolve.BindingContextUtils; import org.jetbrains.kotlin.resolve.scopes.WritableScope; import org.jetbrains.kotlin.types.DeferredType; import org.jetbrains.kotlin.types.ErrorUtils; +import org.jetbrains.kotlin.types.JetType; import org.jetbrains.kotlin.types.JetTypeInfo; import org.jetbrains.kotlin.util.ReenteringLazyValueComputationException; import org.jetbrains.kotlin.utils.KotlinFrontEndException; @@ -140,8 +141,16 @@ public class ExpressionTypingVisitorDispatcher extends JetVisitorp.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/doWhile.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhile.txt new file mode 100644 index 00000000000..9fe4590e72b --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhile.txt @@ -0,0 +1,4 @@ +package + +public fun foo(/*0*/ p: kotlin.String?): kotlin.Int +internal fun x(): kotlin.Boolean diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileBreak.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileBreak.kt new file mode 100644 index 00000000000..93e6a31aa59 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileBreak.kt @@ -0,0 +1,11 @@ +fun x(): Boolean { return true } + +public fun foo(p: String?): Int { + // See KT-6283 + do { + p!!.length() + if (p == "abc") break + } while (!x()) + // p should be smart casted despite of break + return p.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileBreak.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileBreak.txt new file mode 100644 index 00000000000..9fe4590e72b --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileBreak.txt @@ -0,0 +1,4 @@ +package + +public fun foo(/*0*/ p: kotlin.String?): kotlin.Int +internal fun x(): kotlin.Boolean diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileContinue.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileContinue.kt new file mode 100644 index 00000000000..6ec3d7cd88d --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileContinue.kt @@ -0,0 +1,11 @@ +fun x(): Boolean { return true } + +public fun foo(p: String?): Int { + // See KT-6283 + do { + p!!.length() + if (p == "abc") continue + } while (!x()) + // p should be smart casted despite of continue + return p.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileContinue.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileContinue.txt new file mode 100644 index 00000000000..9fe4590e72b --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileContinue.txt @@ -0,0 +1,4 @@ +package + +public fun foo(/*0*/ p: kotlin.String?): kotlin.Int +internal fun x(): kotlin.Boolean diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileEarlyBreak.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileEarlyBreak.kt new file mode 100644 index 00000000000..d29465267e6 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileEarlyBreak.kt @@ -0,0 +1,13 @@ +fun x(): Boolean { return true } + +fun y(): Boolean { return false } + +public fun foo(p: String?): Int { + do { + if (y()) break + // We do not always reach this statement + p!!.length() + } while (!x()) + // Here we have do while loop but p is still nullable due to break before + return p.length() +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileEarlyBreak.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileEarlyBreak.txt new file mode 100644 index 00000000000..cbdffcadc1c --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileEarlyBreak.txt @@ -0,0 +1,5 @@ +package + +public fun foo(/*0*/ p: kotlin.String?): kotlin.Int +internal fun x(): kotlin.Boolean +internal fun y(): kotlin.Boolean diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileEarlyContinue.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileEarlyContinue.kt new file mode 100644 index 00000000000..834c9810bbd --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileEarlyContinue.kt @@ -0,0 +1,13 @@ +fun x(): Boolean { return true } + +fun y(): Boolean { return false } + +public fun foo(p: String?): Int { + do { + if (y()) continue + // We do not always reach this statement + p!!.length() + } while (!x()) + // Here we have do while loop but p is still nullable due to continue before + return p.length() +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileEarlyContinue.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileEarlyContinue.txt new file mode 100644 index 00000000000..cbdffcadc1c --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileEarlyContinue.txt @@ -0,0 +1,5 @@ +package + +public fun foo(/*0*/ p: kotlin.String?): kotlin.Int +internal fun x(): kotlin.Boolean +internal fun y(): kotlin.Boolean diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileInCondition.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileInCondition.kt new file mode 100644 index 00000000000..37f53f7c59a --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileInCondition.kt @@ -0,0 +1,5 @@ +fun foo(s: String?): Int { + do { + } while (s!!.length() > 0) + return s.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileInCondition.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileInCondition.txt new file mode 100644 index 00000000000..2e3387d2c3b --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileInCondition.txt @@ -0,0 +1,3 @@ +package + +internal fun foo(/*0*/ s: kotlin.String?): kotlin.Int diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileInConditionWithBreak.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileInConditionWithBreak.kt new file mode 100644 index 00000000000..c72697a2b63 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileInConditionWithBreak.kt @@ -0,0 +1,9 @@ +fun bar(): Boolean { return true } + +fun foo(s: String?): Int { + do { + if (bar()) break + } while (s!!.length() > 0) + // This call is unsafe due to break + return s.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileInConditionWithBreak.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileInConditionWithBreak.txt new file mode 100644 index 00000000000..c01aa1b5f3b --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileInConditionWithBreak.txt @@ -0,0 +1,4 @@ +package + +internal fun bar(): kotlin.Boolean +internal fun foo(/*0*/ s: kotlin.String?): kotlin.Int diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileLiteral.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileLiteral.kt new file mode 100644 index 00000000000..e392871c41c --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileLiteral.kt @@ -0,0 +1,10 @@ +fun x(): Boolean { return true } + +public fun foo(p: String?): Int { + // Exotic variant with unused literal + do { -> + p!!.length() + } while (!x()) + // Literal is not called so p.length() is unsafe + return p.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileLiteral.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileLiteral.txt new file mode 100644 index 00000000000..9fe4590e72b --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileLiteral.txt @@ -0,0 +1,4 @@ +package + +public fun foo(/*0*/ p: kotlin.String?): kotlin.Int +internal fun x(): kotlin.Boolean diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileNotNullBreak.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileNotNullBreak.kt new file mode 100644 index 00000000000..1afadbba96d --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileNotNullBreak.kt @@ -0,0 +1,10 @@ +fun x(): Boolean { return true } + +public fun foo(p: String?): Int { + // See KT-6283 + do { + if (p != null) break + } while (!x()) + // p can be null despite of the break + return p.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileNotNullBreak.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileNotNullBreak.txt new file mode 100644 index 00000000000..9fe4590e72b --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileNotNullBreak.txt @@ -0,0 +1,4 @@ +package + +public fun foo(/*0*/ p: kotlin.String?): kotlin.Int +internal fun x(): kotlin.Boolean diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileNull.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileNull.kt new file mode 100644 index 00000000000..3661b746aa9 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileNull.kt @@ -0,0 +1,5 @@ +fun foo(s: String?): Int { + do { + } while (s==null) + return s.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileNull.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileNull.txt new file mode 100644 index 00000000000..2e3387d2c3b --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileNull.txt @@ -0,0 +1,3 @@ +package + +internal fun foo(/*0*/ s: kotlin.String?): kotlin.Int diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileNullWithBreak.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileNullWithBreak.kt new file mode 100644 index 00000000000..04aa0fa09f2 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileNullWithBreak.kt @@ -0,0 +1,9 @@ +fun bar(): Boolean { return true } + +fun foo(s: String?): Int { + do { + if (bar()) break + } while (s==null) + // This call is unsafe due to break + return s.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileNullWithBreak.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileNullWithBreak.txt new file mode 100644 index 00000000000..c01aa1b5f3b --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileNullWithBreak.txt @@ -0,0 +1,4 @@ +package + +internal fun bar(): kotlin.Boolean +internal fun foo(/*0*/ s: kotlin.String?): kotlin.Int diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/ifBlockInsideDoWhile.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/ifBlockInsideDoWhile.kt new file mode 100644 index 00000000000..065de3bb90c --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/ifBlockInsideDoWhile.kt @@ -0,0 +1,12 @@ +public fun foo(p: String?, y: String?): Int { + do { + // After the check, smart cast should work + if (y == null) { + "null".toString() + break + } + y.length() + p!!.length() + } while (true) + return y?.length() ?: -1 +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/ifBlockInsideDoWhile.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/ifBlockInsideDoWhile.txt new file mode 100644 index 00000000000..435e072b6ec --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/ifBlockInsideDoWhile.txt @@ -0,0 +1,3 @@ +package + +public fun foo(/*0*/ p: kotlin.String?, /*1*/ y: kotlin.String?): kotlin.Int diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/ifElseBlockInsideDoWhile.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/ifElseBlockInsideDoWhile.kt new file mode 100644 index 00000000000..147d24f3769 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/ifElseBlockInsideDoWhile.kt @@ -0,0 +1,15 @@ +public fun foo(x: String?, y: String?): Int { + do { + // After the check, smart cast should work + if (x != null) { + if (x == "abc") break + y!!.length() + } else { + y!!.length() + } + // y!! in both branches + y.length() + } while (true) + // break is possible before so !! is necessary + return y!!.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/ifElseBlockInsideDoWhile.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/ifElseBlockInsideDoWhile.txt new file mode 100644 index 00000000000..74faaa4f9c5 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/ifElseBlockInsideDoWhile.txt @@ -0,0 +1,3 @@ +package + +public fun foo(/*0*/ x: kotlin.String?, /*1*/ y: kotlin.String?): kotlin.Int diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/ifInsideDoWhile.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/ifInsideDoWhile.kt new file mode 100644 index 00000000000..95255ffb557 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/ifInsideDoWhile.kt @@ -0,0 +1,9 @@ +public fun foo(p: String?, y: String?): Int { + do { + // After the check, smart cast should work + if (y == null) break + y.length() + p!!.length() + } while (true) + return y?.length() ?: -1 +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/ifInsideDoWhile.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/ifInsideDoWhile.txt new file mode 100644 index 00000000000..435e072b6ec --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/ifInsideDoWhile.txt @@ -0,0 +1,3 @@ +package + +public fun foo(/*0*/ p: kotlin.String?, /*1*/ y: kotlin.String?): kotlin.Int diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/nestedDoWhile.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/nestedDoWhile.kt new file mode 100644 index 00000000000..198527ac83b --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/nestedDoWhile.kt @@ -0,0 +1,13 @@ +fun x(): Boolean { return true } + +public fun foo(p: String?, r: String?): Int { + do { + do { + p!!.length() + } while (r == null) + } while (!x()) + // Auto cast possible + r.length() + // Auto cast possible + return p.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/nestedDoWhile.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/nestedDoWhile.txt new file mode 100644 index 00000000000..9c02848f7e7 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/nestedDoWhile.txt @@ -0,0 +1,4 @@ +package + +public fun foo(/*0*/ p: kotlin.String?, /*1*/ r: kotlin.String?): kotlin.Int +internal fun x(): kotlin.Boolean diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/nestedDoWhileWithLongContinue.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/nestedDoWhileWithLongContinue.kt new file mode 100644 index 00000000000..9a124d12905 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/nestedDoWhileWithLongContinue.kt @@ -0,0 +1,14 @@ +fun x(): Boolean { return true } + +public fun foo(p: String?, r: String?): Int { + @outer do { + do { + p!!.length() + if (!x()) continue@outer + } while (r == null) + } while (!x()) + // Auto cast NOT possible due to long continue + r.length() + // Auto cast possible + return p.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/nestedDoWhileWithLongContinue.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/nestedDoWhileWithLongContinue.txt new file mode 100644 index 00000000000..9c02848f7e7 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/nestedDoWhileWithLongContinue.txt @@ -0,0 +1,4 @@ +package + +public fun foo(/*0*/ p: kotlin.String?, /*1*/ r: kotlin.String?): kotlin.Int +internal fun x(): kotlin.Boolean diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoops.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoops.kt new file mode 100644 index 00000000000..3ada0b572ec --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoops.kt @@ -0,0 +1,17 @@ +fun x(): Boolean { return true } + +public fun foo(p: String?, r: String?, q: String?): Int { + while(true) { + q!!.length() + do { + do { + p!!.length() + } while (!x()) + } while (r == null) + if (!x()) break + } + // Smart cast is possible everywhere + r.length() + q.length() + return p.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoops.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoops.txt new file mode 100644 index 00000000000..5551ecc7235 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoops.txt @@ -0,0 +1,4 @@ +package + +public fun foo(/*0*/ p: kotlin.String?, /*1*/ r: kotlin.String?, /*2*/ q: kotlin.String?): kotlin.Int +internal fun x(): kotlin.Boolean diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoopsShort.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoopsShort.kt new file mode 100644 index 00000000000..df00140b236 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoopsShort.kt @@ -0,0 +1,15 @@ +fun x(): Boolean { return true } + +public fun foo(p: String?, r: String?, q: String?): Int { + while(true) { + q!!.length() + do { + p!!.length() + } while (r == null) + if (!x()) break + } + // Smart cast is possible everywhere + r.length() + q.length() + return p.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoopsShort.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoopsShort.txt new file mode 100644 index 00000000000..5551ecc7235 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoopsShort.txt @@ -0,0 +1,4 @@ +package + +public fun foo(/*0*/ p: kotlin.String?, /*1*/ r: kotlin.String?, /*2*/ q: kotlin.String?): kotlin.Int +internal fun x(): kotlin.Boolean diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoopsWithBreak.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoopsWithBreak.kt new file mode 100644 index 00000000000..2f6e3ee37cb --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoopsWithBreak.kt @@ -0,0 +1,18 @@ +fun x(): Boolean { return true } + +public fun foo(p: String?, r: String?, q: String?): Int { + while(true) { + q!!.length() + do { + while(true) { + p!!.length() + if (x()) break + } + } while (r == null) + if (!x()) break + } + // Smart cast is possible everywhere + r.length() + q.length() + return p.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoopsWithBreak.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoopsWithBreak.txt new file mode 100644 index 00000000000..5551ecc7235 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoopsWithBreak.txt @@ -0,0 +1,4 @@ +package + +public fun foo(/*0*/ p: kotlin.String?, /*1*/ r: kotlin.String?, /*2*/ q: kotlin.String?): kotlin.Int +internal fun x(): kotlin.Boolean diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoopsWithLongBreak.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoopsWithLongBreak.kt new file mode 100644 index 00000000000..0291528e703 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoopsWithLongBreak.kt @@ -0,0 +1,20 @@ +fun x(p: String): Boolean { return p == "abc" } + +public fun foo(p: String?, r: String?, q: String?): Int { + while(true) { + q!!.length() + @loop do { + while(true) { + p!!.length() + if (x(p)) break@loop + if (x(q)) break + } + } while (r == null) + if (!x(p)) break + } + // Long break allows r == null + r.length() + // Smart cast is possible + q.length() + return p.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoopsWithLongBreak.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoopsWithLongBreak.txt new file mode 100644 index 00000000000..f1669b1bb6b --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoopsWithLongBreak.txt @@ -0,0 +1,4 @@ +package + +public fun foo(/*0*/ p: kotlin.String?, /*1*/ r: kotlin.String?, /*2*/ q: kotlin.String?): kotlin.Int +internal fun x(/*0*/ p: kotlin.String): kotlin.Boolean diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoopsWithLongContinue.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoopsWithLongContinue.kt new file mode 100644 index 00000000000..db10ed4a868 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoopsWithLongContinue.kt @@ -0,0 +1,19 @@ +fun x(): Boolean { return true } + +public fun foo(p: String?, r: String?, q: String?): Int { + @outer while(true) { + q!!.length() + do { + if (x()) continue@outer + do { + p!!.length() + } while (!x()) + } while (r == null) + if (!x()) break + } + // Smart cast is possible only for q + q.length() + // But not possible for the others + r.length() + return p.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoopsWithLongContinue.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoopsWithLongContinue.txt new file mode 100644 index 00000000000..5551ecc7235 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoopsWithLongContinue.txt @@ -0,0 +1,4 @@ +package + +public fun foo(/*0*/ p: kotlin.String?, /*1*/ r: kotlin.String?, /*2*/ q: kotlin.String?): kotlin.Int +internal fun x(): kotlin.Boolean diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/useInsideDoWhile.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/useInsideDoWhile.kt new file mode 100644 index 00000000000..16f6a8ee1c1 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/useInsideDoWhile.kt @@ -0,0 +1,9 @@ +public fun foo(p: String?, y: String?): Int { + do { + // After this !!, y. should be smartcasted in loop as well as outside + y!!.length() + if (p == null) break + y.length() + } while (true) + return y.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/useInsideDoWhile.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/useInsideDoWhile.txt new file mode 100644 index 00000000000..435e072b6ec --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/useInsideDoWhile.txt @@ -0,0 +1,3 @@ +package + +public fun foo(/*0*/ p: kotlin.String?, /*1*/ y: kotlin.String?): kotlin.Int diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/whileInCondition.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/whileInCondition.kt new file mode 100644 index 00000000000..3227b106fc1 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/whileInCondition.kt @@ -0,0 +1,6 @@ +fun foo(s: String?): Int { + while (s!!.length() > 0) { + s.length() + } + return s.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/whileInCondition.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/whileInCondition.txt new file mode 100644 index 00000000000..2e3387d2c3b --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/whileInCondition.txt @@ -0,0 +1,3 @@ +package + +internal fun foo(/*0*/ s: kotlin.String?): kotlin.Int diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/whileInConditionWithBreak.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/whileInConditionWithBreak.kt new file mode 100644 index 00000000000..4a4b48bc8f3 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/whileInConditionWithBreak.kt @@ -0,0 +1,9 @@ +fun bar(): Boolean { return true } + +fun foo(s: String?): Int { + while (s!!.length() > 0) { + s.length() + if (bar()) break + } + return s.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/whileInConditionWithBreak.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/whileInConditionWithBreak.txt new file mode 100644 index 00000000000..c01aa1b5f3b --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/whileInConditionWithBreak.txt @@ -0,0 +1,4 @@ +package + +internal fun bar(): kotlin.Boolean +internal fun foo(/*0*/ s: kotlin.String?): kotlin.Int diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/whileNull.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/whileNull.kt new file mode 100644 index 00000000000..8e0032fb9ff --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/whileNull.kt @@ -0,0 +1,5 @@ +fun foo(s: String?): Int { + while (s==null) { + } + return s.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/whileNull.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/whileNull.txt new file mode 100644 index 00000000000..2e3387d2c3b --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/whileNull.txt @@ -0,0 +1,3 @@ +package + +internal fun foo(/*0*/ s: kotlin.String?): kotlin.Int diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/whileNullWithBreak.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/whileNullWithBreak.kt new file mode 100644 index 00000000000..67fcf190d18 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/whileNullWithBreak.kt @@ -0,0 +1,9 @@ +fun bar(): Boolean { return true } + +fun foo(s: String?): Int { + while (s==null) { + if (bar()) break + } + // Call is unsafe due to break + return s.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/whileNullWithBreak.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/whileNullWithBreak.txt new file mode 100644 index 00000000000..c01aa1b5f3b --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/whileNullWithBreak.txt @@ -0,0 +1,4 @@ +package + +internal fun bar(): kotlin.Boolean +internal fun foo(/*0*/ s: kotlin.String?): kotlin.Int diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/whileSimple.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/whileSimple.kt new file mode 100644 index 00000000000..a288a8f99d2 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/whileSimple.kt @@ -0,0 +1,10 @@ +fun x(): Boolean { return true } + +public fun foo(p: String?): Int { + while(x()) { + p!!.length() + if (x()) break + } + // p is nullable because it's possible loop body is not executed at all + return p.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/whileSimple.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/whileSimple.txt new file mode 100644 index 00000000000..9fe4590e72b --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/whileSimple.txt @@ -0,0 +1,4 @@ +package + +public fun foo(/*0*/ p: kotlin.String?): kotlin.Int +internal fun x(): kotlin.Boolean diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrivial.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrivial.kt new file mode 100644 index 00000000000..54c15c22340 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrivial.kt @@ -0,0 +1,11 @@ +fun x(): Boolean { return true } + +public fun foo(p: String?): Int { + // Like whileTrue but 2 == 2 is in use + while(2 == 2) { + p!!.length() + if (x()) break + } + // Smart cast should not work in this case, see KT-6284 + return p.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrivial.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrivial.txt new file mode 100644 index 00000000000..9fe4590e72b --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrivial.txt @@ -0,0 +1,4 @@ +package + +public fun foo(/*0*/ p: kotlin.String?): kotlin.Int +internal fun x(): kotlin.Boolean diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrue.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrue.kt new file mode 100644 index 00000000000..c2b38d32499 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrue.kt @@ -0,0 +1,12 @@ +fun x(): Boolean { return true } + +public fun foo(p: String?): Int { + // KT-6284 + while(true) { + p!!.length() + if (x()) break + } + // while (true) loop body is executed at least once + // so p is not null here + return p.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrue.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrue.txt new file mode 100644 index 00000000000..9fe4590e72b --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrue.txt @@ -0,0 +1,4 @@ +package + +public fun foo(/*0*/ p: kotlin.String?): kotlin.Int +internal fun x(): kotlin.Boolean diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrueBreakReturn.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrueBreakReturn.kt new file mode 100644 index 00000000000..a5d79f870c6 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrueBreakReturn.kt @@ -0,0 +1,12 @@ +fun x(): Boolean { return true } + +public fun foo(p: String?): Int { + while(true) { + if (x()) break + if (p==null) return -1 + // p is not null + p.length() + } + // p can be null because break is earlier than return + return p.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrueBreakReturn.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrueBreakReturn.txt new file mode 100644 index 00000000000..9fe4590e72b --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrueBreakReturn.txt @@ -0,0 +1,4 @@ +package + +public fun foo(/*0*/ p: kotlin.String?): kotlin.Int +internal fun x(): kotlin.Boolean diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrueEarlyBreak.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrueEarlyBreak.kt new file mode 100644 index 00000000000..963f56cc8d9 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrueEarlyBreak.kt @@ -0,0 +1,11 @@ +fun x(): Boolean { return true } + +public fun foo(p: String?): Int { + while(true) { + if (x()) break + // We do not always reach this statement + p!!.length() + } + // Here we have while (true) loop but p is nullable due to break before + return p.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrueEarlyBreak.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrueEarlyBreak.txt new file mode 100644 index 00000000000..9fe4590e72b --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrueEarlyBreak.txt @@ -0,0 +1,4 @@ +package + +public fun foo(/*0*/ p: kotlin.String?): kotlin.Int +internal fun x(): kotlin.Boolean diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrueReturn.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrueReturn.kt new file mode 100644 index 00000000000..96c9d2a91d0 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrueReturn.kt @@ -0,0 +1,13 @@ +fun x(): Boolean { return true } + +public fun foo(p: String?): Int { + while(true) { + if (p==null) return -1 + if (x()) break + // p is not null + p.length() + } + // while (true) loop body with return is executed at least once + // so p is not null here + return p.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrueReturn.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrueReturn.txt new file mode 100644 index 00000000000..9fe4590e72b --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrueReturn.txt @@ -0,0 +1,4 @@ +package + +public fun foo(/*0*/ p: kotlin.String?): kotlin.Int +internal fun x(): kotlin.Boolean diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java index 197aa4c614a..d3227b44caa 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java @@ -11016,6 +11016,207 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { } } + @TestMetadata("compiler/testData/diagnostics/tests/smartCasts/loops") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Loops extends AbstractJetDiagnosticsTest { + public void testAllFilesPresentInLoops() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/smartCasts/loops"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("doWhile.kt") + public void testDoWhile() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/doWhile.kt"); + doTest(fileName); + } + + @TestMetadata("doWhileBreak.kt") + public void testDoWhileBreak() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/doWhileBreak.kt"); + doTest(fileName); + } + + @TestMetadata("doWhileContinue.kt") + public void testDoWhileContinue() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/doWhileContinue.kt"); + doTest(fileName); + } + + @TestMetadata("doWhileEarlyBreak.kt") + public void testDoWhileEarlyBreak() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/doWhileEarlyBreak.kt"); + doTest(fileName); + } + + @TestMetadata("doWhileEarlyContinue.kt") + public void testDoWhileEarlyContinue() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/doWhileEarlyContinue.kt"); + doTest(fileName); + } + + @TestMetadata("doWhileInCondition.kt") + public void testDoWhileInCondition() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/doWhileInCondition.kt"); + doTest(fileName); + } + + @TestMetadata("doWhileInConditionWithBreak.kt") + public void testDoWhileInConditionWithBreak() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/doWhileInConditionWithBreak.kt"); + doTest(fileName); + } + + @TestMetadata("doWhileLiteral.kt") + public void testDoWhileLiteral() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/doWhileLiteral.kt"); + doTest(fileName); + } + + @TestMetadata("doWhileNotNullBreak.kt") + public void testDoWhileNotNullBreak() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/doWhileNotNullBreak.kt"); + doTest(fileName); + } + + @TestMetadata("doWhileNull.kt") + public void testDoWhileNull() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/doWhileNull.kt"); + doTest(fileName); + } + + @TestMetadata("doWhileNullWithBreak.kt") + public void testDoWhileNullWithBreak() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/doWhileNullWithBreak.kt"); + doTest(fileName); + } + + @TestMetadata("ifBlockInsideDoWhile.kt") + public void testIfBlockInsideDoWhile() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/ifBlockInsideDoWhile.kt"); + doTest(fileName); + } + + @TestMetadata("ifElseBlockInsideDoWhile.kt") + public void testIfElseBlockInsideDoWhile() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/ifElseBlockInsideDoWhile.kt"); + doTest(fileName); + } + + @TestMetadata("ifInsideDoWhile.kt") + public void testIfInsideDoWhile() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/ifInsideDoWhile.kt"); + doTest(fileName); + } + + @TestMetadata("nestedDoWhile.kt") + public void testNestedDoWhile() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/nestedDoWhile.kt"); + doTest(fileName); + } + + @TestMetadata("nestedDoWhileWithLongContinue.kt") + public void testNestedDoWhileWithLongContinue() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/nestedDoWhileWithLongContinue.kt"); + doTest(fileName); + } + + @TestMetadata("nestedLoops.kt") + public void testNestedLoops() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoops.kt"); + doTest(fileName); + } + + @TestMetadata("nestedLoopsShort.kt") + public void testNestedLoopsShort() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoopsShort.kt"); + doTest(fileName); + } + + @TestMetadata("nestedLoopsWithBreak.kt") + public void testNestedLoopsWithBreak() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoopsWithBreak.kt"); + doTest(fileName); + } + + @TestMetadata("nestedLoopsWithLongBreak.kt") + public void testNestedLoopsWithLongBreak() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoopsWithLongBreak.kt"); + doTest(fileName); + } + + @TestMetadata("nestedLoopsWithLongContinue.kt") + public void testNestedLoopsWithLongContinue() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/nestedLoopsWithLongContinue.kt"); + doTest(fileName); + } + + @TestMetadata("useInsideDoWhile.kt") + public void testUseInsideDoWhile() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/useInsideDoWhile.kt"); + doTest(fileName); + } + + @TestMetadata("whileInCondition.kt") + public void testWhileInCondition() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/whileInCondition.kt"); + doTest(fileName); + } + + @TestMetadata("whileInConditionWithBreak.kt") + public void testWhileInConditionWithBreak() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/whileInConditionWithBreak.kt"); + doTest(fileName); + } + + @TestMetadata("whileNull.kt") + public void testWhileNull() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/whileNull.kt"); + doTest(fileName); + } + + @TestMetadata("whileNullWithBreak.kt") + public void testWhileNullWithBreak() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/whileNullWithBreak.kt"); + doTest(fileName); + } + + @TestMetadata("whileSimple.kt") + public void testWhileSimple() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/whileSimple.kt"); + doTest(fileName); + } + + @TestMetadata("whileTrivial.kt") + public void testWhileTrivial() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/whileTrivial.kt"); + doTest(fileName); + } + + @TestMetadata("whileTrue.kt") + public void testWhileTrue() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/whileTrue.kt"); + doTest(fileName); + } + + @TestMetadata("whileTrueBreakReturn.kt") + public void testWhileTrueBreakReturn() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/whileTrueBreakReturn.kt"); + doTest(fileName); + } + + @TestMetadata("whileTrueEarlyBreak.kt") + public void testWhileTrueEarlyBreak() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/whileTrueEarlyBreak.kt"); + doTest(fileName); + } + + @TestMetadata("whileTrueReturn.kt") + public void testWhileTrueReturn() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/whileTrueReturn.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/diagnostics/tests/smartCasts/safecalls") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/idea/testData/resolve/partialBodyResolve/DoWhile.dump b/idea/testData/resolve/partialBodyResolve/DoWhile.dump index 6bdbf4205ea..0629321e3ef 100644 --- a/idea/testData/resolve/partialBodyResolve/DoWhile.dump +++ b/idea/testData/resolve/partialBodyResolve/DoWhile.dump @@ -1,4 +1,4 @@ -Resolve target: value-parameter val p: kotlin.Any? +Resolve target: value-parameter val p: kotlin.Any? smart-cast to kotlin.Any ---------------------------------------------- fun foo(p: Any?) { do { diff --git a/idea/testData/resolve/partialBodyResolve/WhileTrue.dump b/idea/testData/resolve/partialBodyResolve/WhileTrue.dump index d3500d4b3ef..7ce933cb497 100644 --- a/idea/testData/resolve/partialBodyResolve/WhileTrue.dump +++ b/idea/testData/resolve/partialBodyResolve/WhileTrue.dump @@ -1,4 +1,4 @@ -Resolve target: value-parameter val p: kotlin.Any? +Resolve target: value-parameter val p: kotlin.Any? smart-cast to kotlin.Any ---------------------------------------------- fun x(): Boolean{}