Data flow information is no longer dropped while analyzing object literal expression. #KT-6293 Fixed. #KT-7110 Fixed.
A set of tests for KT-6293 / KT-7110 provided.
This commit is contained in:
@@ -39,6 +39,7 @@ import org.jetbrains.kotlin.types.*;
|
||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext;
|
||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices;
|
||||
import org.jetbrains.kotlin.types.expressions.ValueParameterResolver;
|
||||
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.TypeInfoFactoryPackage;
|
||||
import org.jetbrains.kotlin.util.Box;
|
||||
import org.jetbrains.kotlin.util.ReenteringLazyValueComputationException;
|
||||
import org.jetbrains.kotlin.util.slicedMap.WritableSlice;
|
||||
@@ -48,8 +49,7 @@ import java.util.*;
|
||||
|
||||
import static org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor.NO_RECEIVER_PARAMETER;
|
||||
import static org.jetbrains.kotlin.diagnostics.Errors.*;
|
||||
import static org.jetbrains.kotlin.resolve.BindingContext.CONSTRUCTOR_RESOLVED_DELEGATION_CALL;
|
||||
import static org.jetbrains.kotlin.resolve.BindingContext.DEFERRED_TYPE;
|
||||
import static org.jetbrains.kotlin.resolve.BindingContext.*;
|
||||
import static org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE;
|
||||
|
||||
public class BodyResolver {
|
||||
@@ -350,6 +350,10 @@ public class BodyResolver {
|
||||
primaryConstructorDelegationCall[0] = null;
|
||||
}
|
||||
}
|
||||
// Recording type info for callee to use later in JetObjectLiteralExpression
|
||||
trace.record(PROCESSED, call.getCalleeExpression(), true);
|
||||
trace.record(EXPRESSION_TYPE_INFO, call.getCalleeExpression(),
|
||||
TypeInfoFactoryPackage.noTypeInfo(results.getResultingCall().getDataFlowInfoForArguments().getResultInfo()));
|
||||
}
|
||||
else {
|
||||
recordSupertype(typeReference, trace.getBindingContext().get(BindingContext.TYPE, typeReference));
|
||||
|
||||
+21
-8
@@ -609,8 +609,8 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
||||
@Override
|
||||
public JetTypeInfo visitObjectLiteralExpression(@NotNull final JetObjectLiteralExpression expression, final ExpressionTypingContext context) {
|
||||
final JetType[] result = new JetType[1];
|
||||
final TemporaryBindingTrace temporaryTrace = TemporaryBindingTrace.create(context.trace,
|
||||
"trace to resolve object literal expression", expression);
|
||||
TemporaryBindingTrace temporaryTrace = TemporaryBindingTrace.create(context.trace,
|
||||
"trace to resolve object literal expression", expression);
|
||||
ObservableBindingTrace.RecordHandler<PsiElement, ClassDescriptor> handler = new ObservableBindingTrace.RecordHandler<PsiElement, ClassDescriptor>() {
|
||||
|
||||
@Override
|
||||
@@ -625,11 +625,6 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
||||
}
|
||||
});
|
||||
result[0] = defaultType;
|
||||
// noinspection ConstantConditions
|
||||
if (!context.trace.get(PROCESSED, expression)) {
|
||||
temporaryTrace.recordType(expression, defaultType);
|
||||
temporaryTrace.record(PROCESSED, expression);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -643,7 +638,25 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
||||
components.additionalCheckerProvider,
|
||||
components.dynamicTypesSettings);
|
||||
temporaryTrace.commit();
|
||||
return TypeInfoFactoryPackage.createCheckedTypeInfo(result[0], context, expression);
|
||||
DataFlowInfo resultFlowInfo = context.dataFlowInfo;
|
||||
for (JetDelegationSpecifier specifier: expression.getObjectDeclaration().getDelegationSpecifiers()) {
|
||||
if (specifier instanceof JetDelegatorToSuperCall) {
|
||||
JetDelegatorToSuperCall delegator = (JetDelegatorToSuperCall)specifier;
|
||||
JetTypeInfo delegatorTypeInfo = context.trace.get(EXPRESSION_TYPE_INFO, delegator.getCalleeExpression());
|
||||
if (delegatorTypeInfo != null) {
|
||||
resultFlowInfo = resultFlowInfo.and(delegatorTypeInfo.getDataFlowInfo());
|
||||
}
|
||||
}
|
||||
}
|
||||
// Breaks are not possible inside constructor arguments, so jumpPossible or jumpFlowInfo are not necessary here
|
||||
JetTypeInfo resultTypeInfo = DataFlowUtils.checkType(TypeInfoFactoryPackage.createTypeInfo(result[0], resultFlowInfo),
|
||||
expression,
|
||||
context);
|
||||
// We have to record it here,
|
||||
// otherwise ExpressionTypingVisitorDispatcher records wrong information
|
||||
context.trace.record(EXPRESSION_TYPE_INFO, expression, resultTypeInfo);
|
||||
context.trace.record(PROCESSED, expression);
|
||||
return resultTypeInfo;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
// See KT-6293: Smart cast doesn't work after object literal
|
||||
abstract class Runnable {
|
||||
abstract fun run()
|
||||
}
|
||||
|
||||
fun foo(): Int {
|
||||
val c: Int? = null
|
||||
if (c is Int) {
|
||||
var k: Runnable
|
||||
val d: Int = <!DEBUG_INFO_SMARTCAST!>c<!>
|
||||
k = object: Runnable() {
|
||||
override fun run() = Unit
|
||||
}
|
||||
// Unnecessary but not important smart cast
|
||||
<!DEBUG_INFO_SMARTCAST!>k<!>.run()
|
||||
return <!DEBUG_INFO_SMARTCAST!>c<!> + d
|
||||
}
|
||||
else return -1
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package
|
||||
|
||||
internal fun foo(): kotlin.Int
|
||||
|
||||
internal abstract class Runnable {
|
||||
public constructor Runnable()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
internal abstract fun run(): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// See KT-6293: Smart cast doesn't work after object literal
|
||||
abstract class Runnable {
|
||||
abstract fun run()
|
||||
}
|
||||
|
||||
fun foo(): Int {
|
||||
val c: Int? = null
|
||||
if (c is Int) {
|
||||
val d: Int = <!DEBUG_INFO_SMARTCAST!>c<!>
|
||||
// This object breaks data flow info propagation
|
||||
val k = object: Runnable() {
|
||||
override fun run() = Unit
|
||||
}
|
||||
k.run()
|
||||
// Smart cast should work but error is reported
|
||||
return <!DEBUG_INFO_SMARTCAST!>c<!> + d
|
||||
}
|
||||
else return -1
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package
|
||||
|
||||
internal fun foo(): kotlin.Int
|
||||
|
||||
internal abstract class Runnable {
|
||||
public constructor Runnable()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
internal abstract fun run(): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
abstract class Runnable {
|
||||
abstract fun run()
|
||||
}
|
||||
|
||||
fun foo(): Int {
|
||||
val c: Int? = null
|
||||
var a: Int?
|
||||
if (c is Int) {
|
||||
a = 2
|
||||
val k = object: Runnable() {
|
||||
init {
|
||||
a = null
|
||||
}
|
||||
override fun run() = Unit
|
||||
}
|
||||
k.run()
|
||||
val d: Int = <!DEBUG_INFO_SMARTCAST!>c<!>
|
||||
// a is captured so smart cast is not possible
|
||||
return d + <!SMARTCAST_IMPOSSIBLE!>a<!>
|
||||
}
|
||||
else return -1
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package
|
||||
|
||||
internal fun foo(): kotlin.Int
|
||||
|
||||
internal abstract class Runnable {
|
||||
public constructor Runnable()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
internal abstract fun run(): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
abstract class Runnable {
|
||||
abstract fun run()
|
||||
}
|
||||
|
||||
fun foo(): Int {
|
||||
val c: Int? = null
|
||||
val a: Int? = 1
|
||||
if (c is Int) {
|
||||
val k = object: Runnable() {
|
||||
init {
|
||||
a!!.toInt()
|
||||
}
|
||||
override fun run() = Unit
|
||||
}
|
||||
k.run()
|
||||
val d: Int = <!DEBUG_INFO_SMARTCAST!>c<!>
|
||||
// a is not null because of k constructor, but we do not know it
|
||||
return a <!UNSAFE_INFIX_CALL!>+<!> d
|
||||
}
|
||||
else return -1
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package
|
||||
|
||||
internal fun foo(): kotlin.Int
|
||||
|
||||
internal abstract class Runnable {
|
||||
public constructor Runnable()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
internal abstract fun run(): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
abstract class Runnable(val arg: Int) {
|
||||
abstract fun run(): Int
|
||||
}
|
||||
|
||||
fun foo(): Int {
|
||||
val c: Int? = null
|
||||
val a: Int? = 1
|
||||
if (c is Int) {
|
||||
val k = object: Runnable(a!!) {
|
||||
override fun run() = arg
|
||||
}
|
||||
k.run()
|
||||
val d: Int = <!DEBUG_INFO_SMARTCAST!>c<!>
|
||||
return <!DEBUG_INFO_SMARTCAST!>a<!> + d
|
||||
}
|
||||
else return -1
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package
|
||||
|
||||
internal fun foo(): kotlin.Int
|
||||
|
||||
internal abstract class Runnable {
|
||||
public constructor Runnable(/*0*/ arg: kotlin.Int)
|
||||
internal final val arg: 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
|
||||
internal abstract fun run(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
abstract class Runnable(val arg: Int) {
|
||||
abstract fun run(): Int
|
||||
}
|
||||
|
||||
interface Wrapper {
|
||||
fun run(): Int
|
||||
}
|
||||
|
||||
fun foo(): Int {
|
||||
val c: Int? = null
|
||||
val a: Int? = 1
|
||||
if (c is Int) {
|
||||
val k = object: Wrapper, Runnable(a!!) {
|
||||
override fun run() = arg
|
||||
}
|
||||
k.run()
|
||||
val d: Int = <!DEBUG_INFO_SMARTCAST!>c<!>
|
||||
return <!DEBUG_INFO_SMARTCAST!>a<!> + d
|
||||
}
|
||||
else return -1
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package
|
||||
|
||||
internal fun foo(): kotlin.Int
|
||||
|
||||
internal abstract class Runnable {
|
||||
public constructor Runnable(/*0*/ arg: kotlin.Int)
|
||||
internal final val arg: 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
|
||||
internal abstract fun run(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
internal interface Wrapper {
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
internal abstract fun run(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// Anonymous object's initialization does not affect smart casts
|
||||
|
||||
abstract class A(val s: String) {
|
||||
fun bar(): String = s
|
||||
}
|
||||
|
||||
fun foo(o: String?): Int {
|
||||
val a = object : A(o!!){}
|
||||
a.bar()
|
||||
return <!DEBUG_INFO_SMARTCAST!>o<!>.length()
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package
|
||||
|
||||
internal fun foo(/*0*/ o: kotlin.String?): kotlin.Int
|
||||
|
||||
internal abstract class A {
|
||||
public constructor A(/*0*/ s: kotlin.String)
|
||||
internal final val s: kotlin.String
|
||||
internal final fun bar(): kotlin.String
|
||||
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,16 @@
|
||||
// See KT-6293: Smart cast doesn't work after object literal
|
||||
abstract class Runnable {
|
||||
abstract fun run()
|
||||
}
|
||||
|
||||
fun foo(): Int {
|
||||
val c: Int? = null
|
||||
if (c is Int) {
|
||||
val d: Int = <!DEBUG_INFO_SMARTCAST!>c<!>
|
||||
object: Runnable() {
|
||||
override fun run() = Unit
|
||||
}.run()
|
||||
return <!DEBUG_INFO_SMARTCAST!>c<!> + d
|
||||
}
|
||||
else return -1
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package
|
||||
|
||||
internal fun foo(): kotlin.Int
|
||||
|
||||
internal abstract class Runnable {
|
||||
public constructor Runnable()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
internal abstract fun run(): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
@@ -11655,6 +11655,63 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/diagnostics/tests/smartCasts/objectLiterals")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class ObjectLiterals extends AbstractJetDiagnosticsTest {
|
||||
public void testAllFilesPresentInObjectLiterals() throws Exception {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/smartCasts/objectLiterals"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("assignment.kt")
|
||||
public void testAssignment() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/objectLiterals/assignment.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("base.kt")
|
||||
public void testBase() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/objectLiterals/base.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("captured.kt")
|
||||
public void testCaptured() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/objectLiterals/captured.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("exclexcl.kt")
|
||||
public void testExclexcl() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/objectLiterals/exclexcl.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("exclexclArgument.kt")
|
||||
public void testExclexclArgument() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/objectLiterals/exclexclArgument.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("exclexclTwoArgument.kt")
|
||||
public void testExclexclTwoArgument() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/objectLiterals/exclexclTwoArgument.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("kt7110.kt")
|
||||
public void testKt7110() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/objectLiterals/kt7110.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("receiver.kt")
|
||||
public void testReceiver() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/objectLiterals/receiver.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/diagnostics/tests/smartCasts/publicVals")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user