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
|
||||
// Smart casts are completely safe
|
||||
STABLE_VALUE("stable val"),
|
||||
// Block, or if / else, or when
|
||||
STABLE_COMPLEX_EXPRESSION("complex expression", ""),
|
||||
// Member value with open / custom getter
|
||||
// Smart casts are not safe
|
||||
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,
|
||||
* 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 {
|
||||
if (this === other) return true
|
||||
|
||||
+26
-2
@@ -57,6 +57,16 @@ object DataFlowValueFactory {
|
||||
resolutionContext: ResolutionContext<*>
|
||||
) = 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
|
||||
fun createDataFlowValue(
|
||||
expression: KtExpression,
|
||||
@@ -85,6 +95,10 @@ object DataFlowValueFactory {
|
||||
Nullability.NOT_NULL)
|
||||
}
|
||||
|
||||
if (isComplexExpression(expression)) {
|
||||
return createDataFlowValueForComplexExpression(expression, type)
|
||||
}
|
||||
|
||||
val result = getIdForStableIdentifier(expression, bindingContext, containingDeclarationOrModule)
|
||||
return DataFlowValue(if (result === IdentifierInfo.NO) ExpressionIdentifierInfo(expression) else result, type)
|
||||
}
|
||||
@@ -124,6 +138,11 @@ object DataFlowValueFactory {
|
||||
bindingContext[BOUND_INITIALIZER_VALUE, variableDescriptor]),
|
||||
variableDescriptor.type)
|
||||
|
||||
private fun createDataFlowValueForComplexExpression(
|
||||
expression: KtExpression,
|
||||
type: KotlinType
|
||||
) = DataFlowValue(ExpressionIdentifierInfo(expression, stableComplex = true), type)
|
||||
|
||||
// For only ++ and -- postfix operations
|
||||
private data class PostfixIdentifierInfo(val argumentInfo: IdentifierInfo, val op: KtToken) : IdentifierInfo {
|
||||
override val kind: DataFlowValue.Kind get() = argumentInfo.kind
|
||||
@@ -131,8 +150,13 @@ object DataFlowValueFactory {
|
||||
override fun toString() = "$argumentInfo($op)"
|
||||
}
|
||||
|
||||
data class ExpressionIdentifierInfo(val expression: KtExpression) : IdentifierInfo {
|
||||
override val kind = OTHER
|
||||
class ExpressionIdentifierInfo(val expression: KtExpression, stableComplex: Boolean = false) : IdentifierInfo {
|
||||
|
||||
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)"
|
||||
}
|
||||
|
||||
+11
-2
@@ -177,7 +177,14 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
||||
if (innerExpression == null) {
|
||||
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
|
||||
@@ -1319,8 +1326,10 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
||||
}
|
||||
}
|
||||
DataFlowValue resultValue = DataFlowValueFactory.createDataFlowValue(expression, type, context);
|
||||
dataFlowInfo = dataFlowInfo.disequate(resultValue, nullValue);
|
||||
dataFlowInfo = dataFlowInfo.assign(resultValue, leftValue).disequate(resultValue, nullValue);
|
||||
if (!jumpInRight) {
|
||||
DataFlowValue rightValue = DataFlowValueFactory.createDataFlowValue(right, rightType, context);
|
||||
rightDataFlowInfo = rightDataFlowInfo.assign(resultValue, rightValue);
|
||||
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.ResolvedCall;
|
||||
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.inline.InlineUtil;
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope;
|
||||
@@ -150,6 +152,13 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
|
||||
KotlinType elseType = elseTypeInfo.getType();
|
||||
DataFlowInfo thenDataFlowInfo = thenTypeInfo.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();
|
||||
|
||||
|
||||
+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.ResolutionContext;
|
||||
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.LexicalScopeKind;
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalWritableScope;
|
||||
@@ -267,6 +269,13 @@ public class ExpressionTypingServices {
|
||||
result = getTypeOfLastExpressionInBlock(
|
||||
statementExpression, newContext.replaceExpectedType(context.expectedType), coercionStrategyForLastExpression,
|
||||
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 {
|
||||
result = blockLevelVisitor
|
||||
|
||||
+13
-3
@@ -105,8 +105,10 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
||||
|
||||
val dataFlowInfoForEntries = analyzeConditionsInWhenEntries(expression, contextAfterSubject, subjectDataFlowValue, subjectType)
|
||||
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)
|
||||
|
||||
@@ -191,7 +193,8 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
||||
expression: KtWhenExpression,
|
||||
contextAfterSubject: ExpressionTypingContext,
|
||||
resultType: KotlinType?,
|
||||
jumpOutPossibleInSubject: Boolean
|
||||
jumpOutPossibleInSubject: Boolean,
|
||||
whenResultValue: DataFlowValue?
|
||||
): KotlinTypeInfo {
|
||||
val bindingContext = contextAfterSubject.trace.bindingContext
|
||||
|
||||
@@ -208,7 +211,14 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
||||
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 =
|
||||
if (entryType != null && KotlinBuiltIns.isNothing(entryType))
|
||||
|
||||
+4
-4
@@ -3,7 +3,7 @@
|
||||
fun basic(): String {
|
||||
var current: String? = null
|
||||
current = if (current == null) "bar" else current
|
||||
return <!TYPE_MISMATCH!>current<!>
|
||||
return <!DEBUG_INFO_SMARTCAST!>current<!>
|
||||
}
|
||||
|
||||
fun foo(flag: Boolean) {
|
||||
@@ -13,7 +13,7 @@ fun foo(flag: Boolean) {
|
||||
x = if (flag) "34" else "12"
|
||||
}
|
||||
|
||||
x<!UNSAFE_CALL!>.<!>hashCode()
|
||||
<!DEBUG_INFO_SMARTCAST!>x<!>.hashCode()
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -40,7 +40,7 @@ fun baz(flag: Boolean) {
|
||||
}
|
||||
}
|
||||
|
||||
x<!UNSAFE_CALL!>.<!>hashCode()
|
||||
<!DEBUG_INFO_SMARTCAST!>x<!>.hashCode()
|
||||
}
|
||||
|
||||
fun gav(flag: Boolean, arg: String?) {
|
||||
|
||||
Reference in New Issue
Block a user