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.
This commit is contained in:
@@ -83,6 +83,7 @@ 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);
|
||||
|
||||
+1
-2
@@ -25,8 +25,7 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public interface DataFlowInfo {
|
||||
DataFlowInfo EMPTY = new DelegatingDataFlowInfo(null, ImmutableMap.<DataFlowValue, Nullability>of(),
|
||||
DelegatingDataFlowInfo.newTypeInfo());
|
||||
DataFlowInfo EMPTY = new DelegatingDataFlowInfo(null, ImmutableMap.<DataFlowValue, Nullability>of(), DelegatingDataFlowInfo.newTypeInfo());
|
||||
|
||||
@NotNull
|
||||
Map<DataFlowValue, Nullability> getCompleteNullabilityInfo();
|
||||
|
||||
+53
-14
@@ -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.<JetElement>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
|
||||
|
||||
+20
-4
@@ -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<? extends JetElement> 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<? extends JetElement> 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(
|
||||
|
||||
+17
-2
@@ -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 JetVisitor<JetTypeInfo, E
|
||||
result = expression.accept(visitor, context);
|
||||
// Some recursive definitions (object expressions) must put their types in the cache manually:
|
||||
if (context.trace.get(BindingContext.PROCESSED, expression)) {
|
||||
return JetTypeInfo.create(context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, expression),
|
||||
result.getDataFlowInfo());
|
||||
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 {
|
||||
return JetTypeInfo.create(type, result.getDataFlowInfo());
|
||||
}
|
||||
}
|
||||
|
||||
if (result.getType() instanceof DeferredType) {
|
||||
@@ -150,6 +159,12 @@ 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()) {
|
||||
context.trace.record(BindingContext.EXPRESSION_JUMP_OUT_POSSIBLE, expression, true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch (ReenteringLazyValueComputationException e) {
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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,11 @@
|
||||
fun x(): Boolean { return true }
|
||||
|
||||
public fun foo(p: String?): Int {
|
||||
// See KT-6283
|
||||
do {
|
||||
p!!.length()
|
||||
} while (!x())
|
||||
// Do-while loop is executed at least once, so
|
||||
// p should be not null here
|
||||
return <!DEBUG_INFO_SMARTCAST!>p<!>.length()
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
public fun foo(/*0*/ p: kotlin.String?): kotlin.Int
|
||||
internal fun x(): kotlin.Boolean
|
||||
@@ -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 <!DEBUG_INFO_SMARTCAST!>p<!>.length()
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
public fun foo(/*0*/ p: kotlin.String?): kotlin.Int
|
||||
internal fun x(): kotlin.Boolean
|
||||
@@ -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 <!DEBUG_INFO_SMARTCAST!>p<!>.length()
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
public fun foo(/*0*/ p: kotlin.String?): kotlin.Int
|
||||
internal fun x(): kotlin.Boolean
|
||||
@@ -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<!UNSAFE_CALL!>.<!>length()
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package
|
||||
|
||||
public fun foo(/*0*/ p: kotlin.String?): kotlin.Int
|
||||
internal fun x(): kotlin.Boolean
|
||||
internal fun y(): kotlin.Boolean
|
||||
@@ -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<!UNSAFE_CALL!>.<!>length()
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package
|
||||
|
||||
public fun foo(/*0*/ p: kotlin.String?): kotlin.Int
|
||||
internal fun x(): kotlin.Boolean
|
||||
internal fun y(): kotlin.Boolean
|
||||
@@ -0,0 +1,5 @@
|
||||
fun foo(s: String?): Int {
|
||||
do {
|
||||
} while (s!!.length() > 0)
|
||||
return <!DEBUG_INFO_SMARTCAST!>s<!>.length()
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package
|
||||
|
||||
internal fun foo(/*0*/ s: kotlin.String?): kotlin.Int
|
||||
@@ -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<!UNSAFE_CALL!>.<!>length()
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
internal fun bar(): kotlin.Boolean
|
||||
internal fun foo(/*0*/ s: kotlin.String?): kotlin.Int
|
||||
@@ -0,0 +1,10 @@
|
||||
fun x(): Boolean { return true }
|
||||
|
||||
public fun foo(p: String?): Int {
|
||||
// Exotic variant with unused literal
|
||||
do <!UNUSED_FUNCTION_LITERAL!>{ ->
|
||||
p!!.length()
|
||||
}<!> while (!x())
|
||||
// Literal is not called so p.length() is unsafe
|
||||
return p<!UNSAFE_CALL!>.<!>length()
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
public fun foo(/*0*/ p: kotlin.String?): kotlin.Int
|
||||
internal fun x(): kotlin.Boolean
|
||||
@@ -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<!UNSAFE_CALL!>.<!>length()
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
public fun foo(/*0*/ p: kotlin.String?): kotlin.Int
|
||||
internal fun x(): kotlin.Boolean
|
||||
@@ -0,0 +1,5 @@
|
||||
fun foo(s: String?): Int {
|
||||
do {
|
||||
} while (s==null)
|
||||
return <!DEBUG_INFO_SMARTCAST!>s<!>.length()
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package
|
||||
|
||||
internal fun foo(/*0*/ s: kotlin.String?): kotlin.Int
|
||||
@@ -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<!UNSAFE_CALL!>.<!>length()
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
internal fun bar(): kotlin.Boolean
|
||||
internal fun foo(/*0*/ s: kotlin.String?): kotlin.Int
|
||||
@@ -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
|
||||
}
|
||||
<!DEBUG_INFO_SMARTCAST!>y<!>.length()
|
||||
p!!.length()
|
||||
} while (true)
|
||||
return y?.length() ?: -1
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package
|
||||
|
||||
public fun foo(/*0*/ p: kotlin.String?, /*1*/ y: kotlin.String?): kotlin.Int
|
||||
@@ -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
|
||||
<!DEBUG_INFO_SMARTCAST!>y<!>.length()
|
||||
} while (true)
|
||||
// break is possible before so !! is necessary
|
||||
return y!!.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(p: String?, y: String?): Int {
|
||||
do {
|
||||
// After the check, smart cast should work
|
||||
if (y == null) break
|
||||
<!DEBUG_INFO_SMARTCAST!>y<!>.length()
|
||||
p!!.length()
|
||||
} while (true)
|
||||
return y?.length() ?: -1
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package
|
||||
|
||||
public fun foo(/*0*/ p: kotlin.String?, /*1*/ y: kotlin.String?): kotlin.Int
|
||||
@@ -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
|
||||
<!DEBUG_INFO_SMARTCAST!>r<!>.length()
|
||||
// Auto cast possible
|
||||
return <!DEBUG_INFO_SMARTCAST!>p<!>.length()
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
public fun foo(/*0*/ p: kotlin.String?, /*1*/ r: kotlin.String?): kotlin.Int
|
||||
internal fun x(): kotlin.Boolean
|
||||
@@ -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<!UNSAFE_CALL!>.<!>length()
|
||||
// Auto cast possible
|
||||
return <!DEBUG_INFO_SMARTCAST!>p<!>.length()
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
public fun foo(/*0*/ p: kotlin.String?, /*1*/ r: kotlin.String?): kotlin.Int
|
||||
internal fun x(): kotlin.Boolean
|
||||
@@ -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
|
||||
<!DEBUG_INFO_SMARTCAST!>r<!>.length()
|
||||
<!DEBUG_INFO_SMARTCAST!>q<!>.length()
|
||||
return <!DEBUG_INFO_SMARTCAST!>p<!>.length()
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
<!DEBUG_INFO_SMARTCAST!>r<!>.length()
|
||||
<!DEBUG_INFO_SMARTCAST!>q<!>.length()
|
||||
return <!DEBUG_INFO_SMARTCAST!>p<!>.length()
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
<!DEBUG_INFO_SMARTCAST!>r<!>.length()
|
||||
<!DEBUG_INFO_SMARTCAST!>q<!>.length()
|
||||
return <!DEBUG_INFO_SMARTCAST!>p<!>.length()
|
||||
}
|
||||
@@ -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
|
||||
@@ -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(<!DEBUG_INFO_SMARTCAST!>p<!>)) break@loop
|
||||
if (x(<!DEBUG_INFO_SMARTCAST!>q<!>)) break
|
||||
}
|
||||
} while (r == null)
|
||||
if (!x(<!DEBUG_INFO_SMARTCAST!>p<!>)) break
|
||||
}
|
||||
// Long break allows r == null
|
||||
r<!UNSAFE_CALL!>.<!>length()
|
||||
// Smart cast is possible
|
||||
<!DEBUG_INFO_SMARTCAST!>q<!>.length()
|
||||
return <!DEBUG_INFO_SMARTCAST!>p<!>.length()
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
<!DEBUG_INFO_SMARTCAST!>q<!>.length()
|
||||
// But not possible for the others
|
||||
r<!UNSAFE_CALL!>.<!>length()
|
||||
return p<!UNSAFE_CALL!>.<!>length()
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
<!DEBUG_INFO_SMARTCAST!>y<!>.length()
|
||||
} while (true)
|
||||
return <!DEBUG_INFO_SMARTCAST!>y<!>.length()
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package
|
||||
|
||||
public fun foo(/*0*/ p: kotlin.String?, /*1*/ y: kotlin.String?): kotlin.Int
|
||||
@@ -0,0 +1,6 @@
|
||||
fun foo(s: String?): Int {
|
||||
while (s!!.length() > 0) {
|
||||
<!DEBUG_INFO_SMARTCAST!>s<!>.length()
|
||||
}
|
||||
return <!DEBUG_INFO_SMARTCAST!>s<!>.length()
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package
|
||||
|
||||
internal fun foo(/*0*/ s: kotlin.String?): kotlin.Int
|
||||
@@ -0,0 +1,9 @@
|
||||
fun bar(): Boolean { return true }
|
||||
|
||||
fun foo(s: String?): Int {
|
||||
while (s!!.length() > 0) {
|
||||
<!DEBUG_INFO_SMARTCAST!>s<!>.length()
|
||||
if (bar()) break
|
||||
}
|
||||
return <!DEBUG_INFO_SMARTCAST!>s<!>.length()
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
internal fun bar(): kotlin.Boolean
|
||||
internal fun foo(/*0*/ s: kotlin.String?): kotlin.Int
|
||||
@@ -0,0 +1,5 @@
|
||||
fun foo(s: String?): Int {
|
||||
while (s==null) {
|
||||
}
|
||||
return <!DEBUG_INFO_SMARTCAST!>s<!>.length()
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package
|
||||
|
||||
internal fun foo(/*0*/ s: kotlin.String?): kotlin.Int
|
||||
@@ -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<!UNSAFE_CALL!>.<!>length()
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
internal fun bar(): kotlin.Boolean
|
||||
internal fun foo(/*0*/ s: kotlin.String?): kotlin.Int
|
||||
@@ -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<!UNSAFE_CALL!>.<!>length()
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
public fun foo(/*0*/ p: kotlin.String?): kotlin.Int
|
||||
internal fun x(): kotlin.Boolean
|
||||
@@ -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<!UNSAFE_CALL!>.<!>length()
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
public fun foo(/*0*/ p: kotlin.String?): kotlin.Int
|
||||
internal fun x(): kotlin.Boolean
|
||||
@@ -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 <!DEBUG_INFO_SMARTCAST!>p<!>.length()
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
public fun foo(/*0*/ p: kotlin.String?): kotlin.Int
|
||||
internal fun x(): kotlin.Boolean
|
||||
@@ -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
|
||||
<!DEBUG_INFO_SMARTCAST!>p<!>.length()
|
||||
}
|
||||
// p can be null because break is earlier than return
|
||||
return p<!UNSAFE_CALL!>.<!>length()
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
public fun foo(/*0*/ p: kotlin.String?): kotlin.Int
|
||||
internal fun x(): kotlin.Boolean
|
||||
@@ -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<!UNSAFE_CALL!>.<!>length()
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
public fun foo(/*0*/ p: kotlin.String?): kotlin.Int
|
||||
internal fun x(): kotlin.Boolean
|
||||
@@ -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
|
||||
<!DEBUG_INFO_SMARTCAST!>p<!>.length()
|
||||
}
|
||||
// while (true) loop body with return is executed at least once
|
||||
// so p is not null here
|
||||
return <!DEBUG_INFO_SMARTCAST!>p<!>.length()
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
public fun foo(/*0*/ p: kotlin.String?): kotlin.Int
|
||||
internal fun x(): kotlin.Boolean
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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{}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user