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:
Mikhail Glukhikh
2015-05-19 12:00:20 +03:00
parent 5bca1d3c8f
commit c8aa6defb6
19 changed files with 328 additions and 10 deletions
@@ -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));
@@ -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)