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{}