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:
@@ -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(
|
||||
|
||||
+6
-3
@@ -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
|
||||
|
||||
+18
-24
@@ -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
|
||||
|
||||
+7
-7
@@ -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(
|
||||
|
||||
+8
-10
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user