Data flow information: take language version into account while processing safe calls and bound values #KT-14350 Fixed

This commit is contained in:
Mikhail Glukhikh
2017-01-18 15:47:21 +03:00
committed by mglukhikh
parent ebd577e52a
commit 905e67e713
15 changed files with 165 additions and 53 deletions
@@ -110,7 +110,8 @@ class LocalVariableResolver(
DescriptorUtils.getContainingModuleOrNull(scope.ownerDescriptor))
// We cannot say here anything new about initializerDataFlowValue
// except it has the same value as variableDataFlowValue
typeInfo = typeInfo.replaceDataFlowInfo(dataFlowInfo.assign(variableDataFlowValue, initializerDataFlowValue))
typeInfo = typeInfo.replaceDataFlowInfo(dataFlowInfo.assign(variableDataFlowValue, initializerDataFlowValue,
languageVersionSettings))
}
}
}
@@ -20,6 +20,7 @@ import com.intellij.lang.ASTNode
import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.VariableDescriptor
@@ -70,7 +71,8 @@ class CallExpressionResolver(
private val argumentTypeResolver: ArgumentTypeResolver,
private val dataFlowAnalyzer: DataFlowAnalyzer,
private val builtIns: KotlinBuiltIns,
private val qualifiedExpressionResolver: QualifiedExpressionResolver
private val qualifiedExpressionResolver: QualifiedExpressionResolver,
private val languageVersionSettings: LanguageVersionSettings
) {
private lateinit var expressionTypingServices: ExpressionTypingServices
@@ -323,7 +325,7 @@ class CallExpressionResolver(
// Additional "receiver != null" information should be applied if we consider a safe call
if (receiverCanBeNull) {
initialDataFlowInfoForArguments = initialDataFlowInfoForArguments.disequate(
receiverDataFlowValue, DataFlowValue.nullValue(builtIns))
receiverDataFlowValue, DataFlowValue.nullValue(builtIns), languageVersionSettings)
}
else if (receiver is ReceiverValue) {
reportUnnecessarySafeCall(context.trace, receiver.type, element.node, receiver)
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.resolve.calls.smartcasts
import com.google.common.collect.SetMultimap
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.types.KotlinType
/**
@@ -61,25 +62,25 @@ interface DataFlowInfo {
* Call this function to clear all data flow information about
* the given data flow value. Useful when we are not sure how this value can be changed, e.g. in a loop.
*/
fun clearValueInfo(value: DataFlowValue): DataFlowInfo
fun clearValueInfo(value: DataFlowValue, languageVersionSettings: LanguageVersionSettings): DataFlowInfo
/**
* Call this function when b is assigned to a
*/
fun assign(a: DataFlowValue, b: DataFlowValue): DataFlowInfo
fun assign(a: DataFlowValue, b: DataFlowValue, languageVersionSettings: LanguageVersionSettings): DataFlowInfo
/**
* Call this function when it's known than a == b.
* sameTypes should be true iff we have guarantee that a and b have the same type
*/
fun equate(a: DataFlowValue, b: DataFlowValue, sameTypes: Boolean): DataFlowInfo
fun equate(a: DataFlowValue, b: DataFlowValue, sameTypes: Boolean, languageVersionSettings: LanguageVersionSettings): DataFlowInfo
/**
* Call this function when it's known than a != b
*/
fun disequate(a: DataFlowValue, b: DataFlowValue): DataFlowInfo
fun disequate(a: DataFlowValue, b: DataFlowValue, languageVersionSettings: LanguageVersionSettings): DataFlowInfo
fun establishSubtyping(value: DataFlowValue, type: KotlinType): DataFlowInfo
fun establishSubtyping(value: DataFlowValue, type: KotlinType, languageVersionSettings: LanguageVersionSettings): DataFlowInfo
/**
* Call this function to add data flow information from other to this and return sum as the result
@@ -18,6 +18,8 @@ package org.jetbrains.kotlin.resolve.calls.smartcasts
import com.google.common.collect.*
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
@@ -85,21 +87,25 @@ internal class DelegatingDataFlowInfo private constructor(
nullabilityInfo[key] ?: parent?.getCollectedNullability(key) ?: key.immanentNullability
}
private fun putNullability(map: MutableMap<DataFlowValue, Nullability>, value: DataFlowValue,
nullability: Nullability, affectReceiver: Boolean = true): Boolean {
private fun putNullability(map: MutableMap<DataFlowValue, Nullability>,
value: DataFlowValue,
nullability: Nullability,
languageVersionSettings: LanguageVersionSettings,
affectReceiver: Boolean = true): Boolean {
map.put(value, nullability)
val identifierInfo = value.identifierInfo
if (affectReceiver && !nullability.canBeNull()) {
if (affectReceiver && !nullability.canBeNull() &&
languageVersionSettings.supportsFeature(LanguageFeature.SafeCallBoundSmartCasts)) {
when (identifierInfo) {
is IdentifierInfo.Qualified -> {
val receiverType = identifierInfo.receiverType
if (identifierInfo.safe && receiverType != null) {
putNullability(map, DataFlowValue(identifierInfo.receiverInfo, receiverType), nullability)
putNullability(map, DataFlowValue(identifierInfo.receiverInfo, receiverType), nullability, languageVersionSettings)
}
}
is IdentifierInfo.Variable -> identifierInfo.bound?.let {
putNullability(map, it, nullability)
putNullability(map, it, nullability, languageVersionSettings)
}
}
}
@@ -138,16 +144,16 @@ internal class DelegatingDataFlowInfo private constructor(
* @param value
*/
override fun clearValueInfo(value: DataFlowValue): DataFlowInfo {
override fun clearValueInfo(value: DataFlowValue, languageVersionSettings: LanguageVersionSettings): DataFlowInfo {
val builder = Maps.newHashMap<DataFlowValue, Nullability>()
putNullability(builder, value, Nullability.UNKNOWN)
putNullability(builder, value, Nullability.UNKNOWN, languageVersionSettings)
return create(this, ImmutableMap.copyOf(builder), EMPTY_TYPE_INFO, value)
}
override fun assign(a: DataFlowValue, b: DataFlowValue): DataFlowInfo {
override fun assign(a: DataFlowValue, b: DataFlowValue, languageVersionSettings: LanguageVersionSettings): DataFlowInfo {
val nullability = Maps.newHashMap<DataFlowValue, Nullability>()
val nullabilityOfB = getStableNullability(b)
putNullability(nullability, a, nullabilityOfB, affectReceiver = false)
putNullability(nullability, a, nullabilityOfB, languageVersionSettings, affectReceiver = false)
val newTypeInfo = newTypeInfo()
var typesForB = getStableTypes(b)
@@ -163,13 +169,15 @@ internal class DelegatingDataFlowInfo private constructor(
return create(this, ImmutableMap.copyOf(nullability), if (newTypeInfo.isEmpty) EMPTY_TYPE_INFO else newTypeInfo, a)
}
override fun equate(a: DataFlowValue, b: DataFlowValue, sameTypes: Boolean): DataFlowInfo {
override fun equate(
a: DataFlowValue, b: DataFlowValue, sameTypes: Boolean, languageVersionSettings: LanguageVersionSettings
): DataFlowInfo {
val builder = Maps.newHashMap<DataFlowValue, Nullability>()
val nullabilityOfA = getStableNullability(a)
val nullabilityOfB = getStableNullability(b)
var changed = putNullability(builder, a, nullabilityOfA.refine(nullabilityOfB)) or
putNullability(builder, b, nullabilityOfB.refine(nullabilityOfA))
var changed = putNullability(builder, a, nullabilityOfA.refine(nullabilityOfB), languageVersionSettings) or
putNullability(builder, b, nullabilityOfB.refine(nullabilityOfA), languageVersionSettings)
// NB: == has no guarantees of type equality, see KT-11280 for the example
val newTypeInfo = newTypeInfo()
@@ -219,17 +227,21 @@ internal class DelegatingDataFlowInfo private constructor(
return types
}
override fun disequate(a: DataFlowValue, b: DataFlowValue): DataFlowInfo {
override fun disequate(
a: DataFlowValue, b: DataFlowValue, languageVersionSettings: LanguageVersionSettings
): DataFlowInfo {
val builder = Maps.newHashMap<DataFlowValue, Nullability>()
val nullabilityOfA = getStableNullability(a)
val nullabilityOfB = getStableNullability(b)
val changed = putNullability(builder, a, nullabilityOfA.refine(nullabilityOfB.invert())) or
putNullability(builder, b, nullabilityOfB.refine(nullabilityOfA.invert()))
val changed = putNullability(builder, a, nullabilityOfA.refine(nullabilityOfB.invert()), languageVersionSettings) or
putNullability(builder, b, nullabilityOfB.refine(nullabilityOfA.invert()), languageVersionSettings)
return if (changed) create(this, ImmutableMap.copyOf(builder), EMPTY_TYPE_INFO) else this
}
override fun establishSubtyping(value: DataFlowValue, type: KotlinType): DataFlowInfo {
override fun establishSubtyping(
value: DataFlowValue, type: KotlinType, languageVersionSettings: LanguageVersionSettings
): DataFlowInfo {
if (value.type == type) return this
if (getCollectedTypes(value).contains(type)) return this
if (!value.type.isFlexible() && value.type.isSubtypeOf(type)) return this
@@ -237,7 +249,7 @@ internal class DelegatingDataFlowInfo private constructor(
newTypeInfo.put(value, type)
val builder = Maps.newHashMap<DataFlowValue, Nullability>()
if (!type.isMarkedNullable) {
putNullability(builder, value, NOT_NULL)
putNullability(builder, value, NOT_NULL, languageVersionSettings)
}
val newNullabilityInfo = if (type.isMarkedNullable) EMPTY_NULLABILITY_INFO else ImmutableMap.copyOf(builder)
return create(this, newNullabilityInfo, newTypeInfo)
@@ -29,6 +29,7 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.KtNodeTypes;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.config.LanguageFeature;
import org.jetbrains.kotlin.config.LanguageVersionSettings;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.diagnostics.Diagnostic;
import org.jetbrains.kotlin.diagnostics.Errors;
@@ -182,7 +183,8 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
if (resultType != null) {
DataFlowValue innerValue = DataFlowValueFactory.createDataFlowValue(innerExpression, resultType, context);
DataFlowValue resultValue = DataFlowValueFactory.createDataFlowValue(expression, resultType, context);
result = result.replaceDataFlowInfo(result.getDataFlowInfo().assign(resultValue, innerValue));
result = result.replaceDataFlowInfo(result.getDataFlowInfo().assign(resultValue, innerValue,
components.languageVersionSettings));
}
return result;
}
@@ -316,7 +318,8 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
DataFlowInfo dataFlowInfo = typeInfo.getDataFlowInfo();
if (operationType == AS_KEYWORD) {
DataFlowValue value = createDataFlowValue(left, subjectType, context);
typeInfo = typeInfo.replaceDataFlowInfo(dataFlowInfo.establishSubtyping(value, targetType));
typeInfo = typeInfo.replaceDataFlowInfo(dataFlowInfo.establishSubtyping(value, targetType,
components.languageVersionSettings));
}
}
@@ -859,7 +862,8 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
result = receiverType;
// Also record data flow information for x++ value (= x)
DataFlowValue returnValue = DataFlowValueFactory.createDataFlowValue(expression, receiverType, contextWithExpectedType);
typeInfo = typeInfo.replaceDataFlowInfo(typeInfo.getDataFlowInfo().assign(returnValue, receiverValue));
typeInfo = typeInfo.replaceDataFlowInfo(typeInfo.getDataFlowInfo().assign(returnValue, receiverValue,
components.languageVersionSettings));
}
}
}
@@ -911,7 +915,8 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
}
else {
DataFlowValue value = createDataFlowValue(baseExpression, baseType, context);
baseTypeInfo = baseTypeInfo.replaceDataFlowInfo(dataFlowInfo.disequate(value, DataFlowValue.nullValue(components.builtIns)));
baseTypeInfo = baseTypeInfo.replaceDataFlowInfo(dataFlowInfo.disequate(value, DataFlowValue.nullValue(components.builtIns),
components.languageVersionSettings));
}
KotlinType resultingType = TypeUtils.makeNotNullable(baseType);
if (context.contextDependency == DEPENDENT) {
@@ -1320,16 +1325,19 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
DataFlowValue nullValue = DataFlowValue.nullValue(components.builtIns);
// left argument is considered not-null if it's not-null also in right part or if we have jump in right part
if (jumpInRight || !rightDataFlowInfo.getStableNullability(leftValue).canBeNull()) {
dataFlowInfo = dataFlowInfo.disequate(leftValue, nullValue);
dataFlowInfo = dataFlowInfo.disequate(leftValue, nullValue, components.languageVersionSettings);
if (left instanceof KtBinaryExpressionWithTypeRHS) {
dataFlowInfo = establishSubtypingForTypeRHS((KtBinaryExpressionWithTypeRHS) left, dataFlowInfo, context);
dataFlowInfo = establishSubtypingForTypeRHS((KtBinaryExpressionWithTypeRHS) left, dataFlowInfo, context,
components.languageVersionSettings);
}
}
DataFlowValue resultValue = DataFlowValueFactory.createDataFlowValue(expression, type, context);
dataFlowInfo = dataFlowInfo.assign(resultValue, leftValue).disequate(resultValue, nullValue);
dataFlowInfo =
dataFlowInfo.assign(resultValue, leftValue, components.languageVersionSettings)
.disequate(resultValue, nullValue, components.languageVersionSettings);
if (!jumpInRight) {
DataFlowValue rightValue = DataFlowValueFactory.createDataFlowValue(right, rightType, context);
rightDataFlowInfo = rightDataFlowInfo.assign(resultValue, rightValue);
rightDataFlowInfo = rightDataFlowInfo.assign(resultValue, rightValue, components.languageVersionSettings);
dataFlowInfo = dataFlowInfo.or(rightDataFlowInfo);
}
}
@@ -1354,7 +1362,8 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
private static DataFlowInfo establishSubtypingForTypeRHS(
@NotNull KtBinaryExpressionWithTypeRHS left,
@NotNull DataFlowInfo dataFlowInfo,
@NotNull ExpressionTypingContext context
@NotNull ExpressionTypingContext context,
@NotNull LanguageVersionSettings languageVersionSettings
) {
IElementType operationType = left.getOperationReference().getReferencedNameElementType();
if (operationType == AS_SAFE) {
@@ -1364,7 +1373,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
DataFlowValue underSafeAsValue = createDataFlowValue(underSafeAs, underSafeAsType, context);
KotlinType targetType = context.trace.get(BindingContext.TYPE, left.getRight());
if (targetType != null) {
return dataFlowInfo.establishSubtyping(underSafeAsValue, targetType);
return dataFlowInfo.establishSubtyping(underSafeAsValue, targetType, languageVersionSettings);
}
}
}
@@ -155,9 +155,9 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
if (resultType != null && thenType != null && elseType != null) {
DataFlowValue resultValue = DataFlowValueFactory.createDataFlowValue(ifExpression, resultType, context);
DataFlowValue thenValue = DataFlowValueFactory.createDataFlowValue(thenBranch, thenType, context);
thenDataFlowInfo = thenDataFlowInfo.assign(resultValue, thenValue);
thenDataFlowInfo = thenDataFlowInfo.assign(resultValue, thenValue, components.languageVersionSettings);
DataFlowValue elseValue = DataFlowValueFactory.createDataFlowValue(elseBranch, elseType, context);
elseDataFlowInfo = elseDataFlowInfo.assign(resultValue, elseValue);
elseDataFlowInfo = elseDataFlowInfo.assign(resultValue, elseValue, components.languageVersionSettings);
}
loopBreakContinuePossible |= thenTypeInfo.getJumpOutPossible() || elseTypeInfo.getJumpOutPossible();
@@ -229,7 +229,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
// Preliminary analysis
PreliminaryLoopVisitor loopVisitor = PreliminaryLoopVisitor.visitLoop(expression);
context = context.replaceDataFlowInfo(
loopVisitor.clearDataFlowInfoForAssignedLocalVariables(context.dataFlowInfo)
loopVisitor.clearDataFlowInfoForAssignedLocalVariables(context.dataFlowInfo, components.languageVersionSettings)
);
KtExpression condition = expression.getCondition();
@@ -261,7 +261,8 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
if (body != null && KtPsiUtil.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()));
dataFlowInfo = dataFlowInfo.and(loopVisitor.clearDataFlowInfoForAssignedLocalVariables(bodyTypeInfo.getJumpFlowInfo(),
components.languageVersionSettings));
}
return components.dataFlowAnalyzer
.checkType(bodyTypeInfo.replaceType(components.builtIns.getUnitType()), expression, contextWithExpectedType)
@@ -325,7 +326,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
// Preliminary analysis
PreliminaryLoopVisitor loopVisitor = PreliminaryLoopVisitor.visitLoop(expression);
context = context.replaceDataFlowInfo(
loopVisitor.clearDataFlowInfoForAssignedLocalVariables(context.dataFlowInfo)
loopVisitor.clearDataFlowInfoForAssignedLocalVariables(context.dataFlowInfo, components.languageVersionSettings)
);
// 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
@@ -369,7 +370,8 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
if (body != null) {
// We should take data flow info from the first jump point,
// but without affecting changing variables
dataFlowInfo = dataFlowInfo.and(loopVisitor.clearDataFlowInfoForAssignedLocalVariables(bodyTypeInfo.getJumpFlowInfo()));
dataFlowInfo = dataFlowInfo.and(loopVisitor.clearDataFlowInfoForAssignedLocalVariables(bodyTypeInfo.getJumpFlowInfo(),
components.languageVersionSettings));
}
return components.dataFlowAnalyzer
.checkType(bodyTypeInfo.replaceType(components.builtIns.getUnitType()), expression, contextWithExpectedType)
@@ -388,7 +390,8 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE).replaceContextDependency(INDEPENDENT);
// Preliminary analysis
PreliminaryLoopVisitor loopVisitor = PreliminaryLoopVisitor.visitLoop(expression);
context = context.replaceDataFlowInfo(loopVisitor.clearDataFlowInfoForAssignedLocalVariables(context.dataFlowInfo));
context = context.replaceDataFlowInfo(loopVisitor.clearDataFlowInfoForAssignedLocalVariables(context.dataFlowInfo,
components.languageVersionSettings));
KtExpression loopRange = expression.getLoopRange();
KotlinType expectedParameterType = null;
@@ -22,6 +22,7 @@ import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.config.LanguageVersionSettings;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.diagnostics.DiagnosticUtilsKt;
import org.jetbrains.kotlin.incremental.KotlinLookupLocation;
@@ -55,19 +56,22 @@ public class DataFlowAnalyzer {
private final KotlinBuiltIns builtIns;
private final SmartCastManager smartCastManager;
private final ExpressionTypingFacade facade;
private final LanguageVersionSettings languageVersionSettings;
public DataFlowAnalyzer(
@NotNull Iterable<AdditionalTypeChecker> additionalTypeCheckers,
@NotNull ConstantExpressionEvaluator constantExpressionEvaluator,
@NotNull KotlinBuiltIns builtIns,
@NotNull SmartCastManager smartCastManager,
@NotNull ExpressionTypingFacade facade
@NotNull ExpressionTypingFacade facade,
@NotNull LanguageVersionSettings languageVersionSettings
) {
this.additionalTypeCheckers = additionalTypeCheckers;
this.constantExpressionEvaluator = constantExpressionEvaluator;
this.builtIns = builtIns;
this.smartCastManager = smartCastManager;
this.facade = facade;
this.languageVersionSettings = languageVersionSettings;
}
// NB: use this method only for functions from 'Any'
@@ -176,10 +180,14 @@ public class DataFlowAnalyzer {
if (equals == conditionValue) { // this means: equals && conditionValue || !equals && !conditionValue
boolean byIdentity = operationToken == KtTokens.EQEQEQ || operationToken == KtTokens.EXCLEQEQEQ ||
typeHasEqualsFromAny(lhsType, condition);
result.set(context.dataFlowInfo.equate(leftValue, rightValue, byIdentity).and(expressionFlowInfo));
result.set(context.dataFlowInfo
.equate(leftValue, rightValue, byIdentity, languageVersionSettings)
.and(expressionFlowInfo));
}
else {
result.set(context.dataFlowInfo.disequate(leftValue, rightValue).and(expressionFlowInfo));
result.set(context.dataFlowInfo
.disequate(leftValue, rightValue, languageVersionSettings)
.and(expressionFlowInfo));
}
}
else {
@@ -274,7 +274,8 @@ public class ExpressionTypingServices {
statementExpression, result.getType(), context);
DataFlowValue blockExpressionValue = DataFlowValueFactory.createDataFlowValue(
(KtBlockExpression) statementExpression.getParent(), result.getType(), context);
result = result.replaceDataFlowInfo(result.getDataFlowInfo().assign(blockExpressionValue, lastExpressionValue));
result = result.replaceDataFlowInfo(result.getDataFlowInfo().assign(blockExpressionValue, lastExpressionValue,
expressionTypingComponents.languageVersionSettings));
}
}
else {
@@ -355,7 +355,7 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito
DataFlowValue leftValue = DataFlowValueFactory.createDataFlowValue(left, expectedType, context);
DataFlowValue rightValue = DataFlowValueFactory.createDataFlowValue(right, rightType, context);
// We cannot say here anything new about rightValue except it has the same value as leftValue
resultInfo = resultInfo.replaceDataFlowInfo(dataFlowInfo.assign(leftValue, rightValue));
resultInfo = resultInfo.replaceDataFlowInfo(dataFlowInfo.assign(leftValue, rightValue, components.languageVersionSettings));
}
}
else {
@@ -214,7 +214,7 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
val entryDataFlowInfo =
if (whenResultValue != null && entryType != null) {
val entryValue = DataFlowValueFactory.createDataFlowValue(entryExpression, entryType, contextAfterSubject)
entryTypeInfo.dataFlowInfo.assign(whenResultValue, entryValue)
entryTypeInfo.dataFlowInfo.assign(whenResultValue, entryValue, components.languageVersionSettings)
}
else {
entryTypeInfo.dataFlowInfo
@@ -407,8 +407,12 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
val result = noChange(newContext)
return ConditionalDataFlowInfo(
result.thenInfo.equate(subjectDataFlowValue, expressionDataFlowValue,
DataFlowAnalyzer.typeHasEqualsFromAny(subjectType, expression)),
result.elseInfo.disequate(subjectDataFlowValue, expressionDataFlowValue))
DataFlowAnalyzer.typeHasEqualsFromAny(subjectType, expression),
components.languageVersionSettings),
result.elseInfo.disequate(subjectDataFlowValue,
expressionDataFlowValue,
components.languageVersionSettings)
)
}
private fun checkTypeForIs(
@@ -438,7 +442,9 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
if (CastDiagnosticsUtil.isCastErased(subjectType, targetType, KotlinTypeChecker.DEFAULT)) {
context.trace.report(Errors.CANNOT_CHECK_FOR_ERASED.on(typeReferenceAfterIs, targetType))
}
return ConditionalDataFlowInfo(context.dataFlowInfo.establishSubtyping(subjectDataFlowValue, targetType), context.dataFlowInfo)
return context.dataFlowInfo.let {
ConditionalDataFlowInfo(it.establishSubtyping(subjectDataFlowValue, targetType, components.languageVersionSettings), it)
}
}
private fun noChange(context: ExpressionTypingContext) = ConditionalDataFlowInfo(context.dataFlowInfo)
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.types.expressions
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
import org.jetbrains.kotlin.psi.KtLoopExpression
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
@@ -29,7 +30,8 @@ import java.util.*
*/
class PreliminaryLoopVisitor private constructor() : AssignedVariablesSearcher() {
fun clearDataFlowInfoForAssignedLocalVariables(dataFlowInfo: DataFlowInfo): DataFlowInfo {
fun clearDataFlowInfoForAssignedLocalVariables(dataFlowInfo: DataFlowInfo,
languageVersionSettings: LanguageVersionSettings): DataFlowInfo {
var resultFlowInfo = dataFlowInfo
val nullabilityMap = resultFlowInfo.completeNullabilityInfo
val valueSetToClear = LinkedHashSet<DataFlowValue>()
@@ -44,7 +46,7 @@ class PreliminaryLoopVisitor private constructor() : AssignedVariablesSearcher()
}
}
for (valueToClear in valueSetToClear) {
resultFlowInfo = resultFlowInfo.clearValueInfo(valueToClear)
resultFlowInfo = resultFlowInfo.clearValueInfo(valueToClear, languageVersionSettings)
}
return resultFlowInfo
}
@@ -0,0 +1,52 @@
// !LANGUAGE: -SafeCallBoundSmartCasts
fun foo(arg: Int?) {
val x = arg
if (x != null) {
arg<!UNSAFE_CALL!>.<!>hashCode()
}
val y: Any? = arg
if (y != null) {
arg<!UNSAFE_CALL!>.<!>hashCode()
}
val yy: Any?
yy = arg
if (yy != null) {
arg<!UNSAFE_CALL!>.<!>hashCode()
}
var z = arg
z = z?.let { 42 }
if (z != null) {
arg<!UNSAFE_CALL!>.<!>hashCode()
}
}
fun kt6840_1(s: String?) {
val hash = s?.hashCode()
if (hash != null) {
s<!UNSAFE_CALL!>.<!>length
}
}
fun kt6840_2(s: String?) {
if (s?.hashCode() != null) {
s<!UNSAFE_CALL!>.<!>length
}
}
fun kt1635(s: String?) {
s?.hashCode()!!
s<!UNSAFE_CALL!>.<!>hashCode()
}
fun kt2127() {
val s: String? = ""
if (s?.length != null) {
s<!UNSAFE_CALL!>.<!>hashCode()
}
}
fun kt3356(s: String?): Int {
if (s?.length != 1) return 0
return s<!UNSAFE_CALL!>.<!>length
}
@@ -0,0 +1,8 @@
package
public fun foo(/*0*/ arg: kotlin.Int?): kotlin.Unit
public fun kt1635(/*0*/ s: kotlin.String?): kotlin.Unit
public fun kt2127(): kotlin.Unit
public fun kt3356(/*0*/ s: kotlin.String?): kotlin.Int
public fun kt6840_1(/*0*/ s: kotlin.String?): kotlin.Unit
public fun kt6840_2(/*0*/ s: kotlin.String?): kotlin.Unit
@@ -19453,6 +19453,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("level_1_0.kt")
public void testLevel_1_0() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/level_1_0.kt");
doTest(fileName);
}
@TestMetadata("localClassChanges.kt")
public void testLocalClassChanges() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/localClassChanges.kt");
@@ -39,6 +39,7 @@ enum class LanguageFeature(val sinceVersion: LanguageVersion?) {
OperatorProvideDelegate(KOTLIN_1_1),
ShortSyntaxForPropertyGetters(KOTLIN_1_1),
RefinedSamAdaptersPriority(KOTLIN_1_1),
SafeCallBoundSmartCasts(KOTLIN_1_1),
// Experimental features
MultiPlatformProjects(null),