diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DestructuringDeclarationResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DestructuringDeclarationResolver.kt index 10fdf0ffc01..e1eee33ec36 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DestructuringDeclarationResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DestructuringDeclarationResolver.kt @@ -49,7 +49,8 @@ class DestructuringDeclarationResolver( val componentName = createComponentName(componentIndex + 1) 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 if (results.isSuccess) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/FakeCallResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/FakeCallResolver.kt index 8b077265e4b..e44b7ac7637 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/FakeCallResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/FakeCallResolver.kt @@ -25,7 +25,6 @@ import org.jetbrains.kotlin.psi.Call import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtPsiFactory import org.jetbrains.kotlin.psi.KtSimpleNameExpression -import org.jetbrains.kotlin.resolve.BindingContextUtils import org.jetbrains.kotlin.resolve.TemporaryBindingTrace import org.jetbrains.kotlin.resolve.calls.CallResolver 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 java.util.* +enum class FakeCallKind { + ITERATOR, + COMPONENT, + OTHER +} + class FakeCallResolver( private val project: Project, private val callResolver: CallResolver @@ -44,33 +49,31 @@ class FakeCallResolver( receiver: ReceiverValue?, name: Name, callElement: KtExpression?, + reportErrorsOn: KtExpression? = callElement, + callKind: FakeCallKind = FakeCallKind.OTHER, vararg argumentTypes: KotlinType ): OverloadResolutionResults { val traceWithFakeArgumentInfo = TemporaryBindingTrace.create(context.trace, "trace to store fake argument for", name) val fakeArguments = ArrayList() 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( context: ExpressionTypingContext, receiver: ReceiverValue, name: Name, - callElement: KtExpression + callElement: KtExpression, + reportErrorsOn: KtExpression = callElement, + callKind: FakeCallKind = FakeCallKind.OTHER, + valueArguments: List = emptyList() ): OverloadResolutionResults { - return resolveFakeCall(receiver, context, emptyList(), name, callElement) - } - - fun resolveFakeCall( - receiver: ReceiverValue, - context: ExpressionTypingContext, - valueArguments: List, - name: Name, - callElement: KtExpression - ): OverloadResolutionResults { - return makeAndResolveFakeCall(receiver, context, valueArguments, name, callElement).second + return makeAndResolveFakeCall(receiver, context, valueArguments, name, callElement, callKind, reportErrorsOn).second } fun makeAndResolveFakeCall( @@ -79,25 +82,33 @@ class FakeCallResolver( valueArguments: List, name: Name, callElement: KtExpression?, - iteratorCheck: Boolean = false + callKind: FakeCallKind = FakeCallKind.OTHER, + reportErrorsOn: KtExpression? = callElement ): Pair> { val fakeTrace = TemporaryBindingTrace.create(context.trace, "trace to resolve fake call for", name) val fakeBindingTrace = context.replaceBindingTrace(fakeTrace) - var hasUnreportedIteratorError = false + var hasUnreportedError = false val result = makeAndResolveFakeCallInContext(receiver, fakeBindingTrace, valueArguments, name, callElement) { fake -> fakeTrace.commit({ slice, diagnostic, key -> // excluding all entries related to fake expression // convert all errors on this expression to ITERATOR_MISSING on callElement val isFakeKey = key == fake - if (iteratorCheck && diagnostic?.severity == Severity.ERROR && isFakeKey) { - hasUnreportedIteratorError = true + if (diagnostic?.severity == Severity.ERROR && isFakeKey) { + hasUnreportedError = true } !isFakeKey }, true) } - if (hasUnreportedIteratorError && callElement != null) { - context.trace.report(Errors.ITERATOR_MISSING.on(callElement)) + if (hasUnreportedError && reportErrorsOn != null) { + 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 } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ForLoopConventionsChecker.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ForLoopConventionsChecker.java index b72159babf5..6cb1bac45eb 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ForLoopConventionsChecker.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ForLoopConventionsChecker.java @@ -67,7 +67,7 @@ public class ForLoopConventionsChecker { Name iterator = Name.identifier("iterator"); Pair> calls = fakeCallResolver.makeAndResolveFakeCall(loopRange, context, Collections.emptyList(), iterator, - loopRange.getExpression(), true); + loopRangeExpression, FakeCallKind.ITERATOR, loopRangeExpression); OverloadResolutionResults iteratorResolutionResults = calls.getSecond(); if (iteratorResolutionResults.isSuccess()) { diff --git a/compiler/testData/diagnostics/tests/dataClasses/extensionComponentsOnNullable.kt b/compiler/testData/diagnostics/tests/dataClasses/extensionComponentsOnNullable.kt new file mode 100644 index 00000000000..038ca04ec79 --- /dev/null +++ b/compiler/testData/diagnostics/tests/dataClasses/extensionComponentsOnNullable.kt @@ -0,0 +1,21 @@ +class Data(val x: T, val y: T) + +operator fun Data.component1() = x + +operator fun Data.component2() = y + +fun foo(): Int { + val d: Data? = null + // An error must be here + val (x, y) = d + return x + y +} + +data class NormalData(val x: T, val y: T) + +fun bar(): Int { + val d: NormalData? = null + // An error must be here + val (x, y) = d + return x + y +} diff --git a/compiler/testData/diagnostics/tests/dataClasses/extensionComponentsOnNullable.txt b/compiler/testData/diagnostics/tests/dataClasses/extensionComponentsOnNullable.txt new file mode 100644 index 00000000000..85448291dc5 --- /dev/null +++ b/compiler/testData/diagnostics/tests/dataClasses/extensionComponentsOnNullable.txt @@ -0,0 +1,27 @@ +package + +public fun bar(): kotlin.Int +public fun foo(): kotlin.Int +public operator fun Data.component1(): T +public operator fun Data.component2(): T + +public final class Data { + public constructor Data(/*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 { + public constructor NormalData(/*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 + 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 +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index c441a6095cb..97c01a342dd 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -3600,6 +3600,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { 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") public void testImplementTraitWhichHasComponent1() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/dataClasses/implementTraitWhichHasComponent1.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/resolve/ExpectedResolveDataUtil.java b/compiler/tests/org/jetbrains/kotlin/resolve/ExpectedResolveDataUtil.java index 1b60ef12bf8..8a6c9c361f3 100644 --- a/compiler/tests/org/jetbrains/kotlin/resolve/ExpectedResolveDataUtil.java +++ b/compiler/tests/org/jetbrains/kotlin/resolve/ExpectedResolveDataUtil.java @@ -41,6 +41,7 @@ import org.jetbrains.kotlin.tests.di.InjectionKt; import org.jetbrains.kotlin.types.KotlinType; import org.jetbrains.kotlin.types.TypeUtils; import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext; +import org.jetbrains.kotlin.types.expressions.FakeCallKind; import java.util.Arrays; import java.util.HashMap; @@ -146,7 +147,7 @@ public class ExpectedResolveDataUtil { DataFlowInfoFactory.EMPTY, TypeUtils.NO_EXPECTED_TYPE); OverloadResolutionResults functions = container.getFakeCallResolver().resolveFakeCall( - context, null, Name.identifier(name), null, parameterTypes); + context, null, Name.identifier(name), null, null, FakeCallKind.OTHER, parameterTypes); for (ResolvedCall resolvedCall : functions.getResultingCalls()) { List unsubstitutedValueParameters = resolvedCall.getResultingDescriptor().getValueParameters();