From 0ce6bd99992ee57c7a4bf4d5e069bb5ed31e0a86 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Fri, 27 May 2016 19:34:23 +0300 Subject: [PATCH] Ignore resolution to function without arguments on LHS of '::' In the expression "Runnable::run" we were resolving the left-hand side to the SAM constructor of Runnable. Now we detect this situation, ignore the result of such resolution, and continue resolving the LHS as a type --- .../DoubleColonExpressionResolver.kt | 22 ++++++++++++- .../bound/functionCallWithoutArguments.kt | 1 + .../bound/functionCallWithoutArguments.txt | 3 ++ .../classMemberVsConstructorLikeFunction.kt | 16 ++++++++++ .../classMemberVsConstructorLikeFunction.txt | 16 ++++++++++ .../tests/callableReference/sam.kt | 31 +++++++++++++++++++ .../tests/callableReference/sam.txt | 18 +++++++++++ .../checkers/DiagnosticsTestGenerated.java | 18 +++++++++++ 8 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 compiler/testData/diagnostics/tests/callableReference/bound/functionCallWithoutArguments.kt create mode 100644 compiler/testData/diagnostics/tests/callableReference/bound/functionCallWithoutArguments.txt create mode 100644 compiler/testData/diagnostics/tests/callableReference/function/classMemberVsConstructorLikeFunction.kt create mode 100644 compiler/testData/diagnostics/tests/callableReference/function/classMemberVsConstructorLikeFunction.txt create mode 100644 compiler/testData/diagnostics/tests/callableReference/sam.kt create mode 100644 compiler/testData/diagnostics/tests/callableReference/sam.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt index d4675bf8c0e..b4930d5bdd9 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt @@ -23,6 +23,7 @@ import org.jetbrains.kotlin.config.LanguageFeatureSettings import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ConstructorDescriptor import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor +import org.jetbrains.kotlin.descriptors.VariableDescriptor import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.diagnostics.Errors.* import org.jetbrains.kotlin.psi.* @@ -123,6 +124,18 @@ class DoubleColonExpressionResolver( } } + private fun KtExpression.canBeConsideredProperType(): Boolean { + return when (this) { + is KtSimpleNameExpression -> true + is KtDotQualifiedExpression -> { + receiverExpression.canBeConsideredProperType() && selectorExpression.let { selector -> + selector is KtSimpleNameExpression || (selector is KtCallExpression && selector.isWithoutValueArguments) + } + } + else -> false + } + } + private fun shouldTryResolveLHSAsExpression(expression: KtDoubleColonExpression): Boolean { // TODO: improve diagnostic when bound callable references are disabled if (!languageFeatureSettings.supportsFeature(LanguageFeature.BoundCallableReferences)) return false @@ -143,6 +156,8 @@ class DoubleColonExpressionResolver( val type = typeInfo.type if (type != null) { + // Be careful not to call a utility function to get a resolved call by an expression which may accidentally + // deparenthesize that expression, as this is undesirable here val call = traceForExpr.trace.bindingContext[BindingContext.CALL, expression.getQualifiedElementSelector()] val resolvedCall = call.getResolvedCall(traceForExpr.trace.bindingContext) val implicitReferenceToCompanion = @@ -152,7 +167,12 @@ class DoubleColonExpressionResolver( result.classDescriptor.companionObjectDescriptor != null } - if (!implicitReferenceToCompanion) { + val resolvedToFunctionWithoutArguments = + resolvedCall != null && + expression.canBeConsideredProperType() && + resolvedCall.resultingDescriptor !is VariableDescriptor + + if (!implicitReferenceToCompanion && !resolvedToFunctionWithoutArguments) { traceForExpr.commit() return DoubleColonLHS.Expression(typeInfo).apply { c.trace.record(BindingContext.DOUBLE_COLON_LHS, expression, this) diff --git a/compiler/testData/diagnostics/tests/callableReference/bound/functionCallWithoutArguments.kt b/compiler/testData/diagnostics/tests/callableReference/bound/functionCallWithoutArguments.kt new file mode 100644 index 00000000000..a03b0703fd5 --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/bound/functionCallWithoutArguments.kt @@ -0,0 +1 @@ +fun test() = ("").hashCode::hashCode diff --git a/compiler/testData/diagnostics/tests/callableReference/bound/functionCallWithoutArguments.txt b/compiler/testData/diagnostics/tests/callableReference/bound/functionCallWithoutArguments.txt new file mode 100644 index 00000000000..e1afed3783c --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/bound/functionCallWithoutArguments.txt @@ -0,0 +1,3 @@ +package + +public fun test(): kotlin.reflect.KFunction0 diff --git a/compiler/testData/diagnostics/tests/callableReference/function/classMemberVsConstructorLikeFunction.kt b/compiler/testData/diagnostics/tests/callableReference/function/classMemberVsConstructorLikeFunction.kt new file mode 100644 index 00000000000..a0062c04fa4 --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/function/classMemberVsConstructorLikeFunction.kt @@ -0,0 +1,16 @@ +// FILE: Foo.kt + +package test + +class Foo { + fun bar() {} +} + +// FILE: test.kt + +import test.Foo + +fun Foo(): String = "" + +val f = Foo::bar +val g = Foo::length diff --git a/compiler/testData/diagnostics/tests/callableReference/function/classMemberVsConstructorLikeFunction.txt b/compiler/testData/diagnostics/tests/callableReference/function/classMemberVsConstructorLikeFunction.txt new file mode 100644 index 00000000000..f40a89c432f --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/function/classMemberVsConstructorLikeFunction.txt @@ -0,0 +1,16 @@ +package + +public val f: kotlin.reflect.KFunction1 +public val g: [ERROR : Type for Foo::length] +public fun Foo(): kotlin.String + +package test { + + public final class Foo { + public constructor Foo() + public final fun bar(): 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 + } +} diff --git a/compiler/testData/diagnostics/tests/callableReference/sam.kt b/compiler/testData/diagnostics/tests/callableReference/sam.kt new file mode 100644 index 00000000000..e733d9060f1 --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/sam.kt @@ -0,0 +1,31 @@ +// FILE: test/GenericSam.java + +package test; + +public interface GenericSam { + void invoke(T t); +} + +// FILE: test.kt + +import test.GenericSam + +fun f1() = Runnable::class +fun f2() = Runnable::run +fun f3() = java.lang.Runnable::class +fun f4() = java.lang.Runnable::run + +fun f5() = GenericSam::class +fun f6() = GenericSam<*>::invoke +fun f7() = test.GenericSam::class +fun f8() = test.GenericSam::invoke + +fun g1() = Runnable {}::class +fun g2() = Runnable {}::run +fun g3() = java.lang.Runnable {}::class +fun g4() = java.lang.Runnable {}::run + +fun g5() = GenericSam {}::class +fun g6() = GenericSam {}::invoke +fun g7() = test.GenericSam {}::class +fun g8() = test.GenericSam {}::invoke diff --git a/compiler/testData/diagnostics/tests/callableReference/sam.txt b/compiler/testData/diagnostics/tests/callableReference/sam.txt new file mode 100644 index 00000000000..09bf9d12812 --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/sam.txt @@ -0,0 +1,18 @@ +package + +public fun f1(): kotlin.reflect.KClass +public fun f2(): kotlin.reflect.KFunction1 +public fun f3(): kotlin.reflect.KClass +public fun f4(): kotlin.reflect.KFunction1 +public fun f5(): kotlin.reflect.KClass> +public fun f6(): kotlin.reflect.KFunction2, kotlin.Nothing!, kotlin.Unit> +public fun f7(): kotlin.reflect.KClass> +public fun f8(): kotlin.reflect.KFunction2, kotlin.String!, kotlin.Unit> +public fun g1(): kotlin.reflect.KClass +public fun g2(): kotlin.reflect.KFunction0 +public fun g3(): kotlin.reflect.KClass +public fun g4(): kotlin.reflect.KFunction0 +public fun g5(): kotlin.reflect.KClass> +public fun g6(): kotlin.reflect.KFunction1 +public fun g7(): kotlin.reflect.KClass> +public fun g8(): kotlin.reflect.KFunction1 diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 294052440d3..4f21098d6e0 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -1701,6 +1701,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("sam.kt") + public void testSam() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/callableReference/sam.kt"); + doTest(fileName); + } + @TestMetadata("unused.kt") public void testUnused() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/callableReference/unused.kt"); @@ -1727,6 +1733,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("functionCallWithoutArguments.kt") + public void testFunctionCallWithoutArguments() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/callableReference/bound/functionCallWithoutArguments.kt"); + doTest(fileName); + } + @TestMetadata("object.kt") public void testObject() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/callableReference/bound/object.kt"); @@ -1766,6 +1778,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("classMemberVsConstructorLikeFunction.kt") + public void testClassMemberVsConstructorLikeFunction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/callableReference/function/classMemberVsConstructorLikeFunction.kt"); + doTest(fileName); + } + @TestMetadata("constructorFromClass.kt") public void testConstructorFromClass() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/callableReference/function/constructorFromClass.kt");