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))
|
DescriptorUtils.getContainingModuleOrNull(scope.ownerDescriptor))
|
||||||
// We cannot say here anything new about initializerDataFlowValue
|
// We cannot say here anything new about initializerDataFlowValue
|
||||||
// except it has the same value as variableDataFlowValue
|
// 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.PsiElement
|
||||||
import com.intellij.psi.util.PsiTreeUtil
|
import com.intellij.psi.util.PsiTreeUtil
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
|
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||||
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
|
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||||
@@ -70,7 +71,8 @@ class CallExpressionResolver(
|
|||||||
private val argumentTypeResolver: ArgumentTypeResolver,
|
private val argumentTypeResolver: ArgumentTypeResolver,
|
||||||
private val dataFlowAnalyzer: DataFlowAnalyzer,
|
private val dataFlowAnalyzer: DataFlowAnalyzer,
|
||||||
private val builtIns: KotlinBuiltIns,
|
private val builtIns: KotlinBuiltIns,
|
||||||
private val qualifiedExpressionResolver: QualifiedExpressionResolver
|
private val qualifiedExpressionResolver: QualifiedExpressionResolver,
|
||||||
|
private val languageVersionSettings: LanguageVersionSettings
|
||||||
) {
|
) {
|
||||||
private lateinit var expressionTypingServices: ExpressionTypingServices
|
private lateinit var expressionTypingServices: ExpressionTypingServices
|
||||||
|
|
||||||
@@ -323,7 +325,7 @@ class CallExpressionResolver(
|
|||||||
// Additional "receiver != null" information should be applied if we consider a safe call
|
// Additional "receiver != null" information should be applied if we consider a safe call
|
||||||
if (receiverCanBeNull) {
|
if (receiverCanBeNull) {
|
||||||
initialDataFlowInfoForArguments = initialDataFlowInfoForArguments.disequate(
|
initialDataFlowInfoForArguments = initialDataFlowInfoForArguments.disequate(
|
||||||
receiverDataFlowValue, DataFlowValue.nullValue(builtIns))
|
receiverDataFlowValue, DataFlowValue.nullValue(builtIns), languageVersionSettings)
|
||||||
}
|
}
|
||||||
else if (receiver is ReceiverValue) {
|
else if (receiver is ReceiverValue) {
|
||||||
reportUnnecessarySafeCall(context.trace, receiver.type, element.node, receiver)
|
reportUnnecessarySafeCall(context.trace, receiver.type, element.node, receiver)
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package org.jetbrains.kotlin.resolve.calls.smartcasts
|
package org.jetbrains.kotlin.resolve.calls.smartcasts
|
||||||
|
|
||||||
import com.google.common.collect.SetMultimap
|
import com.google.common.collect.SetMultimap
|
||||||
|
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -61,25 +62,25 @@ interface DataFlowInfo {
|
|||||||
* Call this function to clear all data flow information about
|
* 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.
|
* 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
|
* 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.
|
* 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
|
* 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
|
* 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
|
* 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 com.google.common.collect.*
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
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.*
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.TypeUtils
|
import org.jetbrains.kotlin.types.TypeUtils
|
||||||
@@ -85,21 +87,25 @@ internal class DelegatingDataFlowInfo private constructor(
|
|||||||
nullabilityInfo[key] ?: parent?.getCollectedNullability(key) ?: key.immanentNullability
|
nullabilityInfo[key] ?: parent?.getCollectedNullability(key) ?: key.immanentNullability
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun putNullability(map: MutableMap<DataFlowValue, Nullability>, value: DataFlowValue,
|
private fun putNullability(map: MutableMap<DataFlowValue, Nullability>,
|
||||||
nullability: Nullability, affectReceiver: Boolean = true): Boolean {
|
value: DataFlowValue,
|
||||||
|
nullability: Nullability,
|
||||||
|
languageVersionSettings: LanguageVersionSettings,
|
||||||
|
affectReceiver: Boolean = true): Boolean {
|
||||||
map.put(value, nullability)
|
map.put(value, nullability)
|
||||||
|
|
||||||
val identifierInfo = value.identifierInfo
|
val identifierInfo = value.identifierInfo
|
||||||
if (affectReceiver && !nullability.canBeNull()) {
|
if (affectReceiver && !nullability.canBeNull() &&
|
||||||
|
languageVersionSettings.supportsFeature(LanguageFeature.SafeCallBoundSmartCasts)) {
|
||||||
when (identifierInfo) {
|
when (identifierInfo) {
|
||||||
is IdentifierInfo.Qualified -> {
|
is IdentifierInfo.Qualified -> {
|
||||||
val receiverType = identifierInfo.receiverType
|
val receiverType = identifierInfo.receiverType
|
||||||
if (identifierInfo.safe && receiverType != null) {
|
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 {
|
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
|
* @param value
|
||||||
*/
|
*/
|
||||||
override fun clearValueInfo(value: DataFlowValue): DataFlowInfo {
|
override fun clearValueInfo(value: DataFlowValue, languageVersionSettings: LanguageVersionSettings): DataFlowInfo {
|
||||||
val builder = Maps.newHashMap<DataFlowValue, Nullability>()
|
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)
|
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 nullability = Maps.newHashMap<DataFlowValue, Nullability>()
|
||||||
val nullabilityOfB = getStableNullability(b)
|
val nullabilityOfB = getStableNullability(b)
|
||||||
putNullability(nullability, a, nullabilityOfB, affectReceiver = false)
|
putNullability(nullability, a, nullabilityOfB, languageVersionSettings, affectReceiver = false)
|
||||||
|
|
||||||
val newTypeInfo = newTypeInfo()
|
val newTypeInfo = newTypeInfo()
|
||||||
var typesForB = getStableTypes(b)
|
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)
|
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 builder = Maps.newHashMap<DataFlowValue, Nullability>()
|
||||||
val nullabilityOfA = getStableNullability(a)
|
val nullabilityOfA = getStableNullability(a)
|
||||||
val nullabilityOfB = getStableNullability(b)
|
val nullabilityOfB = getStableNullability(b)
|
||||||
|
|
||||||
var changed = putNullability(builder, a, nullabilityOfA.refine(nullabilityOfB)) or
|
var changed = putNullability(builder, a, nullabilityOfA.refine(nullabilityOfB), languageVersionSettings) or
|
||||||
putNullability(builder, b, nullabilityOfB.refine(nullabilityOfA))
|
putNullability(builder, b, nullabilityOfB.refine(nullabilityOfA), languageVersionSettings)
|
||||||
|
|
||||||
// NB: == has no guarantees of type equality, see KT-11280 for the example
|
// NB: == has no guarantees of type equality, see KT-11280 for the example
|
||||||
val newTypeInfo = newTypeInfo()
|
val newTypeInfo = newTypeInfo()
|
||||||
@@ -219,17 +227,21 @@ internal class DelegatingDataFlowInfo private constructor(
|
|||||||
return types
|
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 builder = Maps.newHashMap<DataFlowValue, Nullability>()
|
||||||
val nullabilityOfA = getStableNullability(a)
|
val nullabilityOfA = getStableNullability(a)
|
||||||
val nullabilityOfB = getStableNullability(b)
|
val nullabilityOfB = getStableNullability(b)
|
||||||
|
|
||||||
val changed = putNullability(builder, a, nullabilityOfA.refine(nullabilityOfB.invert())) or
|
val changed = putNullability(builder, a, nullabilityOfA.refine(nullabilityOfB.invert()), languageVersionSettings) or
|
||||||
putNullability(builder, b, nullabilityOfB.refine(nullabilityOfA.invert()))
|
putNullability(builder, b, nullabilityOfB.refine(nullabilityOfA.invert()), languageVersionSettings)
|
||||||
return if (changed) create(this, ImmutableMap.copyOf(builder), EMPTY_TYPE_INFO) else this
|
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 (value.type == type) return this
|
||||||
if (getCollectedTypes(value).contains(type)) return this
|
if (getCollectedTypes(value).contains(type)) return this
|
||||||
if (!value.type.isFlexible() && value.type.isSubtypeOf(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)
|
newTypeInfo.put(value, type)
|
||||||
val builder = Maps.newHashMap<DataFlowValue, Nullability>()
|
val builder = Maps.newHashMap<DataFlowValue, Nullability>()
|
||||||
if (!type.isMarkedNullable) {
|
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)
|
val newNullabilityInfo = if (type.isMarkedNullable) EMPTY_NULLABILITY_INFO else ImmutableMap.copyOf(builder)
|
||||||
return create(this, newNullabilityInfo, newTypeInfo)
|
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.KtNodeTypes;
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||||
import org.jetbrains.kotlin.config.LanguageFeature;
|
import org.jetbrains.kotlin.config.LanguageFeature;
|
||||||
|
import org.jetbrains.kotlin.config.LanguageVersionSettings;
|
||||||
import org.jetbrains.kotlin.descriptors.*;
|
import org.jetbrains.kotlin.descriptors.*;
|
||||||
import org.jetbrains.kotlin.diagnostics.Diagnostic;
|
import org.jetbrains.kotlin.diagnostics.Diagnostic;
|
||||||
import org.jetbrains.kotlin.diagnostics.Errors;
|
import org.jetbrains.kotlin.diagnostics.Errors;
|
||||||
@@ -182,7 +183,8 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
if (resultType != null) {
|
if (resultType != null) {
|
||||||
DataFlowValue innerValue = DataFlowValueFactory.createDataFlowValue(innerExpression, resultType, context);
|
DataFlowValue innerValue = DataFlowValueFactory.createDataFlowValue(innerExpression, resultType, context);
|
||||||
DataFlowValue resultValue = DataFlowValueFactory.createDataFlowValue(expression, 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;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -316,7 +318,8 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
DataFlowInfo dataFlowInfo = typeInfo.getDataFlowInfo();
|
DataFlowInfo dataFlowInfo = typeInfo.getDataFlowInfo();
|
||||||
if (operationType == AS_KEYWORD) {
|
if (operationType == AS_KEYWORD) {
|
||||||
DataFlowValue value = createDataFlowValue(left, subjectType, context);
|
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;
|
result = receiverType;
|
||||||
// Also record data flow information for x++ value (= x)
|
// Also record data flow information for x++ value (= x)
|
||||||
DataFlowValue returnValue = DataFlowValueFactory.createDataFlowValue(expression, receiverType, contextWithExpectedType);
|
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 {
|
else {
|
||||||
DataFlowValue value = createDataFlowValue(baseExpression, baseType, context);
|
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);
|
KotlinType resultingType = TypeUtils.makeNotNullable(baseType);
|
||||||
if (context.contextDependency == DEPENDENT) {
|
if (context.contextDependency == DEPENDENT) {
|
||||||
@@ -1320,16 +1325,19 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
DataFlowValue nullValue = DataFlowValue.nullValue(components.builtIns);
|
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
|
// 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()) {
|
if (jumpInRight || !rightDataFlowInfo.getStableNullability(leftValue).canBeNull()) {
|
||||||
dataFlowInfo = dataFlowInfo.disequate(leftValue, nullValue);
|
dataFlowInfo = dataFlowInfo.disequate(leftValue, nullValue, components.languageVersionSettings);
|
||||||
if (left instanceof KtBinaryExpressionWithTypeRHS) {
|
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);
|
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) {
|
if (!jumpInRight) {
|
||||||
DataFlowValue rightValue = DataFlowValueFactory.createDataFlowValue(right, rightType, context);
|
DataFlowValue rightValue = DataFlowValueFactory.createDataFlowValue(right, rightType, context);
|
||||||
rightDataFlowInfo = rightDataFlowInfo.assign(resultValue, rightValue);
|
rightDataFlowInfo = rightDataFlowInfo.assign(resultValue, rightValue, components.languageVersionSettings);
|
||||||
dataFlowInfo = dataFlowInfo.or(rightDataFlowInfo);
|
dataFlowInfo = dataFlowInfo.or(rightDataFlowInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1354,7 +1362,8 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
private static DataFlowInfo establishSubtypingForTypeRHS(
|
private static DataFlowInfo establishSubtypingForTypeRHS(
|
||||||
@NotNull KtBinaryExpressionWithTypeRHS left,
|
@NotNull KtBinaryExpressionWithTypeRHS left,
|
||||||
@NotNull DataFlowInfo dataFlowInfo,
|
@NotNull DataFlowInfo dataFlowInfo,
|
||||||
@NotNull ExpressionTypingContext context
|
@NotNull ExpressionTypingContext context,
|
||||||
|
@NotNull LanguageVersionSettings languageVersionSettings
|
||||||
) {
|
) {
|
||||||
IElementType operationType = left.getOperationReference().getReferencedNameElementType();
|
IElementType operationType = left.getOperationReference().getReferencedNameElementType();
|
||||||
if (operationType == AS_SAFE) {
|
if (operationType == AS_SAFE) {
|
||||||
@@ -1364,7 +1373,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
DataFlowValue underSafeAsValue = createDataFlowValue(underSafeAs, underSafeAsType, context);
|
DataFlowValue underSafeAsValue = createDataFlowValue(underSafeAs, underSafeAsType, context);
|
||||||
KotlinType targetType = context.trace.get(BindingContext.TYPE, left.getRight());
|
KotlinType targetType = context.trace.get(BindingContext.TYPE, left.getRight());
|
||||||
if (targetType != null) {
|
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) {
|
if (resultType != null && thenType != null && elseType != null) {
|
||||||
DataFlowValue resultValue = DataFlowValueFactory.createDataFlowValue(ifExpression, resultType, context);
|
DataFlowValue resultValue = DataFlowValueFactory.createDataFlowValue(ifExpression, resultType, context);
|
||||||
DataFlowValue thenValue = DataFlowValueFactory.createDataFlowValue(thenBranch, thenType, 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);
|
DataFlowValue elseValue = DataFlowValueFactory.createDataFlowValue(elseBranch, elseType, context);
|
||||||
elseDataFlowInfo = elseDataFlowInfo.assign(resultValue, elseValue);
|
elseDataFlowInfo = elseDataFlowInfo.assign(resultValue, elseValue, components.languageVersionSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
loopBreakContinuePossible |= thenTypeInfo.getJumpOutPossible() || elseTypeInfo.getJumpOutPossible();
|
loopBreakContinuePossible |= thenTypeInfo.getJumpOutPossible() || elseTypeInfo.getJumpOutPossible();
|
||||||
@@ -229,7 +229,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
// Preliminary analysis
|
// Preliminary analysis
|
||||||
PreliminaryLoopVisitor loopVisitor = PreliminaryLoopVisitor.visitLoop(expression);
|
PreliminaryLoopVisitor loopVisitor = PreliminaryLoopVisitor.visitLoop(expression);
|
||||||
context = context.replaceDataFlowInfo(
|
context = context.replaceDataFlowInfo(
|
||||||
loopVisitor.clearDataFlowInfoForAssignedLocalVariables(context.dataFlowInfo)
|
loopVisitor.clearDataFlowInfoForAssignedLocalVariables(context.dataFlowInfo, components.languageVersionSettings)
|
||||||
);
|
);
|
||||||
|
|
||||||
KtExpression condition = expression.getCondition();
|
KtExpression condition = expression.getCondition();
|
||||||
@@ -261,7 +261,8 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
if (body != null && KtPsiUtil.isTrueConstant(condition)) {
|
if (body != null && KtPsiUtil.isTrueConstant(condition)) {
|
||||||
// We should take data flow info from the first jump point,
|
// We should take data flow info from the first jump point,
|
||||||
// but without affecting changing variables
|
// but without affecting changing variables
|
||||||
dataFlowInfo = dataFlowInfo.and(loopVisitor.clearDataFlowInfoForAssignedLocalVariables(bodyTypeInfo.getJumpFlowInfo()));
|
dataFlowInfo = dataFlowInfo.and(loopVisitor.clearDataFlowInfoForAssignedLocalVariables(bodyTypeInfo.getJumpFlowInfo(),
|
||||||
|
components.languageVersionSettings));
|
||||||
}
|
}
|
||||||
return components.dataFlowAnalyzer
|
return components.dataFlowAnalyzer
|
||||||
.checkType(bodyTypeInfo.replaceType(components.builtIns.getUnitType()), expression, contextWithExpectedType)
|
.checkType(bodyTypeInfo.replaceType(components.builtIns.getUnitType()), expression, contextWithExpectedType)
|
||||||
@@ -325,7 +326,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
// Preliminary analysis
|
// Preliminary analysis
|
||||||
PreliminaryLoopVisitor loopVisitor = PreliminaryLoopVisitor.visitLoop(expression);
|
PreliminaryLoopVisitor loopVisitor = PreliminaryLoopVisitor.visitLoop(expression);
|
||||||
context = context.replaceDataFlowInfo(
|
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
|
// 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
|
// .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) {
|
if (body != null) {
|
||||||
// We should take data flow info from the first jump point,
|
// We should take data flow info from the first jump point,
|
||||||
// but without affecting changing variables
|
// but without affecting changing variables
|
||||||
dataFlowInfo = dataFlowInfo.and(loopVisitor.clearDataFlowInfoForAssignedLocalVariables(bodyTypeInfo.getJumpFlowInfo()));
|
dataFlowInfo = dataFlowInfo.and(loopVisitor.clearDataFlowInfoForAssignedLocalVariables(bodyTypeInfo.getJumpFlowInfo(),
|
||||||
|
components.languageVersionSettings));
|
||||||
}
|
}
|
||||||
return components.dataFlowAnalyzer
|
return components.dataFlowAnalyzer
|
||||||
.checkType(bodyTypeInfo.replaceType(components.builtIns.getUnitType()), expression, contextWithExpectedType)
|
.checkType(bodyTypeInfo.replaceType(components.builtIns.getUnitType()), expression, contextWithExpectedType)
|
||||||
@@ -388,7 +390,8 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE).replaceContextDependency(INDEPENDENT);
|
contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE).replaceContextDependency(INDEPENDENT);
|
||||||
// Preliminary analysis
|
// Preliminary analysis
|
||||||
PreliminaryLoopVisitor loopVisitor = PreliminaryLoopVisitor.visitLoop(expression);
|
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();
|
KtExpression loopRange = expression.getLoopRange();
|
||||||
KotlinType expectedParameterType = null;
|
KotlinType expectedParameterType = null;
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import com.intellij.psi.tree.IElementType;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||||
|
import org.jetbrains.kotlin.config.LanguageVersionSettings;
|
||||||
import org.jetbrains.kotlin.descriptors.*;
|
import org.jetbrains.kotlin.descriptors.*;
|
||||||
import org.jetbrains.kotlin.diagnostics.DiagnosticUtilsKt;
|
import org.jetbrains.kotlin.diagnostics.DiagnosticUtilsKt;
|
||||||
import org.jetbrains.kotlin.incremental.KotlinLookupLocation;
|
import org.jetbrains.kotlin.incremental.KotlinLookupLocation;
|
||||||
@@ -55,19 +56,22 @@ public class DataFlowAnalyzer {
|
|||||||
private final KotlinBuiltIns builtIns;
|
private final KotlinBuiltIns builtIns;
|
||||||
private final SmartCastManager smartCastManager;
|
private final SmartCastManager smartCastManager;
|
||||||
private final ExpressionTypingFacade facade;
|
private final ExpressionTypingFacade facade;
|
||||||
|
private final LanguageVersionSettings languageVersionSettings;
|
||||||
|
|
||||||
public DataFlowAnalyzer(
|
public DataFlowAnalyzer(
|
||||||
@NotNull Iterable<AdditionalTypeChecker> additionalTypeCheckers,
|
@NotNull Iterable<AdditionalTypeChecker> additionalTypeCheckers,
|
||||||
@NotNull ConstantExpressionEvaluator constantExpressionEvaluator,
|
@NotNull ConstantExpressionEvaluator constantExpressionEvaluator,
|
||||||
@NotNull KotlinBuiltIns builtIns,
|
@NotNull KotlinBuiltIns builtIns,
|
||||||
@NotNull SmartCastManager smartCastManager,
|
@NotNull SmartCastManager smartCastManager,
|
||||||
@NotNull ExpressionTypingFacade facade
|
@NotNull ExpressionTypingFacade facade,
|
||||||
|
@NotNull LanguageVersionSettings languageVersionSettings
|
||||||
) {
|
) {
|
||||||
this.additionalTypeCheckers = additionalTypeCheckers;
|
this.additionalTypeCheckers = additionalTypeCheckers;
|
||||||
this.constantExpressionEvaluator = constantExpressionEvaluator;
|
this.constantExpressionEvaluator = constantExpressionEvaluator;
|
||||||
this.builtIns = builtIns;
|
this.builtIns = builtIns;
|
||||||
this.smartCastManager = smartCastManager;
|
this.smartCastManager = smartCastManager;
|
||||||
this.facade = facade;
|
this.facade = facade;
|
||||||
|
this.languageVersionSettings = languageVersionSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NB: use this method only for functions from 'Any'
|
// 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
|
if (equals == conditionValue) { // this means: equals && conditionValue || !equals && !conditionValue
|
||||||
boolean byIdentity = operationToken == KtTokens.EQEQEQ || operationToken == KtTokens.EXCLEQEQEQ ||
|
boolean byIdentity = operationToken == KtTokens.EQEQEQ || operationToken == KtTokens.EXCLEQEQEQ ||
|
||||||
typeHasEqualsFromAny(lhsType, condition);
|
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 {
|
else {
|
||||||
result.set(context.dataFlowInfo.disequate(leftValue, rightValue).and(expressionFlowInfo));
|
result.set(context.dataFlowInfo
|
||||||
|
.disequate(leftValue, rightValue, languageVersionSettings)
|
||||||
|
.and(expressionFlowInfo));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
+2
-1
@@ -274,7 +274,8 @@ public class ExpressionTypingServices {
|
|||||||
statementExpression, result.getType(), context);
|
statementExpression, result.getType(), context);
|
||||||
DataFlowValue blockExpressionValue = DataFlowValueFactory.createDataFlowValue(
|
DataFlowValue blockExpressionValue = DataFlowValueFactory.createDataFlowValue(
|
||||||
(KtBlockExpression) statementExpression.getParent(), result.getType(), context);
|
(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 {
|
else {
|
||||||
|
|||||||
+1
-1
@@ -355,7 +355,7 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito
|
|||||||
DataFlowValue leftValue = DataFlowValueFactory.createDataFlowValue(left, expectedType, context);
|
DataFlowValue leftValue = DataFlowValueFactory.createDataFlowValue(left, expectedType, context);
|
||||||
DataFlowValue rightValue = DataFlowValueFactory.createDataFlowValue(right, rightType, context);
|
DataFlowValue rightValue = DataFlowValueFactory.createDataFlowValue(right, rightType, context);
|
||||||
// We cannot say here anything new about rightValue except it has the same value as leftValue
|
// 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 {
|
else {
|
||||||
|
|||||||
+10
-4
@@ -214,7 +214,7 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
|||||||
val entryDataFlowInfo =
|
val entryDataFlowInfo =
|
||||||
if (whenResultValue != null && entryType != null) {
|
if (whenResultValue != null && entryType != null) {
|
||||||
val entryValue = DataFlowValueFactory.createDataFlowValue(entryExpression, entryType, contextAfterSubject)
|
val entryValue = DataFlowValueFactory.createDataFlowValue(entryExpression, entryType, contextAfterSubject)
|
||||||
entryTypeInfo.dataFlowInfo.assign(whenResultValue, entryValue)
|
entryTypeInfo.dataFlowInfo.assign(whenResultValue, entryValue, components.languageVersionSettings)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
entryTypeInfo.dataFlowInfo
|
entryTypeInfo.dataFlowInfo
|
||||||
@@ -407,8 +407,12 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
|||||||
val result = noChange(newContext)
|
val result = noChange(newContext)
|
||||||
return ConditionalDataFlowInfo(
|
return ConditionalDataFlowInfo(
|
||||||
result.thenInfo.equate(subjectDataFlowValue, expressionDataFlowValue,
|
result.thenInfo.equate(subjectDataFlowValue, expressionDataFlowValue,
|
||||||
DataFlowAnalyzer.typeHasEqualsFromAny(subjectType, expression)),
|
DataFlowAnalyzer.typeHasEqualsFromAny(subjectType, expression),
|
||||||
result.elseInfo.disequate(subjectDataFlowValue, expressionDataFlowValue))
|
components.languageVersionSettings),
|
||||||
|
result.elseInfo.disequate(subjectDataFlowValue,
|
||||||
|
expressionDataFlowValue,
|
||||||
|
components.languageVersionSettings)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkTypeForIs(
|
private fun checkTypeForIs(
|
||||||
@@ -438,7 +442,9 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
|||||||
if (CastDiagnosticsUtil.isCastErased(subjectType, targetType, KotlinTypeChecker.DEFAULT)) {
|
if (CastDiagnosticsUtil.isCastErased(subjectType, targetType, KotlinTypeChecker.DEFAULT)) {
|
||||||
context.trace.report(Errors.CANNOT_CHECK_FOR_ERASED.on(typeReferenceAfterIs, targetType))
|
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)
|
private fun noChange(context: ExpressionTypingContext) = ConditionalDataFlowInfo(context.dataFlowInfo)
|
||||||
|
|||||||
+4
-2
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.types.expressions
|
package org.jetbrains.kotlin.types.expressions
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
||||||
import org.jetbrains.kotlin.psi.KtLoopExpression
|
import org.jetbrains.kotlin.psi.KtLoopExpression
|
||||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
|
||||||
@@ -29,7 +30,8 @@ import java.util.*
|
|||||||
*/
|
*/
|
||||||
class PreliminaryLoopVisitor private constructor() : AssignedVariablesSearcher() {
|
class PreliminaryLoopVisitor private constructor() : AssignedVariablesSearcher() {
|
||||||
|
|
||||||
fun clearDataFlowInfoForAssignedLocalVariables(dataFlowInfo: DataFlowInfo): DataFlowInfo {
|
fun clearDataFlowInfoForAssignedLocalVariables(dataFlowInfo: DataFlowInfo,
|
||||||
|
languageVersionSettings: LanguageVersionSettings): DataFlowInfo {
|
||||||
var resultFlowInfo = dataFlowInfo
|
var resultFlowInfo = dataFlowInfo
|
||||||
val nullabilityMap = resultFlowInfo.completeNullabilityInfo
|
val nullabilityMap = resultFlowInfo.completeNullabilityInfo
|
||||||
val valueSetToClear = LinkedHashSet<DataFlowValue>()
|
val valueSetToClear = LinkedHashSet<DataFlowValue>()
|
||||||
@@ -44,7 +46,7 @@ class PreliminaryLoopVisitor private constructor() : AssignedVariablesSearcher()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (valueToClear in valueSetToClear) {
|
for (valueToClear in valueSetToClear) {
|
||||||
resultFlowInfo = resultFlowInfo.clearValueInfo(valueToClear)
|
resultFlowInfo = resultFlowInfo.clearValueInfo(valueToClear, languageVersionSettings)
|
||||||
}
|
}
|
||||||
return resultFlowInfo
|
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);
|
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")
|
@TestMetadata("localClassChanges.kt")
|
||||||
public void testLocalClassChanges() throws Exception {
|
public void testLocalClassChanges() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/localClassChanges.kt");
|
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),
|
OperatorProvideDelegate(KOTLIN_1_1),
|
||||||
ShortSyntaxForPropertyGetters(KOTLIN_1_1),
|
ShortSyntaxForPropertyGetters(KOTLIN_1_1),
|
||||||
RefinedSamAdaptersPriority(KOTLIN_1_1),
|
RefinedSamAdaptersPriority(KOTLIN_1_1),
|
||||||
|
SafeCallBoundSmartCasts(KOTLIN_1_1),
|
||||||
|
|
||||||
// Experimental features
|
// Experimental features
|
||||||
MultiPlatformProjects(null),
|
MultiPlatformProjects(null),
|
||||||
|
|||||||
Reference in New Issue
Block a user