diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index f54dfc71d2a..79bdc858dc0 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -574,6 +574,8 @@ public interface Errors { DiagnosticFactory3 RESOLUTION_TO_CLASSIFIER = DiagnosticFactory3.create(ERROR); + DiagnosticFactory0 RESERVED_SYNTAX_IN_CALLABLE_REFERENCE_LHS = DiagnosticFactory0.create(ERROR); + // Type inference DiagnosticFactory0 CANNOT_INFER_PARAMETER_TYPE = DiagnosticFactory0.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 a6457fe5869..81a79f735f9 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -331,6 +331,8 @@ public class DefaultErrorMessages { MAP.put(NOT_A_CLASS, "Not a class"); MAP.put(ILLEGAL_ESCAPE_SEQUENCE, "Illegal escape sequence"); + MAP.put(RESERVED_SYNTAX_IN_CALLABLE_REFERENCE_LHS, "Left-hand side of callable reference matches expression syntax reserved for future releases"); + MAP.put(LOCAL_EXTENSION_PROPERTY, "Local extension properties are not allowed"); MAP.put(LOCAL_VARIABLE_WITH_GETTER, "Local variables are not allowed to have getters"); MAP.put(LOCAL_VARIABLE_WITH_SETTER, "Local variables are not allowed to have setters"); 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 97ca31b9377..ba3220b9495 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt @@ -170,10 +170,46 @@ class DoubleColonExpressionResolver( } } + private fun shouldTryResolveLHSAsReservedExpression(expression: KtDoubleColonExpression): Boolean { + val lhs = expression.receiverExpression ?: return false + return (expression.hasQuestionMarks && lhs.canBeConsideredProperExpression()) || + (lhs is KtCallExpression && lhs.canBeReservedGenericPropertyCall()) + } + + private fun KtCallExpression.canBeReservedGenericPropertyCall(): Boolean = + // This condition captures 'name::...' + // TODO 'expr.name', 'expr.name' - discuss + calleeExpression is KtNameReferenceExpression && + valueArguments.isEmpty() && + typeArguments.isNotEmpty() + + private fun resolveReservedExpressionOnLHS(expression: KtExpression, c: ExpressionTypingContext): DoubleColonLHS.Expression? { + val doubleColonExpression = expression.parent as? KtDoubleColonExpression ?: + return null // should assert here? + + if (expression is KtCallExpression && expression.typeArguments.isNotEmpty()) { + val callee = expression.calleeExpression ?: return null + val calleeAsDoubleColonLHS = resolveExpressionOnLHS(callee, c) ?: return null + + for (typeArgument in expression.typeArguments) { + val typeReference = typeArgument.typeReference ?: continue + typeResolver.resolveType(c.scope, typeReference, c.trace, true) + } + + return calleeAsDoubleColonLHS + } + else if (doubleColonExpression.hasQuestionMarks) { + return resolveExpressionOnLHS(expression, c) + } + else { + return null + } + } + private fun resolveDoubleColonLHS(doubleColonExpression: KtDoubleColonExpression, c: ExpressionTypingContext): DoubleColonLHS? { val resultForExpr = tryResolveLHS(doubleColonExpression, c, this::shouldTryResolveLHSAsExpression, this::resolveExpressionOnLHS) if (resultForExpr != null) { - val lhs = resultForExpr.lhs as DoubleColonLHS.Expression? + val lhs = resultForExpr.lhs // If expression result is an object, we remember this and skip it here, because there are valid situations where // another type (representing another classifier) should win if (lhs != null && !lhs.isObject) { @@ -181,6 +217,17 @@ class DoubleColonExpressionResolver( } } + val resultForReservedExpr = tryResolveLHS(doubleColonExpression, c, + this::shouldTryResolveLHSAsReservedExpression, + this::resolveReservedExpressionOnLHS) + if (resultForReservedExpr != null) { + val lhs = resultForReservedExpr.lhs + if (lhs != null) { + c.trace.report(RESERVED_SYNTAX_IN_CALLABLE_REFERENCE_LHS.on(resultForReservedExpr.expression)) + return resultForReservedExpr.commit() + } + } + val resultForType = tryResolveLHS(doubleColonExpression, c, this::shouldTryResolveLHSAsType) { expression, context -> resolveTypeOnLHS(expression, doubleColonExpression, context) } @@ -205,12 +252,12 @@ class DoubleColonExpressionResolver( return null } - private class LHSResolutionResult( - val lhs: DoubleColonLHS?, + private class LHSResolutionResult( + val lhs: T?, val expression: KtExpression, val traceAndCache: TemporaryTraceAndCache ) { - fun commit(): DoubleColonLHS? { + fun commit(): T? { if (lhs != null) { traceAndCache.trace.record(BindingContext.DOUBLE_COLON_LHS, expression, lhs) } @@ -223,12 +270,12 @@ class DoubleColonExpressionResolver( * Returns null if the LHS is definitely not an expression. Returns a non-null result if a resolution was attempted and led to * either a successful result or not. */ - private fun tryResolveLHS( + private fun tryResolveLHS( doubleColonExpression: KtDoubleColonExpression, context: ExpressionTypingContext, criterion: (KtDoubleColonExpression) -> Boolean, - resolve: (KtExpression, ExpressionTypingContext) -> DoubleColonLHS? - ): LHSResolutionResult? { + resolve: (KtExpression, ExpressionTypingContext) -> T? + ): LHSResolutionResult? { val expression = doubleColonExpression.receiverExpression ?: return null if (!criterion(doubleColonExpression)) return null diff --git a/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax.kt b/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax.kt new file mode 100644 index 00000000000..08f80ca122d --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax.kt @@ -0,0 +1,33 @@ +// !DIAGNOSTICS: -UNUSED_VARIABLE +package test + +object ClassMemberMarker + +class a { + fun foo() = ClassMemberMarker +} + +class b { + fun foo() = ClassMemberMarker +} + +fun Int.foo() {} + +class Test { + val List.a: Int get() = size + val List.b: Int? get() = size + + fun List.testCallable1(): () -> Unit = a::foo + fun List.testCallable2(): () -> Unit = b?::foo + fun List.testCallable3(): () -> Unit = b::foo + fun List.testCallable4(): () -> Unit = b?::foo + + fun List.testClassLiteral1() = a::class + fun List.testClassLiteral2() = b?::class + fun List.testClassLiteral3() = b::class + + fun List.testUnresolved1() = unresolved::foo + fun List.testUnresolved2() = a<unresolved>::foo + fun List.testUnresolved3() = a<>::foo + fun List.testUnresolved4() = unresolved?::foo +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax.txt b/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax.txt new file mode 100644 index 00000000000..e6fa2b8eb6f --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax.txt @@ -0,0 +1,48 @@ +package + +package test { + public fun kotlin.Int.foo(): kotlin.Unit + + public object ClassMemberMarker { + private constructor ClassMemberMarker() + 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 Test { + public constructor Test() + public final val kotlin.collections.List.a: kotlin.Int + public final val kotlin.collections.List.b: 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 + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + public final fun kotlin.collections.List.testCallable1(): () -> kotlin.Unit + public final fun kotlin.collections.List.testCallable2(): () -> kotlin.Unit + public final fun kotlin.collections.List.testCallable3(): () -> kotlin.Unit + public final fun kotlin.collections.List.testCallable4(): () -> kotlin.Unit + public final fun kotlin.collections.List.testClassLiteral1(): kotlin.reflect.KClass + public final fun kotlin.collections.List.testClassLiteral2(): kotlin.reflect.KClass + public final fun kotlin.collections.List.testClassLiteral3(): kotlin.reflect.KClass + public final fun kotlin.collections.List.testUnresolved1(): [ERROR : Error function type] + public final fun kotlin.collections.List.testUnresolved2(): kotlin.reflect.KFunction0 + public final fun kotlin.collections.List.testUnresolved3(): kotlin.reflect.KFunction0 + public final fun kotlin.collections.List.testUnresolved4(): [ERROR : Error function type] + } + + public final class a { + public constructor a() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun foo(): test.ClassMemberMarker + 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 final fun foo(): test.ClassMemberMarker + 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/bound/reservedExpressionSyntax2.kt b/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax2.kt new file mode 100644 index 00000000000..3650411d391 --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax2.kt @@ -0,0 +1,7 @@ +// !DIAGNOSTICS: -UNUSED_VARIABLE +package test + +fun nullableFun(): Int? = null +fun Int.foo() {} + +val test1 = nullableFun()?::foo \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax2.txt b/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax2.txt new file mode 100644 index 00000000000..69a542f3707 --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax2.txt @@ -0,0 +1,7 @@ +package + +package test { + public val test1: kotlin.reflect.KFunction0 + public fun nullableFun(): kotlin.Int? + public fun kotlin.Int.foo(): kotlin.Unit +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index e1f74185ec5..175c11fd2b1 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -1848,6 +1848,18 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("reservedExpressionSyntax.kt") + public void testReservedExpressionSyntax() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax.kt"); + doTest(fileName); + } + + @TestMetadata("reservedExpressionSyntax2.kt") + public void testReservedExpressionSyntax2() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax2.kt"); + doTest(fileName); + } + @TestMetadata("syntheticExtensionOnLHS.kt") public void testSyntheticExtensionOnLHS() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/callableReference/bound/syntheticExtensionOnLHS.kt");