Destructuring declaration initializer now cannot use extension componentX() on nullable #KT-7794 Fixed

This commit is contained in:
Mikhail Glukhikh
2016-01-25 18:58:14 +03:00
parent 850dc89b38
commit 22c0ddaa48
7 changed files with 91 additions and 24 deletions
@@ -49,7 +49,8 @@ class DestructuringDeclarationResolver(
val componentName = createComponentName(componentIndex + 1) val componentName = createComponentName(componentIndex + 1)
val expectedType = getExpectedTypeForComponent(context, entry) val expectedType = getExpectedTypeForComponent(context, entry)
val results = fakeCallResolver.resolveFakeCall(context.replaceExpectedType(expectedType), receiver, componentName, entry) val results = fakeCallResolver.resolveFakeCall(context.replaceExpectedType(expectedType), receiver, componentName,
entry, reportErrorsOn, FakeCallKind.COMPONENT)
var componentType: KotlinType? = null var componentType: KotlinType? = null
if (results.isSuccess) { if (results.isSuccess) {
@@ -25,7 +25,6 @@ import org.jetbrains.kotlin.psi.Call
import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtPsiFactory import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.KtSimpleNameExpression import org.jetbrains.kotlin.psi.KtSimpleNameExpression
import org.jetbrains.kotlin.resolve.BindingContextUtils
import org.jetbrains.kotlin.resolve.TemporaryBindingTrace import org.jetbrains.kotlin.resolve.TemporaryBindingTrace
import org.jetbrains.kotlin.resolve.calls.CallResolver import org.jetbrains.kotlin.resolve.calls.CallResolver
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults
@@ -35,6 +34,12 @@ import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.doNothing import org.jetbrains.kotlin.utils.doNothing
import java.util.* import java.util.*
enum class FakeCallKind {
ITERATOR,
COMPONENT,
OTHER
}
class FakeCallResolver( class FakeCallResolver(
private val project: Project, private val project: Project,
private val callResolver: CallResolver private val callResolver: CallResolver
@@ -44,33 +49,31 @@ class FakeCallResolver(
receiver: ReceiverValue?, receiver: ReceiverValue?,
name: Name, name: Name,
callElement: KtExpression?, callElement: KtExpression?,
reportErrorsOn: KtExpression? = callElement,
callKind: FakeCallKind = FakeCallKind.OTHER,
vararg argumentTypes: KotlinType vararg argumentTypes: KotlinType
): OverloadResolutionResults<FunctionDescriptor> { ): OverloadResolutionResults<FunctionDescriptor> {
val traceWithFakeArgumentInfo = TemporaryBindingTrace.create(context.trace, "trace to store fake argument for", name) val traceWithFakeArgumentInfo = TemporaryBindingTrace.create(context.trace, "trace to store fake argument for", name)
val fakeArguments = ArrayList<KtExpression>() val fakeArguments = ArrayList<KtExpression>()
for (type in argumentTypes) { for (type in argumentTypes) {
fakeArguments.add(ExpressionTypingUtils.createFakeExpressionOfType(project, traceWithFakeArgumentInfo, "fakeArgument" + fakeArguments.size, type)) fakeArguments.add(ExpressionTypingUtils.createFakeExpressionOfType(project, traceWithFakeArgumentInfo,
"fakeArgument" + fakeArguments.size, type))
} }
return makeAndResolveFakeCall(receiver, context.replaceBindingTrace(traceWithFakeArgumentInfo), fakeArguments, name, callElement).second return makeAndResolveFakeCall(receiver, context.replaceBindingTrace(traceWithFakeArgumentInfo),
fakeArguments, name, callElement, callKind, reportErrorsOn).second
} }
@JvmOverloads
fun resolveFakeCall( fun resolveFakeCall(
context: ExpressionTypingContext, context: ExpressionTypingContext,
receiver: ReceiverValue, receiver: ReceiverValue,
name: Name, name: Name,
callElement: KtExpression callElement: KtExpression,
reportErrorsOn: KtExpression = callElement,
callKind: FakeCallKind = FakeCallKind.OTHER,
valueArguments: List<KtExpression> = emptyList()
): OverloadResolutionResults<FunctionDescriptor> { ): OverloadResolutionResults<FunctionDescriptor> {
return resolveFakeCall(receiver, context, emptyList(), name, callElement) return makeAndResolveFakeCall(receiver, context, valueArguments, name, callElement, callKind, reportErrorsOn).second
}
fun resolveFakeCall(
receiver: ReceiverValue,
context: ExpressionTypingContext,
valueArguments: List<KtExpression>,
name: Name,
callElement: KtExpression
): OverloadResolutionResults<FunctionDescriptor> {
return makeAndResolveFakeCall(receiver, context, valueArguments, name, callElement).second
} }
fun makeAndResolveFakeCall( fun makeAndResolveFakeCall(
@@ -79,25 +82,33 @@ class FakeCallResolver(
valueArguments: List<KtExpression>, valueArguments: List<KtExpression>,
name: Name, name: Name,
callElement: KtExpression?, callElement: KtExpression?,
iteratorCheck: Boolean = false callKind: FakeCallKind = FakeCallKind.OTHER,
reportErrorsOn: KtExpression? = callElement
): Pair<Call, OverloadResolutionResults<FunctionDescriptor>> { ): Pair<Call, OverloadResolutionResults<FunctionDescriptor>> {
val fakeTrace = TemporaryBindingTrace.create(context.trace, "trace to resolve fake call for", name) val fakeTrace = TemporaryBindingTrace.create(context.trace, "trace to resolve fake call for", name)
val fakeBindingTrace = context.replaceBindingTrace(fakeTrace) val fakeBindingTrace = context.replaceBindingTrace(fakeTrace)
var hasUnreportedIteratorError = false var hasUnreportedError = false
val result = makeAndResolveFakeCallInContext(receiver, fakeBindingTrace, valueArguments, name, callElement) { fake -> val result = makeAndResolveFakeCallInContext(receiver, fakeBindingTrace, valueArguments, name, callElement) { fake ->
fakeTrace.commit({ slice, diagnostic, key -> fakeTrace.commit({ slice, diagnostic, key ->
// excluding all entries related to fake expression // excluding all entries related to fake expression
// convert all errors on this expression to ITERATOR_MISSING on callElement // convert all errors on this expression to ITERATOR_MISSING on callElement
val isFakeKey = key == fake val isFakeKey = key == fake
if (iteratorCheck && diagnostic?.severity == Severity.ERROR && isFakeKey) { if (diagnostic?.severity == Severity.ERROR && isFakeKey) {
hasUnreportedIteratorError = true hasUnreportedError = true
} }
!isFakeKey !isFakeKey
}, true) }, true)
} }
if (hasUnreportedIteratorError && callElement != null) { if (hasUnreportedError && reportErrorsOn != null) {
context.trace.report(Errors.ITERATOR_MISSING.on(callElement)) when (callKind) {
FakeCallKind.ITERATOR -> context.trace.report(Errors.ITERATOR_MISSING.on(reportErrorsOn))
FakeCallKind.COMPONENT ->
if (receiver != null) {
context.trace.report(Errors.COMPONENT_FUNCTION_MISSING.on(reportErrorsOn, name, receiver.type))
}
FakeCallKind.OTHER -> {}
}
} }
return result return result
} }
@@ -67,7 +67,7 @@ public class ForLoopConventionsChecker {
Name iterator = Name.identifier("iterator"); Name iterator = Name.identifier("iterator");
Pair<Call, OverloadResolutionResults<FunctionDescriptor>> calls = Pair<Call, OverloadResolutionResults<FunctionDescriptor>> calls =
fakeCallResolver.makeAndResolveFakeCall(loopRange, context, Collections.<KtExpression>emptyList(), iterator, fakeCallResolver.makeAndResolveFakeCall(loopRange, context, Collections.<KtExpression>emptyList(), iterator,
loopRange.getExpression(), true); loopRangeExpression, FakeCallKind.ITERATOR, loopRangeExpression);
OverloadResolutionResults<FunctionDescriptor> iteratorResolutionResults = calls.getSecond(); OverloadResolutionResults<FunctionDescriptor> iteratorResolutionResults = calls.getSecond();
if (iteratorResolutionResults.isSuccess()) { if (iteratorResolutionResults.isSuccess()) {
@@ -0,0 +1,21 @@
class Data<T>(val x: T, val y: T)
operator fun <T> Data<T>.component1() = x
operator fun <T> Data<T>.component2() = y
fun foo(): Int {
val d: Data<Int>? = null
// An error must be here
val (x, y) = <!COMPONENT_FUNCTION_MISSING, COMPONENT_FUNCTION_MISSING!>d<!>
return x + y
}
data class NormalData<T>(val x: T, val y: T)
fun bar(): Int {
val d: NormalData<Int>? = null
// An error must be here
val (x, y) = <!COMPONENT_FUNCTION_MISSING, COMPONENT_FUNCTION_MISSING!>d<!>
return <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>x<!> <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>+<!> <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>y<!>
}
@@ -0,0 +1,27 @@
package
public fun bar(): kotlin.Int
public fun foo(): kotlin.Int
public operator fun </*0*/ T> Data<T>.component1(): T
public operator fun </*0*/ T> Data<T>.component2(): T
public final class Data</*0*/ T> {
public constructor Data</*0*/ T>(/*0*/ x: T, /*1*/ y: T)
public final val x: T
public final val y: T
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
}
public final data class NormalData</*0*/ T> {
public constructor NormalData</*0*/ T>(/*0*/ x: T, /*1*/ y: T)
public final val x: T
public final val y: T
public final operator /*synthesized*/ fun component1(): T
public final operator /*synthesized*/ fun component2(): T
public final /*synthesized*/ fun copy(/*0*/ x: T = ..., /*1*/ y: T = ...): NormalData<T>
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
}
@@ -3600,6 +3600,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("extensionComponentsOnNullable.kt")
public void testExtensionComponentsOnNullable() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/dataClasses/extensionComponentsOnNullable.kt");
doTest(fileName);
}
@TestMetadata("implementTraitWhichHasComponent1.kt") @TestMetadata("implementTraitWhichHasComponent1.kt")
public void testImplementTraitWhichHasComponent1() throws Exception { public void testImplementTraitWhichHasComponent1() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/dataClasses/implementTraitWhichHasComponent1.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/dataClasses/implementTraitWhichHasComponent1.kt");
@@ -41,6 +41,7 @@ import org.jetbrains.kotlin.tests.di.InjectionKt;
import org.jetbrains.kotlin.types.KotlinType; import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.types.TypeUtils; import org.jetbrains.kotlin.types.TypeUtils;
import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext; import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext;
import org.jetbrains.kotlin.types.expressions.FakeCallKind;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
@@ -146,7 +147,7 @@ public class ExpectedResolveDataUtil {
DataFlowInfoFactory.EMPTY, TypeUtils.NO_EXPECTED_TYPE); DataFlowInfoFactory.EMPTY, TypeUtils.NO_EXPECTED_TYPE);
OverloadResolutionResults<FunctionDescriptor> functions = container.getFakeCallResolver().resolveFakeCall( OverloadResolutionResults<FunctionDescriptor> functions = container.getFakeCallResolver().resolveFakeCall(
context, null, Name.identifier(name), null, parameterTypes); context, null, Name.identifier(name), null, null, FakeCallKind.OTHER, parameterTypes);
for (ResolvedCall<? extends FunctionDescriptor> resolvedCall : functions.getResultingCalls()) { for (ResolvedCall<? extends FunctionDescriptor> resolvedCall : functions.getResultingCalls()) {
List<ValueParameterDescriptor> unsubstitutedValueParameters = resolvedCall.getResultingDescriptor().getValueParameters(); List<ValueParameterDescriptor> unsubstitutedValueParameters = resolvedCall.getResultingDescriptor().getValueParameters();