Smart cast impossible on when subject is no more recorded #KT-10061 Fixed
This commit is contained in:
@@ -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) {
|
||||||
|
|||||||
+11
-6
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user