Review fixes for smart casts in / out loops.

LoopTypeInfo became TypeInfoWithJumpInfo rewritten in Kotlin.
Fixed a bug with break inside Elvis inside a loop, a pack of related tests added.
isTrueConstant moved to JetPsiUtil.
This commit is contained in:
Mikhail Glukhikh
2015-04-16 17:07:00 +03:00
parent d5aed62410
commit e97e792674
18 changed files with 172 additions and 115 deletions
@@ -398,6 +398,11 @@ public class JetPsiUtil {
return deparenthesized instanceof JetConstantExpression && deparenthesized.getNode().getElementType() == JetNodeTypes.NULL;
}
public static boolean isTrueConstant(@Nullable JetExpression condition) {
return (condition != null && condition.getNode().getElementType() == JetNodeTypes.BOOLEAN_CONSTANT &&
condition.getNode().findChildByType(JetTokens.TRUE_KEYWORD) != null);
}
public static boolean isAbstract(@NotNull JetDeclarationWithBody declaration) {
return declaration.getBodyExpression() == null;
}
@@ -83,13 +83,17 @@ public interface BindingContext {
WritableSlice<JetTypeReference, JetType> TYPE = Slices.createSimpleSlice();
WritableSlice<JetExpression, JetType> EXPRESSION_TYPE = new BasicWritableSlice<JetExpression, JetType>(DO_NOTHING);
WritableSlice<JetExpression, Boolean> EXPRESSION_JUMP_OUT_POSSIBLE = new BasicWritableSlice<JetExpression, Boolean>(DO_NOTHING);
WritableSlice<JetExpression, JetType> EXPECTED_EXPRESSION_TYPE = new BasicWritableSlice<JetExpression, JetType>(DO_NOTHING);
WritableSlice<JetFunction, JetType> EXPECTED_RETURN_TYPE = new BasicWritableSlice<JetFunction, JetType>(DO_NOTHING);
WritableSlice<JetExpression, DataFlowInfo> EXPRESSION_DATA_FLOW_INFO = new BasicWritableSlice<JetExpression, DataFlowInfo>(DO_NOTHING);
WritableSlice<JetExpression, Approximation.Info> EXPRESSION_RESULT_APPROXIMATION = new BasicWritableSlice<JetExpression, Approximation.Info>(DO_NOTHING);
WritableSlice<JetExpression, DataFlowInfo> DATAFLOW_INFO_AFTER_CONDITION = Slices.createSimpleSlice();
/**
* Expression has jump out of the loop (break/continue) inside
*/
WritableSlice<JetExpression, Boolean> EXPRESSION_JUMP_OUT_POSSIBLE = new BasicWritableSlice<JetExpression, Boolean>(DO_NOTHING);
/**
* A qualifier corresponds to a receiver expression (if any). For 'A.B' qualifier is recorded for 'A'.
*/
@@ -30,8 +30,8 @@ import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall;
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
import org.jetbrains.kotlin.types.JetType;
import org.jetbrains.kotlin.types.JetTypeInfo;
import org.jetbrains.kotlin.types.TypeUtils;
import org.jetbrains.kotlin.types.expressions.TypeInfoWithJumpInfo;
import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice;
import java.util.Collection;
@@ -144,11 +144,12 @@ public class BindingContextUtils {
}
@Nullable
public static JetTypeInfo getRecordedTypeInfo(@NotNull JetExpression expression, @NotNull BindingContext context) {
public static TypeInfoWithJumpInfo getRecordedTypeInfo(@NotNull JetExpression expression, @NotNull BindingContext context) {
if (!context.get(BindingContext.PROCESSED, expression)) return null;
DataFlowInfo dataFlowInfo = BindingContextUtilPackage.getDataFlowInfo(context, expression);
JetType type = context.get(BindingContext.EXPRESSION_TYPE, expression);
return JetTypeInfo.create(type, dataFlowInfo);
Boolean jumpOutPossible = context.get(BindingContext.EXPRESSION_JUMP_OUT_POSSIBLE, expression);
return new TypeInfoWithJumpInfo(type, dataFlowInfo, Boolean.TRUE.equals(jumpOutPossible), dataFlowInfo);
}
public static boolean isExpressionWithValidReference(
@@ -1236,14 +1236,15 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
Call call = createCallForSpecialConstruction(expression, expression.getOperationReference(), Lists.newArrayList(left, right));
ResolvedCall<FunctionDescriptor> resolvedCall = components.controlStructureTypingUtils.resolveSpecialConstructionAsCall(
call, "Elvis", Lists.newArrayList("left", "right"), Lists.newArrayList(true, false), contextWithExpectedType, null);
JetTypeInfo leftTypeInfo = BindingContextUtils.getRecordedTypeInfo(left, context.trace.getBindingContext());
TypeInfoWithJumpInfo leftTypeInfo = BindingContextUtils.getRecordedTypeInfo(left, context.trace.getBindingContext());
assert leftTypeInfo != null : "Left expression was not processed: " + expression;
JetType leftType = leftTypeInfo.getType();
if (leftType != null && isKnownToBeNotNull(left, leftType, context)) {
context.trace.report(USELESS_ELVIS.on(left, leftType));
}
JetTypeInfo rightTypeInfo = BindingContextUtils.getRecordedTypeInfo(right, context.trace.getBindingContext());
TypeInfoWithJumpInfo rightTypeInfo = BindingContextUtils.getRecordedTypeInfo(right, context.trace.getBindingContext());
assert rightTypeInfo != null : "Right expression was not processed: " + expression;
boolean loopBreakContinuePossible = leftTypeInfo.getJumpOutPossible() || rightTypeInfo.getJumpOutPossible();
JetType rightType = rightTypeInfo.getType();
DataFlowInfo dataFlowInfo = resolvedCall.getDataFlowInfoForArguments().getResultInfo();
@@ -1262,7 +1263,9 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
if (context.contextDependency == DEPENDENT) {
return JetTypeInfo.create(type, dataFlowInfo);
}
return DataFlowUtils.checkType(type, expression, context, dataFlowInfo);
JetTypeInfo result = DataFlowUtils.checkType(type, expression, context, dataFlowInfo);
// If break or continue was possible, take condition check info as the jump info
return new TypeInfoWithJumpInfo(result.getType(), result.getDataFlowInfo(), loopBreakContinuePossible, context.dataFlowInfo);
}
@NotNull
@@ -104,11 +104,12 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
if (elseBranch == null) {
if (thenBranch != null) {
LoopTypeInfo result = getTypeInfoWhenOnlyOneBranchIsPresent(
TypeInfoWithJumpInfo 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 result.getJumpOutPossible()
? result.replaceJumpOutPossible(true).replaceJumpFlowInfo(conditionDataFlowInfo)
: result;
}
return DataFlowUtils.checkImplicitCast(components.builtIns.getUnitType(), ifExpression, contextWithExpectedType,
isStatement, thenInfo.or(elseInfo));
@@ -129,13 +130,11 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
contextWithExpectedType, dataFlowInfoForArguments);
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);
TypeInfoWithJumpInfo thenTypeInfo = BindingContextUtils.getRecordedTypeInfo(thenBranch, bindingContext);
TypeInfoWithJumpInfo elseTypeInfo = BindingContextUtils.getRecordedTypeInfo(elseBranch, bindingContext);
assert thenTypeInfo != null : "'Then' branch of if expression was not processed: " + ifExpression;
assert elseTypeInfo != null : "'Else' branch of if expression was not processed: " + ifExpression;
boolean loopBreakContinuePossible = thenTypeInfo.getJumpOutPossible() || elseTypeInfo.getJumpOutPossible();
JetType thenType = thenTypeInfo.getType();
JetType elseType = elseTypeInfo.getType();
@@ -162,11 +161,11 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
JetType resultType = resolvedCall.getResultingDescriptor().getReturnType();
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);
return new TypeInfoWithJumpInfo(result.getType(), result.getDataFlowInfo(), loopBreakContinuePossible, conditionDataFlowInfo);
}
@NotNull
private LoopTypeInfo getTypeInfoWhenOnlyOneBranchIsPresent(
private TypeInfoWithJumpInfo getTypeInfoWhenOnlyOneBranchIsPresent(
@NotNull JetExpression presentBranch,
@NotNull WritableScopeImpl presentScope,
@NotNull DataFlowInfo presentInfo,
@@ -177,7 +176,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
) {
ExpressionTypingContext newContext = context.replaceDataFlowInfo(presentInfo).replaceExpectedType(NO_EXPECTED_TYPE)
.replaceContextDependency(INDEPENDENT);
LoopTypeInfo typeInfo = components.expressionTypingServices.getBlockReturnedTypeWithWritableScope(
TypeInfoWithJumpInfo typeInfo = components.expressionTypingServices.getBlockReturnedTypeWithWritableScope(
presentScope, Collections.singletonList(presentBranch), CoercionStrategy.NO_COERCION, newContext);
JetType type = typeInfo.getType();
DataFlowInfo dataFlowInfo;
@@ -188,7 +187,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
}
JetType typeForIfExpression = DataFlowUtils.checkType(components.builtIns.getUnitType(), ifExpression, context);
JetTypeInfo result = DataFlowUtils.checkImplicitCast(typeForIfExpression, ifExpression, context, isStatement, dataFlowInfo);
return new LoopTypeInfo(result.getType(), result.getDataFlowInfo(), typeInfo.isJumpOutPossible(), typeInfo.getJumpFlowInfo());
return new TypeInfoWithJumpInfo(result.getType(), result.getDataFlowInfo(), typeInfo.getJumpOutPossible(), typeInfo.getJumpFlowInfo());
}
@Override
@@ -196,11 +195,6 @@ 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);
@@ -216,7 +210,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
DataFlowInfo dataFlowInfo = checkCondition(context.scope, condition, context);
JetExpression body = expression.getBody();
LoopTypeInfo bodyTypeInfo = null;
TypeInfoWithJumpInfo bodyTypeInfo = null;
if (body != null) {
WritableScopeImpl scopeToExtend = newWritableScopeImpl(context, "Scope extended in while's condition");
DataFlowInfo conditionInfo = DataFlowUtils.extractDataFlowInfoFromCondition(condition, true, context).and(dataFlowInfo);
@@ -234,7 +228,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
// 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)) {
if (bodyTypeInfo != null && JetPsiUtil.isTrueConstant(condition)) {
// We should take data flow info from the first jump point,
// but without affecting changing variables
dataFlowInfo = dataFlowInfo.and(loopVisitor.clearDataFlowInfoForAssignedLocalVariables(bodyTypeInfo.getJumpFlowInfo()));
@@ -300,7 +294,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
// 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
LoopTypeInfo bodyTypeInfo = null;
TypeInfoWithJumpInfo bodyTypeInfo = null;
if (body instanceof JetFunctionLiteralExpression) {
JetFunctionLiteralExpression function = (JetFunctionLiteralExpression) body;
JetFunctionLiteral functionLiteral = function.getFunctionLiteral();
@@ -570,17 +564,17 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
}
@Override
public LoopTypeInfo visitBreakExpression(@NotNull JetBreakExpression expression, ExpressionTypingContext context) {
public TypeInfoWithJumpInfo visitBreakExpression(@NotNull JetBreakExpression expression, ExpressionTypingContext context) {
LabelResolver.INSTANCE.resolveControlLabel(expression, context);
JetTypeInfo result = DataFlowUtils.checkType(components.builtIns.getNothingType(), expression, context, context.dataFlowInfo);
return new LoopTypeInfo(result.getType(), result.getDataFlowInfo(), true, result.getDataFlowInfo());
return new TypeInfoWithJumpInfo(result.getType(), result.getDataFlowInfo(), true, result.getDataFlowInfo());
}
@Override
public LoopTypeInfo visitContinueExpression(@NotNull JetContinueExpression expression, ExpressionTypingContext context) {
public TypeInfoWithJumpInfo visitContinueExpression(@NotNull JetContinueExpression expression, ExpressionTypingContext context) {
LabelResolver.INSTANCE.resolveControlLabel(expression, context);
JetTypeInfo result = DataFlowUtils.checkType(components.builtIns.getNothingType(), expression, context, context.dataFlowInfo);
return new LoopTypeInfo(result.getType(), result.getDataFlowInfo(), true, result.getDataFlowInfo());
return new TypeInfoWithJumpInfo(result.getType(), result.getDataFlowInfo(), true, result.getDataFlowInfo());
}
@NotNull
@@ -308,14 +308,14 @@ public class ExpressionTypingServices {
* 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*/ LoopTypeInfo getBlockReturnedTypeWithWritableScope(
/*package*/ TypeInfoWithJumpInfo getBlockReturnedTypeWithWritableScope(
@NotNull WritableScope scope,
@NotNull List<? extends JetElement> block,
@NotNull CoercionStrategy coercionStrategyForLastExpression,
@NotNull ExpressionTypingContext context
) {
if (block.isEmpty()) {
return new LoopTypeInfo(builtIns.getUnitType(), context.dataFlowInfo);
return new TypeInfoWithJumpInfo(builtIns.getUnitType(), context.dataFlowInfo, false, context.dataFlowInfo);
}
ExpressionTypingInternals blockLevelVisitor = ExpressionTypingVisitorDispatcher.createForBlock(expressionTypingComponents, scope);
@@ -344,10 +344,10 @@ 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();
if (result instanceof TypeInfoWithJumpInfo) {
TypeInfoWithJumpInfo jumpTypeInfo = (TypeInfoWithJumpInfo) result;
beforeJumpInfo = jumpTypeInfo.getJumpFlowInfo();
jumpOutPossible = jumpTypeInfo.getJumpOutPossible();
}
else {
beforeJumpInfo = newDataFlowInfo;
@@ -359,7 +359,7 @@ public class ExpressionTypingServices {
}
blockLevelVisitor = ExpressionTypingVisitorDispatcher.createForBlock(expressionTypingComponents, scope);
}
return new LoopTypeInfo(result.getType(), result.getDataFlowInfo(), jumpOutPossible, beforeJumpInfo);
return new TypeInfoWithJumpInfo(result.getType(), result.getDataFlowInfo(), jumpOutPossible, beforeJumpInfo);
}
private JetTypeInfo getTypeOfLastExpressionInBlock(
@@ -142,13 +142,11 @@ public class ExpressionTypingVisitorDispatcher extends JetVisitor<JetTypeInfo, E
// Some recursive definitions (object expressions) must put their types in the cache manually:
if (context.trace.get(BindingContext.PROCESSED, expression)) {
JetType type = context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, expression);
if (result instanceof LoopTypeInfo) {
LoopTypeInfo loopTypeInfo = (LoopTypeInfo)result;
return new LoopTypeInfo(type,
loopTypeInfo.getDataFlowInfo(),
loopTypeInfo.isJumpOutPossible(),
loopTypeInfo.getJumpFlowInfo());
} else {
if (result instanceof TypeInfoWithJumpInfo) {
TypeInfoWithJumpInfo jumpTypeInfo = (TypeInfoWithJumpInfo) result;
return jumpTypeInfo.replaceType(type);
}
else {
return JetTypeInfo.create(type, result.getDataFlowInfo());
}
}
@@ -159,9 +157,9 @@ public class ExpressionTypingVisitorDispatcher extends JetVisitor<JetTypeInfo, E
if (result.getType() != null) {
context.trace.record(BindingContext.EXPRESSION_TYPE, expression, result.getType());
}
if (result instanceof LoopTypeInfo) {
LoopTypeInfo loopTypeInfo = (LoopTypeInfo)result;
if (loopTypeInfo.isJumpOutPossible()) {
if (result instanceof TypeInfoWithJumpInfo) {
TypeInfoWithJumpInfo jumpTypeInfo = (TypeInfoWithJumpInfo)result;
if (jumpTypeInfo.getJumpOutPossible()) {
context.trace.record(BindingContext.EXPRESSION_JUMP_OUT_POSSIBLE, expression, true);
}
}
@@ -1,67 +0,0 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.types.expressions;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
import org.jetbrains.kotlin.types.JetType;
import org.jetbrains.kotlin.types.JetTypeInfo;
/**
* A local descendant of JetTypeInfo. Stores simultaneously current data flow info
* and jump point data flow info, together with information about possible jump inside. For example:
* do {
* x!!.foo()
* if (bar()) break;
* y!!.gav()
* } while (bis())
* At the end current data flow info is x != null && y != null, but jump data flow info is x != null only.
* Jump will be possible
*/
/*package*/ class LoopTypeInfo extends JetTypeInfo {
private final DataFlowInfo jumpFlowInfo;
private final boolean jumpOutPossible;
LoopTypeInfo(@Nullable JetType type, @NotNull DataFlowInfo dataFlowInfo) {
this(type, dataFlowInfo, false, dataFlowInfo);
}
LoopTypeInfo(
@Nullable JetType type, @NotNull DataFlowInfo dataFlowInfo,
boolean jumpOutPossible, @NotNull DataFlowInfo jumpFlowInfo
) {
super(type, dataFlowInfo);
this.jumpFlowInfo = jumpFlowInfo;
this.jumpOutPossible = jumpOutPossible;
}
@NotNull
DataFlowInfo getJumpFlowInfo() {
return jumpFlowInfo;
}
/**
* Returns true if jump to the end of the loop is possible inside the considered expression.
* Break and continue are both counted as possible jump to the end, but return is not.
*/
boolean isJumpOutPossible() {
return jumpOutPossible;
}
}
@@ -0,0 +1,46 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.types.expressions
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.types.JetTypeInfo
/**
* A local descendant of JetTypeInfo. Stores simultaneously current type with data flow info
* and jump point data flow info, together with information about possible jump outside. For example:
* do {
* x!!.foo()
* if (bar()) break;
* y!!.gav()
* } while (bis())
* At the end current data flow info is x != null && y != null, but jump data flow info is x != null only.
* Both break and continue are counted as possible jump outside of a loop, but return is not.
*/
/*package*/ class TypeInfoWithJumpInfo(
type: JetType?,
dataFlowInfo: DataFlowInfo,
val jumpOutPossible: Boolean = false,
val jumpFlowInfo: DataFlowInfo = dataFlowInfo
) : JetTypeInfo(type, dataFlowInfo) {
fun replaceType(type: JetType?) = TypeInfoWithJumpInfo(type, getDataFlowInfo(), jumpOutPossible, jumpFlowInfo)
fun replaceJumpOutPossible(jumpOutPossible: Boolean) = TypeInfoWithJumpInfo(getType(), getDataFlowInfo(), jumpOutPossible, jumpFlowInfo)
fun replaceJumpFlowInfo(jumpFlowInfo: DataFlowInfo) = TypeInfoWithJumpInfo(getType(), getDataFlowInfo(), jumpOutPossible, jumpFlowInfo)
}
@@ -0,0 +1,10 @@
public fun foo(x: String?): Int {
do {
// After the check, smart cast should work
x ?: break
// x is not null in both branches
<!DEBUG_INFO_SMARTCAST!>x<!>.length()
} while (true)
// x is null because of the break
return x<!UNSAFE_CALL!>.<!>length()
}
@@ -0,0 +1,3 @@
package
public fun foo(/*0*/ x: kotlin.String?): kotlin.Int
@@ -0,0 +1,9 @@
public fun foo(x: String?, y: String?): Int {
while (true) {
x ?: if (y == null) break
// y is not null in both branches
<!DEBUG_INFO_SMARTCAST!>y<!>.length()
}
// y is null because of the break
return y<!UNSAFE_CALL!>.<!>length()
}
@@ -0,0 +1,3 @@
package
public fun foo(/*0*/ x: kotlin.String?, /*1*/ y: kotlin.String?): kotlin.Int
@@ -0,0 +1,9 @@
public fun foo(x: String?): Int {
do {
// After the check, smart cast should work
x ?: x!!.length()
// x is not null in both branches
if (<!DEBUG_INFO_SMARTCAST!>x<!>.length() == 0) break
} while (true)
return <!DEBUG_INFO_SMARTCAST!>x<!>.length()
}
@@ -0,0 +1,3 @@
package
public fun foo(/*0*/ x: kotlin.String?): kotlin.Int
@@ -0,0 +1,9 @@
public fun foo(x: String?, y: String?): Int {
while (true) {
(if (x != null) break else y) ?: y!!
// x is not null in both branches
<!DEBUG_INFO_SMARTCAST!>y<!>.length()
}
// y can be null because of the break
return y<!UNSAFE_CALL!>.<!>length()
}
@@ -0,0 +1,3 @@
package
public fun foo(/*0*/ x: kotlin.String?, /*1*/ y: kotlin.String?): kotlin.Int
@@ -11132,6 +11132,30 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("elvisBreakInsideDoWhile.kt")
public void testElvisBreakInsideDoWhile() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/elvisBreakInsideDoWhile.kt");
doTest(fileName);
}
@TestMetadata("elvisIfBreakInsideWhileTrue.kt")
public void testElvisIfBreakInsideWhileTrue() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/elvisIfBreakInsideWhileTrue.kt");
doTest(fileName);
}
@TestMetadata("elvisInsideDoWhile.kt")
public void testElvisInsideDoWhile() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/elvisInsideDoWhile.kt");
doTest(fileName);
}
@TestMetadata("elvisLeftBreakInsideWhileTrue.kt")
public void testElvisLeftBreakInsideWhileTrue() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/elvisLeftBreakInsideWhileTrue.kt");
doTest(fileName);
}
@TestMetadata("ifBlockInsideDoWhile.kt")
public void testIfBlockInsideDoWhile() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/ifBlockInsideDoWhile.kt");