Data flow information: take language version into account while processing safe calls and bound values #KT-14350 Fixed
This commit is contained in:
committed by
mglukhikh
parent
ebd577e52a
commit
905e67e713
@@ -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
|
||||
|
||||
+29
-17
@@ -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)
|
||||
|
||||
+19
-10
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-7
@@ -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 {
|
||||
|
||||
+2
-1
@@ -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 {
|
||||
|
||||
+1
-1
@@ -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 {
|
||||
|
||||
+10
-4
@@ -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)
|
||||
|
||||
+4
-2
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user