Smart cast impossible on when subject is no more recorded #KT-10061 Fixed

This commit is contained in:
Mikhail Glukhikh
2015-11-18 14:18:04 +03:00
parent 58dc88dd2c
commit 3a4f6d8226
7 changed files with 70 additions and 11 deletions
@@ -207,8 +207,8 @@ public class DataFlowAnalyzer {
return expressionType; return expressionType;
} }
KotlinType possibleType = checkPossibleCast(expressionType, expression, c); SmartCastResult castResult = checkPossibleCast(expressionType, expression, c);
if (possibleType != null) return possibleType; if (castResult != null) return castResult.getResultType();
c.trace.report(TYPE_MISMATCH.on(expression, c.expectedType, expressionType)); c.trace.report(TYPE_MISMATCH.on(expression, c.expectedType, expressionType));
if (hasError != null) hasError.set(true); if (hasError != null) hasError.set(true);
@@ -245,15 +245,14 @@ public class DataFlowAnalyzer {
} }
@Nullable @Nullable
public KotlinType checkPossibleCast( public SmartCastResult checkPossibleCast(
@NotNull KotlinType expressionType, @NotNull KotlinType expressionType,
@NotNull KtExpression expression, @NotNull KtExpression expression,
@NotNull ResolutionContext c @NotNull ResolutionContext c
) { ) {
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, expressionType, c); DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, expressionType, c);
SmartCastResult result = SmartCastManager.checkAndRecordPossibleCast(dataFlowValue, c.expectedType, expression, c, null, false); return SmartCastManager.checkAndRecordPossibleCast(dataFlowValue, c.expectedType, expression, c, null, false);
return result != null ? result.getResultType() : null;
} }
public void recordExpectedType(@NotNull BindingTrace trace, @NotNull KtExpression expression, @NotNull KotlinType expectedType) { public void recordExpectedType(@NotNull BindingTrace trace, @NotNull KtExpression expression, @NotNull KotlinType expectedType) {
@@ -25,13 +25,11 @@ import org.jetbrains.kotlin.cfg.WhenChecker;
import org.jetbrains.kotlin.descriptors.ClassDescriptor; import org.jetbrains.kotlin.descriptors.ClassDescriptor;
import org.jetbrains.kotlin.diagnostics.Errors; import org.jetbrains.kotlin.diagnostics.Errors;
import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.*;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.PossiblyBareType;
import org.jetbrains.kotlin.resolve.TypeResolutionContext;
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.DataFlowValue;
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory; import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory;
import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastResult;
import org.jetbrains.kotlin.resolve.calls.util.CallMaker; import org.jetbrains.kotlin.resolve.calls.util.CallMaker;
import org.jetbrains.kotlin.resolve.scopes.LexicalWritableScope; import org.jetbrains.kotlin.resolve.scopes.LexicalWritableScope;
import org.jetbrains.kotlin.types.*; import org.jetbrains.kotlin.types.*;
@@ -92,8 +90,15 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
subjectType = typeInfo.getType(); subjectType = typeInfo.getType();
assert subjectType != null; assert subjectType != null;
if (TypeUtils.isNullableType(subjectType) && !WhenChecker.containsNullCase(expression, context.trace)) { if (TypeUtils.isNullableType(subjectType) && !WhenChecker.containsNullCase(expression, context.trace)) {
ExpressionTypingContext subjectContext = context.replaceExpectedType(TypeUtils.makeNotNullable(subjectType)); TemporaryBindingTrace trace = TemporaryBindingTrace.create(context.trace, "Temporary trace for when subject nullability");
components.dataFlowAnalyzer.checkPossibleCast(subjectType, KtPsiUtil.safeDeparenthesize(subjectExpression), subjectContext); ExpressionTypingContext subjectContext =
context.replaceExpectedType(TypeUtils.makeNotNullable(subjectType)).replaceBindingTrace(trace);
SmartCastResult castResult = components.dataFlowAnalyzer.checkPossibleCast(
subjectType, KtPsiUtil.safeDeparenthesize(subjectExpression), subjectContext
);
if (castResult != null && castResult.isCorrect()) {
trace.commit();
}
} }
context = context.replaceDataFlowInfo(typeInfo.getDataFlowInfo()); context = context.replaceDataFlowInfo(typeInfo.getDataFlowInfo());
} }
@@ -0,0 +1,10 @@
// See KT-10061
class My {
val x: Int? get() = 42
}
fun foo(my: My) {
my.x!!
when (my.x) { }
}
@@ -0,0 +1,11 @@
package
public fun foo(/*0*/ my: My): kotlin.Unit
public final class My {
public constructor My()
public final val x: kotlin.Int?
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,11 @@
// See KT-10061
// FILE: My.java
public class My {
String getSomething() { return "xyz"; }
}
// FILE: My.kt
fun foo(my: My) {
my.something!!
when (my.something) { }
}
@@ -0,0 +1,11 @@
package
public fun foo(/*0*/ my: My): kotlin.Unit
public open class My {
public constructor My()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public/*package*/ open fun getSomething(): kotlin.String!
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -14922,6 +14922,18 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("whenSubjectImpossible.kt")
public void testWhenSubjectImpossible() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/whenSubjectImpossible.kt");
doTest(fileName);
}
@TestMetadata("whenSubjectImpossibleJava.kt")
public void testWhenSubjectImpossibleJava() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/whenSubjectImpossibleJava.kt");
doTest(fileName);
}
@TestMetadata("compiler/testData/diagnostics/tests/smartCasts/inference") @TestMetadata("compiler/testData/diagnostics/tests/smartCasts/inference")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class) @RunWith(JUnit3RunnerWithInners.class)