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:
Dmitry Petrov
2016-11-28 09:59:52 +03:00
parent 5d1b848658
commit 2cd6c85cc7
8 changed files with 165 additions and 7 deletions
@@ -574,6 +574,8 @@ public interface Errors {
DiagnosticFactory3<KtReferenceExpression, ClassifierDescriptor, WrongResolutionToClassifier, String> RESOLUTION_TO_CLASSIFIER =
DiagnosticFactory3.create(ERROR);
DiagnosticFactory0<KtExpression> RESERVED_SYNTAX_IN_CALLABLE_REFERENCE_LHS = DiagnosticFactory0.create(ERROR);
// Type inference
DiagnosticFactory0<KtParameter> CANNOT_INFER_PARAMETER_TYPE = DiagnosticFactory0.create(ERROR);
@@ -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");
@@ -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? {
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<out T : DoubleColonLHS>(
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 <T : DoubleColonLHS> tryResolveLHS(
doubleColonExpression: KtDoubleColonExpression,
context: ExpressionTypingContext,
criterion: (KtDoubleColonExpression) -> Boolean,
resolve: (KtExpression, ExpressionTypingContext) -> DoubleColonLHS?
): LHSResolutionResult? {
resolve: (KtExpression, ExpressionTypingContext) -> T?
): LHSResolutionResult<T>? {
val expression = doubleColonExpression.receiverExpression ?: return null
if (!criterion(doubleColonExpression)) return null