Syntax forms 'a<T>::foo', 'a?::foo' have special cases resolved for future releases.
'a<T>::foo' is reserved if 'a' is a simple name and can be resolved as an expression (this can be extended to 'a.b.c<T>::foo' case, although that is rather hard to implement using PSI). 'a?::foo' is reserved if 'a' can be resolved as an expression.
This commit is contained in:
@@ -574,6 +574,8 @@ public interface Errors {
|
|||||||
DiagnosticFactory3<KtReferenceExpression, ClassifierDescriptor, WrongResolutionToClassifier, String> RESOLUTION_TO_CLASSIFIER =
|
DiagnosticFactory3<KtReferenceExpression, ClassifierDescriptor, WrongResolutionToClassifier, String> RESOLUTION_TO_CLASSIFIER =
|
||||||
DiagnosticFactory3.create(ERROR);
|
DiagnosticFactory3.create(ERROR);
|
||||||
|
|
||||||
|
DiagnosticFactory0<KtExpression> RESERVED_SYNTAX_IN_CALLABLE_REFERENCE_LHS = DiagnosticFactory0.create(ERROR);
|
||||||
|
|
||||||
// Type inference
|
// Type inference
|
||||||
|
|
||||||
DiagnosticFactory0<KtParameter> CANNOT_INFER_PARAMETER_TYPE = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<KtParameter> CANNOT_INFER_PARAMETER_TYPE = DiagnosticFactory0.create(ERROR);
|
||||||
|
|||||||
+2
@@ -331,6 +331,8 @@ public class DefaultErrorMessages {
|
|||||||
MAP.put(NOT_A_CLASS, "Not a class");
|
MAP.put(NOT_A_CLASS, "Not a class");
|
||||||
MAP.put(ILLEGAL_ESCAPE_SEQUENCE, "Illegal escape sequence");
|
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_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_GETTER, "Local variables are not allowed to have getters");
|
||||||
MAP.put(LOCAL_VARIABLE_WITH_SETTER, "Local variables are not allowed to have setters");
|
MAP.put(LOCAL_VARIABLE_WITH_SETTER, "Local variables are not allowed to have setters");
|
||||||
|
|||||||
+54
-7
@@ -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<T>::...'
|
||||||
|
// TODO 'expr.name<T>', 'expr<T>.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? {
|
private fun resolveDoubleColonLHS(doubleColonExpression: KtDoubleColonExpression, c: ExpressionTypingContext): DoubleColonLHS? {
|
||||||
val resultForExpr = tryResolveLHS(doubleColonExpression, c, this::shouldTryResolveLHSAsExpression, this::resolveExpressionOnLHS)
|
val resultForExpr = tryResolveLHS(doubleColonExpression, c, this::shouldTryResolveLHSAsExpression, this::resolveExpressionOnLHS)
|
||||||
if (resultForExpr != null) {
|
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
|
// 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
|
// another type (representing another classifier) should win
|
||||||
if (lhs != null && !lhs.isObject) {
|
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 ->
|
val resultForType = tryResolveLHS(doubleColonExpression, c, this::shouldTryResolveLHSAsType) { expression, context ->
|
||||||
resolveTypeOnLHS(expression, doubleColonExpression, context)
|
resolveTypeOnLHS(expression, doubleColonExpression, context)
|
||||||
}
|
}
|
||||||
@@ -205,12 +252,12 @@ class DoubleColonExpressionResolver(
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
private class LHSResolutionResult(
|
private class LHSResolutionResult<out T : DoubleColonLHS>(
|
||||||
val lhs: DoubleColonLHS?,
|
val lhs: T?,
|
||||||
val expression: KtExpression,
|
val expression: KtExpression,
|
||||||
val traceAndCache: TemporaryTraceAndCache
|
val traceAndCache: TemporaryTraceAndCache
|
||||||
) {
|
) {
|
||||||
fun commit(): DoubleColonLHS? {
|
fun commit(): T? {
|
||||||
if (lhs != null) {
|
if (lhs != null) {
|
||||||
traceAndCache.trace.record(BindingContext.DOUBLE_COLON_LHS, expression, lhs)
|
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
|
* 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.
|
* either a successful result or not.
|
||||||
*/
|
*/
|
||||||
private fun tryResolveLHS(
|
private fun <T : DoubleColonLHS> tryResolveLHS(
|
||||||
doubleColonExpression: KtDoubleColonExpression,
|
doubleColonExpression: KtDoubleColonExpression,
|
||||||
context: ExpressionTypingContext,
|
context: ExpressionTypingContext,
|
||||||
criterion: (KtDoubleColonExpression) -> Boolean,
|
criterion: (KtDoubleColonExpression) -> Boolean,
|
||||||
resolve: (KtExpression, ExpressionTypingContext) -> DoubleColonLHS?
|
resolve: (KtExpression, ExpressionTypingContext) -> T?
|
||||||
): LHSResolutionResult? {
|
): LHSResolutionResult<T>? {
|
||||||
val expression = doubleColonExpression.receiverExpression ?: return null
|
val expression = doubleColonExpression.receiverExpression ?: return null
|
||||||
|
|
||||||
if (!criterion(doubleColonExpression)) return null
|
if (!criterion(doubleColonExpression)) return null
|
||||||
|
|||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
// !DIAGNOSTICS: -UNUSED_VARIABLE
|
||||||
|
package test
|
||||||
|
|
||||||
|
object ClassMemberMarker
|
||||||
|
|
||||||
|
class a<T> {
|
||||||
|
fun foo() = ClassMemberMarker
|
||||||
|
}
|
||||||
|
|
||||||
|
class b<T1, T2> {
|
||||||
|
fun foo() = ClassMemberMarker
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Int.foo() {}
|
||||||
|
|
||||||
|
class Test {
|
||||||
|
val <T> List<T>.a: Int get() = size
|
||||||
|
val <T> List<T>.b: Int? get() = size
|
||||||
|
|
||||||
|
fun <T> List<T>.testCallable1(): () -> Unit = <!RESERVED_SYNTAX_IN_CALLABLE_REFERENCE_LHS!>a<T><!>::foo
|
||||||
|
fun <T> List<T>.testCallable2(): () -> Unit = <!RESERVED_SYNTAX_IN_CALLABLE_REFERENCE_LHS!>b<!>?::<!UNSAFE_CALL!>foo<!>
|
||||||
|
fun <T> List<T>.testCallable3(): () -> Unit = <!RESERVED_SYNTAX_IN_CALLABLE_REFERENCE_LHS!>b<T, Any><!>::<!UNSAFE_CALL!>foo<!>
|
||||||
|
fun <T> List<T>.testCallable4(): () -> Unit = <!RESERVED_SYNTAX_IN_CALLABLE_REFERENCE_LHS!>b<T><!>?::<!UNSAFE_CALL!>foo<!>
|
||||||
|
|
||||||
|
fun <T> List<T>.testClassLiteral1() = <!RESERVED_SYNTAX_IN_CALLABLE_REFERENCE_LHS!>a<T><!>::class
|
||||||
|
fun <T> List<T>.testClassLiteral2() = <!RESERVED_SYNTAX_IN_CALLABLE_REFERENCE_LHS!>b<!>?::class
|
||||||
|
fun <T> List<T>.testClassLiteral3() = <!RESERVED_SYNTAX_IN_CALLABLE_REFERENCE_LHS!>b<T, Any><!>::class
|
||||||
|
|
||||||
|
fun <T> List<T>.testUnresolved1() = <!UNRESOLVED_REFERENCE!>unresolved<!><T>::<!UNRESOLVED_REFERENCE_WRONG_RECEIVER!>foo<!>
|
||||||
|
fun <T> List<T>.testUnresolved2() = <!RESERVED_SYNTAX_IN_CALLABLE_REFERENCE_LHS!>a<<!UNRESOLVED_REFERENCE!>unresolved<!>><!>::foo
|
||||||
|
fun <T> List<T>.testUnresolved3() = a<<!SYNTAX!><!>>::foo
|
||||||
|
fun <T> List<T>.testUnresolved4() = <!UNRESOLVED_REFERENCE!>unresolved<!>?::<!UNRESOLVED_REFERENCE_WRONG_RECEIVER!>foo<!>
|
||||||
|
}
|
||||||
+48
@@ -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 </*0*/ T> kotlin.collections.List<T>.a: kotlin.Int
|
||||||
|
public final val </*0*/ T> kotlin.collections.List<T>.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 </*0*/ T> kotlin.collections.List<T>.testCallable1(): () -> kotlin.Unit
|
||||||
|
public final fun </*0*/ T> kotlin.collections.List<T>.testCallable2(): () -> kotlin.Unit
|
||||||
|
public final fun </*0*/ T> kotlin.collections.List<T>.testCallable3(): () -> kotlin.Unit
|
||||||
|
public final fun </*0*/ T> kotlin.collections.List<T>.testCallable4(): () -> kotlin.Unit
|
||||||
|
public final fun </*0*/ T> kotlin.collections.List<T>.testClassLiteral1(): kotlin.reflect.KClass<kotlin.Int>
|
||||||
|
public final fun </*0*/ T> kotlin.collections.List<T>.testClassLiteral2(): kotlin.reflect.KClass<kotlin.Int?>
|
||||||
|
public final fun </*0*/ T> kotlin.collections.List<T>.testClassLiteral3(): kotlin.reflect.KClass<kotlin.Int?>
|
||||||
|
public final fun </*0*/ T> kotlin.collections.List<T>.testUnresolved1(): [ERROR : Error function type]
|
||||||
|
public final fun </*0*/ T> kotlin.collections.List<T>.testUnresolved2(): kotlin.reflect.KFunction0<kotlin.Unit>
|
||||||
|
public final fun </*0*/ T> kotlin.collections.List<T>.testUnresolved3(): kotlin.reflect.KFunction0<kotlin.Unit>
|
||||||
|
public final fun </*0*/ T> kotlin.collections.List<T>.testUnresolved4(): [ERROR : Error function type]
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class a</*0*/ T> {
|
||||||
|
public constructor a</*0*/ T>()
|
||||||
|
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</*0*/ T1, /*1*/ T2> {
|
||||||
|
public constructor b</*0*/ T1, /*1*/ T2>()
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// !DIAGNOSTICS: -UNUSED_VARIABLE
|
||||||
|
package test
|
||||||
|
|
||||||
|
fun nullableFun(): Int? = null
|
||||||
|
fun Int.foo() {}
|
||||||
|
|
||||||
|
val test1 = <!RESERVED_SYNTAX_IN_CALLABLE_REFERENCE_LHS!>nullableFun()<!>?::<!UNSAFE_CALL!>foo<!>
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
package test {
|
||||||
|
public val test1: kotlin.reflect.KFunction0<kotlin.Unit>
|
||||||
|
public fun nullableFun(): kotlin.Int?
|
||||||
|
public fun kotlin.Int.foo(): kotlin.Unit
|
||||||
|
}
|
||||||
@@ -1848,6 +1848,18 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("syntheticExtensionOnLHS.kt")
|
||||||
public void testSyntheticExtensionOnLHS() throws Exception {
|
public void testSyntheticExtensionOnLHS() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/callableReference/bound/syntheticExtensionOnLHS.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/callableReference/bound/syntheticExtensionOnLHS.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user