From a3b11f4214ad756dd15a07dadd9d6fb1c514c598 Mon Sep 17 00:00:00 2001 From: Stanislav Erokhin Date: Tue, 10 Nov 2015 11:01:18 +0300 Subject: [PATCH] Created warning for KT-9805 #KT-9805 In Progress --- .../jetbrains/kotlin/diagnostics/Errors.java | 2 + .../rendering/DefaultErrorMessages.java | 1 + .../kotlin/resolve/TargetPlatform.kt | 3 +- .../calls/checkers/InvokeConventionChecker.kt | 42 +++++++++ ...vokeOnVariableWithExtensionFunctionType.kt | 88 +++++++++++++++++++ ...okeOnVariableWithExtensionFunctionType.txt | 41 +++++++++ .../tests/resolve/invoke/kt9805.kt | 19 ++++ .../tests/resolve/invoke/kt9805.txt | 19 ++++ .../checkers/DiagnosticsTestGenerated.java | 12 +++ 9 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/InvokeConventionChecker.kt create mode 100644 compiler/testData/diagnostics/tests/resolve/invoke/invokeOnVariableWithExtensionFunctionType.kt create mode 100644 compiler/testData/diagnostics/tests/resolve/invoke/invokeOnVariableWithExtensionFunctionType.txt create mode 100644 compiler/testData/diagnostics/tests/resolve/invoke/kt9805.kt create mode 100644 compiler/testData/diagnostics/tests/resolve/invoke/kt9805.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index efa185d4f51..bea2e019e66 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -479,6 +479,8 @@ public interface Errors { DiagnosticFactory1>> CANNOT_COMPLETE_RESOLVE = DiagnosticFactory1.create(ERROR); DiagnosticFactory1>> UNRESOLVED_REFERENCE_WRONG_RECEIVER = DiagnosticFactory1.create(ERROR); DiagnosticFactory1 INVOKE_EXTENSION_ON_NOT_EXTENSION_FUNCTION = DiagnosticFactory1.create(ERROR); + DiagnosticFactory1 + INVOKE_ON_EXTENSION_FUNCTION_WITH_EXPLICIT_DISPATCH_RECEIVER = DiagnosticFactory1.create(WARNING); DiagnosticFactory1 TYPE_PARAMETER_AS_REIFIED = DiagnosticFactory1.create(ERROR); DiagnosticFactory1 REIFIED_TYPE_FORBIDDEN_SUBSTITUTION = DiagnosticFactory1.create(ERROR); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 2a56df2b818..df9381af0d5 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -594,6 +594,7 @@ public class DefaultErrorMessages { MAP.put(CANNOT_COMPLETE_RESOLVE, "Cannot choose among the following candidates without completing type inference: {0}", AMBIGUOUS_CALLS); MAP.put(UNRESOLVED_REFERENCE_WRONG_RECEIVER, "Unresolved reference. None of the following candidates is applicable because of receiver type mismatch: {0}", AMBIGUOUS_CALLS); MAP.put(INVOKE_EXTENSION_ON_NOT_EXTENSION_FUNCTION, "Impossible call as extension because {0} is not an extension function.", ELEMENT_TEXT); + MAP.put(INVOKE_ON_EXTENSION_FUNCTION_WITH_EXPLICIT_DISPATCH_RECEIVER, "Deprecated call {0}", ELEMENT_TEXT); MAP.put(NO_VALUE_FOR_PARAMETER, "No value passed for parameter {0}", NAME); MAP.put(MISSING_RECEIVER, "A receiver of type {0} is required", RENDER_TYPE); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt index 44cbdfd771e..2cf2e1482d7 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt @@ -56,7 +56,8 @@ private val DEFAULT_DECLARATION_CHECKERS = listOf( OperatorModifierChecker(), InfixModifierChecker()) -private val DEFAULT_CALL_CHECKERS = listOf(CapturingInClosureChecker(), InlineCheckerWrapper(), ReifiedTypeParameterSubstitutionChecker(), SafeCallChecker()) +private val DEFAULT_CALL_CHECKERS = listOf(CapturingInClosureChecker(), InlineCheckerWrapper(), ReifiedTypeParameterSubstitutionChecker(), + SafeCallChecker(), InvokeConventionChecker()) private val DEFAULT_TYPE_CHECKERS = emptyList() private val DEFAULT_VALIDATORS = listOf(DeprecatedSymbolValidator(), OperatorValidator(), InfixValidator()) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/InvokeConventionChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/InvokeConventionChecker.kt new file mode 100644 index 00000000000..350cd07267a --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/InvokeConventionChecker.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.checkers + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCallImpl +import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver + +class InvokeConventionChecker : CallChecker { + override fun check(resolvedCall: ResolvedCall, context: BasicCallResolutionContext) { + if (resolvedCall !is VariableAsFunctionResolvedCallImpl || !resolvedCall.functionCall.dispatchReceiver.exists() + || !resolvedCall.functionCall.extensionReceiver.exists()) { + return + } + + if (!KotlinBuiltIns.isExactExtensionFunctionType(resolvedCall.variableCall.resultingDescriptor.type)) return + + if (resolvedCall.variableCall.dispatchReceiver is ExpressionReceiver || resolvedCall.variableCall.extensionReceiver is ExpressionReceiver) { + context.trace.report(Errors.INVOKE_ON_EXTENSION_FUNCTION_WITH_EXPLICIT_DISPATCH_RECEIVER.on( + resolvedCall.variableCall.call.callElement, resolvedCall.variableCall.call.callElement)) + } + } + +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/resolve/invoke/invokeOnVariableWithExtensionFunctionType.kt b/compiler/testData/diagnostics/tests/resolve/invoke/invokeOnVariableWithExtensionFunctionType.kt new file mode 100644 index 00000000000..3d7acd8d43a --- /dev/null +++ b/compiler/testData/diagnostics/tests/resolve/invoke/invokeOnVariableWithExtensionFunctionType.kt @@ -0,0 +1,88 @@ +// FILE: 1.kt +package fooIsExtension + +class A +class B + +val A.foo: B.() -> Unit get() = {} + +fun test(a: A, b: B) { + b.(a.foo)() + (a.foo)(b) + a.foo(b) + + with(a) { + b.foo() // todo + + b.(foo)() + + (b.foo)() + + foo(b) + (foo)(b) + } + + with(b) { + a.foo() + a.(foo)() + + (a.foo)() + + (a.foo)(this) + a.foo(this) + } + + with(a) { + with(b) { + foo() + (foo)() + } + } +} + +public inline fun with(receiver: T, f: T.() -> R): R = receiver.f() + + +// FILE: 1.kt +package fooIsMember + +class A { + val foo: B.() -> Unit get() = {} +} +class B + +fun test(a: A, b: B) { + b.(a.foo)() + (a.foo)(b) + a.foo(b) + + with(a) { + b.foo() + + b.(foo)() + + (b.foo)() + + foo(b) + (foo)(b) + } + + with(b) { + a.foo() + a.(foo)() + + (a.foo)() + + (a.foo)(this) + a.foo(this) + } + + with(a) { + with(b) { + foo() + (foo)() + } + } +} + +public inline fun with(receiver: T, f: T.() -> R): R = receiver.f() \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/resolve/invoke/invokeOnVariableWithExtensionFunctionType.txt b/compiler/testData/diagnostics/tests/resolve/invoke/invokeOnVariableWithExtensionFunctionType.txt new file mode 100644 index 00000000000..043a96ad5f3 --- /dev/null +++ b/compiler/testData/diagnostics/tests/resolve/invoke/invokeOnVariableWithExtensionFunctionType.txt @@ -0,0 +1,41 @@ +package + +package fooIsExtension { + public val fooIsExtension.A.foo: fooIsExtension.B.() -> kotlin.Unit + public fun test(/*0*/ a: fooIsExtension.A, /*1*/ b: fooIsExtension.B): kotlin.Unit + public inline fun with(/*0*/ receiver: T, /*1*/ f: T.() -> R): R + + public final class A { + public constructor A() + 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 class B { + public constructor B() + 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 + } +} + +package fooIsMember { + public fun test(/*0*/ a: fooIsMember.A, /*1*/ b: fooIsMember.B): kotlin.Unit + public inline fun with(/*0*/ receiver: T, /*1*/ f: T.() -> R): R + + public final class A { + public constructor A() + public final val foo: fooIsMember.B.() -> kotlin.Unit + 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 class B { + public constructor B() + 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/testData/diagnostics/tests/resolve/invoke/kt9805.kt b/compiler/testData/diagnostics/tests/resolve/invoke/kt9805.kt new file mode 100644 index 00000000000..4f5fef9aef2 --- /dev/null +++ b/compiler/testData/diagnostics/tests/resolve/invoke/kt9805.kt @@ -0,0 +1,19 @@ +class A { + val foo: B.() -> Unit get() = null!! +} + +class B + +fun test(a: A, b: B) { + with(b) { + a.foo() // here must be error, because a is not extension receiver + + a.foo(this) + + (a.foo)() + + (a.foo)(this) + } +} + +public inline fun with(receiver: T, f: T.() -> R): R = receiver.f() \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/resolve/invoke/kt9805.txt b/compiler/testData/diagnostics/tests/resolve/invoke/kt9805.txt new file mode 100644 index 00000000000..230b1ba4234 --- /dev/null +++ b/compiler/testData/diagnostics/tests/resolve/invoke/kt9805.txt @@ -0,0 +1,19 @@ +package + +public fun test(/*0*/ a: A, /*1*/ b: B): kotlin.Unit +public inline fun with(/*0*/ receiver: T, /*1*/ f: T.() -> R): R + +public final class A { + public constructor A() + public final val foo: B.() -> kotlin.Unit + 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 class B { + public constructor B() + 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 59b6ecb7242..8d5df5c3f73 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -13079,6 +13079,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("invokeOnVariableWithExtensionFunctionType.kt") + public void testInvokeOnVariableWithExtensionFunctionType() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/resolve/invoke/invokeOnVariableWithExtensionFunctionType.kt"); + doTest(fileName); + } + @TestMetadata("KT-4372.kt") public void testKT_4372() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/resolve/invoke/KT-4372.kt"); @@ -13109,6 +13115,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("kt9805.kt") + public void testKt9805() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/resolve/invoke/kt9805.kt"); + doTest(fileName); + } + @TestMetadata("valNamedInvoke.kt") public void testValNamedInvoke() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/resolve/invoke/valNamedInvoke.kt");