Data flow information: stable complex expressions introduced back for brackets / elvis / if / when #KT-13468 Fixed
This reverts commite515d7f773and also commit1c9f08e986(cherry picked from commit 9fa155b)
This commit is contained in:
committed by
Mikhail Glukhikh
parent
86bc49558d
commit
b6443eed9e
+3
-1
@@ -39,6 +39,8 @@ class DataFlowValue(val identifierInfo: IdentifierInfo,
|
|||||||
// or protected / public member value from the same module without open / custom getter
|
// or protected / public member value from the same module without open / custom getter
|
||||||
// Smart casts are completely safe
|
// Smart casts are completely safe
|
||||||
STABLE_VALUE("stable val"),
|
STABLE_VALUE("stable val"),
|
||||||
|
// Block, or if / else, or when
|
||||||
|
STABLE_COMPLEX_EXPRESSION("complex expression", ""),
|
||||||
// Member value with open / custom getter
|
// Member value with open / custom getter
|
||||||
// Smart casts are not safe
|
// Smart casts are not safe
|
||||||
PROPERTY_WITH_GETTER("custom getter", "property that has open or custom getter"),
|
PROPERTY_WITH_GETTER("custom getter", "property that has open or custom getter"),
|
||||||
@@ -65,7 +67,7 @@ class DataFlowValue(val identifierInfo: IdentifierInfo,
|
|||||||
* Stable means here we do not expect some sudden change of their values,
|
* Stable means here we do not expect some sudden change of their values,
|
||||||
* like accessing mutable properties in another thread, so smart casts can be used safely.
|
* like accessing mutable properties in another thread, so smart casts can be used safely.
|
||||||
*/
|
*/
|
||||||
val isStable = (kind == Kind.STABLE_VALUE || kind == Kind.STABLE_VARIABLE)
|
val isStable = (kind == Kind.STABLE_VALUE || kind == Kind.STABLE_VARIABLE || kind == Kind.STABLE_COMPLEX_EXPRESSION)
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
|
|||||||
+26
-2
@@ -57,6 +57,16 @@ object DataFlowValueFactory {
|
|||||||
resolutionContext: ResolutionContext<*>
|
resolutionContext: ResolutionContext<*>
|
||||||
) = createDataFlowValue(expression, type, resolutionContext.trace.bindingContext, resolutionContext.scope.ownerDescriptor)
|
) = createDataFlowValue(expression, type, resolutionContext.trace.bindingContext, resolutionContext.scope.ownerDescriptor)
|
||||||
|
|
||||||
|
private fun isComplexExpression(expression: KtExpression): Boolean = when(expression) {
|
||||||
|
is KtBlockExpression, is KtIfExpression, is KtWhenExpression -> true
|
||||||
|
is KtBinaryExpression -> expression.operationToken === KtTokens.ELVIS
|
||||||
|
is KtParenthesizedExpression -> {
|
||||||
|
val deparenthesized = KtPsiUtil.deparenthesize(expression)
|
||||||
|
deparenthesized != null && isComplexExpression(deparenthesized)
|
||||||
|
}
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun createDataFlowValue(
|
fun createDataFlowValue(
|
||||||
expression: KtExpression,
|
expression: KtExpression,
|
||||||
@@ -85,6 +95,10 @@ object DataFlowValueFactory {
|
|||||||
Nullability.NOT_NULL)
|
Nullability.NOT_NULL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isComplexExpression(expression)) {
|
||||||
|
return createDataFlowValueForComplexExpression(expression, type)
|
||||||
|
}
|
||||||
|
|
||||||
val result = getIdForStableIdentifier(expression, bindingContext, containingDeclarationOrModule)
|
val result = getIdForStableIdentifier(expression, bindingContext, containingDeclarationOrModule)
|
||||||
return DataFlowValue(if (result === IdentifierInfo.NO) ExpressionIdentifierInfo(expression) else result, type)
|
return DataFlowValue(if (result === IdentifierInfo.NO) ExpressionIdentifierInfo(expression) else result, type)
|
||||||
}
|
}
|
||||||
@@ -124,6 +138,11 @@ object DataFlowValueFactory {
|
|||||||
bindingContext[BOUND_INITIALIZER_VALUE, variableDescriptor]),
|
bindingContext[BOUND_INITIALIZER_VALUE, variableDescriptor]),
|
||||||
variableDescriptor.type)
|
variableDescriptor.type)
|
||||||
|
|
||||||
|
private fun createDataFlowValueForComplexExpression(
|
||||||
|
expression: KtExpression,
|
||||||
|
type: KotlinType
|
||||||
|
) = DataFlowValue(ExpressionIdentifierInfo(expression, stableComplex = true), type)
|
||||||
|
|
||||||
// For only ++ and -- postfix operations
|
// For only ++ and -- postfix operations
|
||||||
private data class PostfixIdentifierInfo(val argumentInfo: IdentifierInfo, val op: KtToken) : IdentifierInfo {
|
private data class PostfixIdentifierInfo(val argumentInfo: IdentifierInfo, val op: KtToken) : IdentifierInfo {
|
||||||
override val kind: DataFlowValue.Kind get() = argumentInfo.kind
|
override val kind: DataFlowValue.Kind get() = argumentInfo.kind
|
||||||
@@ -131,8 +150,13 @@ object DataFlowValueFactory {
|
|||||||
override fun toString() = "$argumentInfo($op)"
|
override fun toString() = "$argumentInfo($op)"
|
||||||
}
|
}
|
||||||
|
|
||||||
data class ExpressionIdentifierInfo(val expression: KtExpression) : IdentifierInfo {
|
class ExpressionIdentifierInfo(val expression: KtExpression, stableComplex: Boolean = false) : IdentifierInfo {
|
||||||
override val kind = OTHER
|
|
||||||
|
override val kind = if (stableComplex) STABLE_COMPLEX_EXPRESSION else OTHER
|
||||||
|
|
||||||
|
override fun equals(other: Any?) = other is ExpressionIdentifierInfo && expression == other.expression
|
||||||
|
|
||||||
|
override fun hashCode() = expression.hashCode()
|
||||||
|
|
||||||
override fun toString() = expression.text ?: "(empty expression)"
|
override fun toString() = expression.text ?: "(empty expression)"
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-2
@@ -177,7 +177,14 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
if (innerExpression == null) {
|
if (innerExpression == null) {
|
||||||
return TypeInfoFactoryKt.noTypeInfo(context);
|
return TypeInfoFactoryKt.noTypeInfo(context);
|
||||||
}
|
}
|
||||||
return facade.getTypeInfo(innerExpression, context.replaceScope(context.scope));
|
KotlinTypeInfo result = facade.getTypeInfo(innerExpression, context.replaceScope(context.scope));
|
||||||
|
KotlinType resultType = result.getType();
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1319,8 +1326,10 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataFlowValue resultValue = DataFlowValueFactory.createDataFlowValue(expression, type, context);
|
DataFlowValue resultValue = DataFlowValueFactory.createDataFlowValue(expression, type, context);
|
||||||
dataFlowInfo = dataFlowInfo.disequate(resultValue, nullValue);
|
dataFlowInfo = dataFlowInfo.assign(resultValue, leftValue).disequate(resultValue, nullValue);
|
||||||
if (!jumpInRight) {
|
if (!jumpInRight) {
|
||||||
|
DataFlowValue rightValue = DataFlowValueFactory.createDataFlowValue(right, rightType, context);
|
||||||
|
rightDataFlowInfo = rightDataFlowInfo.assign(resultValue, rightValue);
|
||||||
dataFlowInfo = dataFlowInfo.or(rightDataFlowInfo);
|
dataFlowInfo = dataFlowInfo.or(rightDataFlowInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
@@ -31,6 +31,8 @@ import org.jetbrains.kotlin.resolve.ModifiersChecker;
|
|||||||
import org.jetbrains.kotlin.resolve.calls.model.MutableDataFlowInfoForArguments;
|
import org.jetbrains.kotlin.resolve.calls.model.MutableDataFlowInfoForArguments;
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
||||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue;
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory;
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
|
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
|
||||||
import org.jetbrains.kotlin.resolve.inline.InlineUtil;
|
import org.jetbrains.kotlin.resolve.inline.InlineUtil;
|
||||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope;
|
import org.jetbrains.kotlin.resolve.scopes.LexicalScope;
|
||||||
@@ -150,6 +152,13 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
KotlinType elseType = elseTypeInfo.getType();
|
KotlinType elseType = elseTypeInfo.getType();
|
||||||
DataFlowInfo thenDataFlowInfo = thenTypeInfo.getDataFlowInfo();
|
DataFlowInfo thenDataFlowInfo = thenTypeInfo.getDataFlowInfo();
|
||||||
DataFlowInfo elseDataFlowInfo = elseTypeInfo.getDataFlowInfo();
|
DataFlowInfo elseDataFlowInfo = elseTypeInfo.getDataFlowInfo();
|
||||||
|
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);
|
||||||
|
DataFlowValue elseValue = DataFlowValueFactory.createDataFlowValue(elseBranch, elseType, context);
|
||||||
|
elseDataFlowInfo = elseDataFlowInfo.assign(resultValue, elseValue);
|
||||||
|
}
|
||||||
|
|
||||||
loopBreakContinuePossible |= thenTypeInfo.getJumpOutPossible() || elseTypeInfo.getJumpOutPossible();
|
loopBreakContinuePossible |= thenTypeInfo.getJumpOutPossible() || elseTypeInfo.getJumpOutPossible();
|
||||||
|
|
||||||
|
|||||||
+9
@@ -30,6 +30,8 @@ import org.jetbrains.kotlin.resolve.*;
|
|||||||
import org.jetbrains.kotlin.resolve.calls.context.ContextDependency;
|
import org.jetbrains.kotlin.resolve.calls.context.ContextDependency;
|
||||||
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext;
|
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext;
|
||||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue;
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory;
|
||||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope;
|
import org.jetbrains.kotlin.resolve.scopes.LexicalScope;
|
||||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScopeKind;
|
import org.jetbrains.kotlin.resolve.scopes.LexicalScopeKind;
|
||||||
import org.jetbrains.kotlin.resolve.scopes.LexicalWritableScope;
|
import org.jetbrains.kotlin.resolve.scopes.LexicalWritableScope;
|
||||||
@@ -267,6 +269,13 @@ public class ExpressionTypingServices {
|
|||||||
result = getTypeOfLastExpressionInBlock(
|
result = getTypeOfLastExpressionInBlock(
|
||||||
statementExpression, newContext.replaceExpectedType(context.expectedType), coercionStrategyForLastExpression,
|
statementExpression, newContext.replaceExpectedType(context.expectedType), coercionStrategyForLastExpression,
|
||||||
blockLevelVisitor);
|
blockLevelVisitor);
|
||||||
|
if (result.getType() != null && statementExpression.getParent() instanceof KtBlockExpression) {
|
||||||
|
DataFlowValue lastExpressionValue = DataFlowValueFactory.createDataFlowValue(
|
||||||
|
statementExpression, result.getType(), context);
|
||||||
|
DataFlowValue blockExpressionValue = DataFlowValueFactory.createDataFlowValue(
|
||||||
|
(KtBlockExpression) statementExpression.getParent(), result.getType(), context);
|
||||||
|
result = result.replaceDataFlowInfo(result.getDataFlowInfo().assign(blockExpressionValue, lastExpressionValue));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
result = blockLevelVisitor
|
result = blockLevelVisitor
|
||||||
|
|||||||
+13
-3
@@ -105,8 +105,10 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
|||||||
|
|
||||||
val dataFlowInfoForEntries = analyzeConditionsInWhenEntries(expression, contextAfterSubject, subjectDataFlowValue, subjectType)
|
val dataFlowInfoForEntries = analyzeConditionsInWhenEntries(expression, contextAfterSubject, subjectDataFlowValue, subjectType)
|
||||||
val whenReturnType = inferTypeForWhenExpression(expression, contextWithExpectedType, contextAfterSubject, dataFlowInfoForEntries)
|
val whenReturnType = inferTypeForWhenExpression(expression, contextWithExpectedType, contextAfterSubject, dataFlowInfoForEntries)
|
||||||
|
val whenResultValue = whenReturnType?.let { DataFlowValueFactory.createDataFlowValue(expression, it, contextAfterSubject) }
|
||||||
|
|
||||||
val branchesTypeInfo = joinWhenExpressionBranches(expression, contextAfterSubject, whenReturnType, jumpOutPossibleInSubject)
|
val branchesTypeInfo =
|
||||||
|
joinWhenExpressionBranches(expression, contextAfterSubject, whenReturnType, jumpOutPossibleInSubject, whenResultValue)
|
||||||
|
|
||||||
val isExhaustive = WhenChecker.isWhenExhaustive(expression, trace)
|
val isExhaustive = WhenChecker.isWhenExhaustive(expression, trace)
|
||||||
|
|
||||||
@@ -191,7 +193,8 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
|||||||
expression: KtWhenExpression,
|
expression: KtWhenExpression,
|
||||||
contextAfterSubject: ExpressionTypingContext,
|
contextAfterSubject: ExpressionTypingContext,
|
||||||
resultType: KotlinType?,
|
resultType: KotlinType?,
|
||||||
jumpOutPossibleInSubject: Boolean
|
jumpOutPossibleInSubject: Boolean,
|
||||||
|
whenResultValue: DataFlowValue?
|
||||||
): KotlinTypeInfo {
|
): KotlinTypeInfo {
|
||||||
val bindingContext = contextAfterSubject.trace.bindingContext
|
val bindingContext = contextAfterSubject.trace.bindingContext
|
||||||
|
|
||||||
@@ -208,7 +211,14 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
|||||||
errorTypeExistInBranch = true
|
errorTypeExistInBranch = true
|
||||||
}
|
}
|
||||||
|
|
||||||
val entryDataFlowInfo = entryTypeInfo.dataFlowInfo
|
val entryDataFlowInfo =
|
||||||
|
if (whenResultValue != null && entryType != null) {
|
||||||
|
val entryValue = DataFlowValueFactory.createDataFlowValue(entryExpression, entryType, contextAfterSubject)
|
||||||
|
entryTypeInfo.dataFlowInfo.assign(whenResultValue, entryValue)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
entryTypeInfo.dataFlowInfo
|
||||||
|
}
|
||||||
|
|
||||||
currentDataFlowInfo =
|
currentDataFlowInfo =
|
||||||
if (entryType != null && KotlinBuiltIns.isNothing(entryType))
|
if (entryType != null && KotlinBuiltIns.isNothing(entryType))
|
||||||
|
|||||||
+4
-4
@@ -3,7 +3,7 @@
|
|||||||
fun basic(): String {
|
fun basic(): String {
|
||||||
var current: String? = null
|
var current: String? = null
|
||||||
current = if (current == null) "bar" else current
|
current = if (current == null) "bar" else current
|
||||||
return <!TYPE_MISMATCH!>current<!>
|
return <!DEBUG_INFO_SMARTCAST!>current<!>
|
||||||
}
|
}
|
||||||
|
|
||||||
fun foo(flag: Boolean) {
|
fun foo(flag: Boolean) {
|
||||||
@@ -13,7 +13,7 @@ fun foo(flag: Boolean) {
|
|||||||
x = if (flag) "34" else "12"
|
x = if (flag) "34" else "12"
|
||||||
}
|
}
|
||||||
|
|
||||||
x<!UNSAFE_CALL!>.<!>hashCode()
|
<!DEBUG_INFO_SMARTCAST!>x<!>.hashCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bar(flag: Boolean) {
|
fun bar(flag: Boolean) {
|
||||||
@@ -26,7 +26,7 @@ fun bar(flag: Boolean) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
x<!UNSAFE_CALL!>.<!>hashCode()
|
<!DEBUG_INFO_SMARTCAST!>x<!>.hashCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun baz(flag: Boolean) {
|
fun baz(flag: Boolean) {
|
||||||
@@ -40,7 +40,7 @@ fun baz(flag: Boolean) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
x<!UNSAFE_CALL!>.<!>hashCode()
|
<!DEBUG_INFO_SMARTCAST!>x<!>.hashCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun gav(flag: Boolean, arg: String?) {
|
fun gav(flag: Boolean, arg: String?) {
|
||||||
|
|||||||
Reference in New Issue
Block a user