From d8263eb0b8815b895a4caf4392232806f033b63f Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Mon, 11 Apr 2016 12:19:49 +0300 Subject: [PATCH] Assert that callable references cannot have expression on LHS --- .../resolve/calls/CallExpressionResolver.kt | 25 ++++--- .../BasicExpressionTypingVisitor.java | 75 ++++++++++++++++++- .../kotlin/resolve/DescriptorUtils.java | 2 +- 3 files changed, 89 insertions(+), 13 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallExpressionResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallExpressionResolver.kt index fc3cf89ab2e..334a392da87 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallExpressionResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallExpressionResolver.kt @@ -20,10 +20,7 @@ import com.intellij.lang.ASTNode import com.intellij.psi.PsiElement import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.ConstructorDescriptor -import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.diagnostics.Errors.* import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getParentOfType @@ -39,6 +36,7 @@ import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext import org.jetbrains.kotlin.resolve.calls.context.TemporaryTraceAndCache import org.jetbrains.kotlin.resolve.calls.model.DataFlowInfoForArgumentsImpl import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults.Code.CANDIDATES_WITH_WRONG_RECEIVER import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults.Code.NAME_NOT_FOUND import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResultsUtil @@ -266,13 +264,7 @@ class CallExpressionResolver( private fun KtQualifiedExpression.elementChain(context: ExpressionTypingContext) = qualifiedExpressionResolver.resolveQualifierInExpressionAndUnroll(this, context) { nameExpression -> - val temporaryForVariable = TemporaryTraceAndCache.create( - context, "trace to resolve as local variable or property", nameExpression) - val call = CallMaker.makePropertyCall(null, null, nameExpression) - val contextForVariable = BasicCallResolutionContext.create( - context.replaceTraceAndCache(temporaryForVariable), - call, CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS) - val resolutionResult = callResolver.resolveSimpleProperty(contextForVariable) + val resolutionResult = resolveSimpleName(context, nameExpression) if (resolutionResult.isSingleResult && resolutionResult.resultingDescriptor is FakeCallableDescriptorForObject) { false @@ -283,6 +275,17 @@ class CallExpressionResolver( } } + fun resolveSimpleName( + context: ExpressionTypingContext, expression: KtSimpleNameExpression + ): OverloadResolutionResults { + val temporaryForVariable = TemporaryTraceAndCache.create(context, "trace to resolve as local variable or property", expression) + val call = CallMaker.makePropertyCall(null, null, expression) + val contextForVariable = BasicCallResolutionContext.create( + context.replaceTraceAndCache(temporaryForVariable), call, CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS + ) + return callResolver.resolveSimpleProperty(contextForVariable) + } + private fun getUnsafeSelectorTypeInfo( receiver: Receiver, callOperationNode: ASTNode?, diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java index 68eece0179b..0d88108cb16 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java @@ -32,6 +32,7 @@ import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.descriptors.annotations.Annotations; import org.jetbrains.kotlin.diagnostics.Diagnostic; import org.jetbrains.kotlin.diagnostics.Errors; +import org.jetbrains.kotlin.diagnostics.Severity; import org.jetbrains.kotlin.lexer.KtKeywordToken; import org.jetbrains.kotlin.lexer.KtTokens; import org.jetbrains.kotlin.name.Name; @@ -41,9 +42,11 @@ import org.jetbrains.kotlin.resolve.bindingContextUtil.BindingContextUtilsKt; import org.jetbrains.kotlin.resolve.callableReferences.CallableReferencesResolutionUtilsKt; import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver; import org.jetbrains.kotlin.resolve.calls.CallExpressionResolver; +import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt; import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker; import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext; import org.jetbrains.kotlin.resolve.calls.context.CheckArgumentTypesMode; +import org.jetbrains.kotlin.resolve.calls.context.TemporaryTraceAndCache; import org.jetbrains.kotlin.resolve.calls.model.DataFlowInfoForArgumentsImpl; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCallImpl; @@ -58,6 +61,7 @@ import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind; import org.jetbrains.kotlin.resolve.calls.tasks.ResolutionCandidate; import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy; import org.jetbrains.kotlin.resolve.calls.util.CallMaker; +import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject; import org.jetbrains.kotlin.resolve.constants.*; import org.jetbrains.kotlin.resolve.scopes.LexicalScopeKind; import org.jetbrains.kotlin.resolve.scopes.LexicalWritableScope; @@ -711,10 +715,79 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { return components.dataFlowAnalyzer.createCheckedTypeInfo(errorType, c, expression); } - KotlinType result = getCallableReferenceType(expression, receiverType, c); + TemporaryBindingTrace trace = TemporaryBindingTrace.create(c.trace, "Callable reference type"); + KotlinType result = getCallableReferenceType(expression, receiverType, c.replaceBindingTrace(trace)); + boolean hasErrors = hasErrors(trace); // Do not inline this local variable (execution order is important) + trace.commit(); + if (!hasErrors && result != null) { + checkNoExpressionOnLHS(expression, c); + } return components.dataFlowAnalyzer.createCheckedTypeInfo(result, c, expression); } + private static boolean hasErrors(TemporaryBindingTrace trace) { + for (Diagnostic diagnostic : trace.getBindingContext().getDiagnostics().all()) { + if (diagnostic.getSeverity() == Severity.ERROR) { + return true; + } + } + return false; + } + + private void checkNoExpressionOnLHS(@NotNull KtCallableReferenceExpression expression, @NotNull ExpressionTypingContext c) { + KtTypeReference typeReference = expression.getTypeReference(); + if (typeReference == null) return; + KtTypeElement typeElement = typeReference.getTypeElement(); + if (!(typeElement instanceof KtUserType)) return; + + KtUserType userType = (KtUserType) typeElement; + while (true) { + if (userType.getTypeArgumentList() != null) return; + KtUserType qualifier = userType.getQualifier(); + if (qualifier == null) break; + userType = qualifier; + } + + KtSimpleNameExpression simpleNameExpression = userType.getReferenceExpression(); + if (simpleNameExpression == null) return; + + TemporaryTraceAndCache traceAndCache = + TemporaryTraceAndCache.create(c, "Resolve expression on LHS of callable reference", simpleNameExpression); + OverloadResolutionResults resolutionResult = + components.callExpressionResolver.resolveSimpleName(c.replaceTraceAndCache(traceAndCache), simpleNameExpression); + + Collection> resultingCalls = + CollectionsKt.filter(resolutionResult.getResultingCalls(), new Function1, Boolean>() { + @Override + public Boolean invoke(ResolvedCall call) { + return call.getStatus().possibleTransformToSuccess(); + } + }); + + if (resultingCalls.isEmpty()) return; + + if (resultingCalls.size() == 1 && + resultingCalls.iterator().next().getResultingDescriptor() instanceof FakeCallableDescriptorForObject) return; + + ResolvedCall originalResolvedCall = CallUtilKt.getResolvedCall(expression.getCallableReference(), c.trace.getBindingContext()); + CallableDescriptor originalResult = originalResolvedCall == null ? null : originalResolvedCall.getResultingDescriptor(); + + throw new AssertionError(String.format( + "Expressions on left-hand side of callable reference are not supported yet.\n" + + "Resolution result: %s\n" + + "Original result: %s", + CollectionsKt.map( + resultingCalls, new Function1, VariableDescriptor>() { + @Override + public VariableDescriptor invoke(ResolvedCall call) { + return call.getResultingDescriptor(); + } + } + ), + originalResult + )); + } + @Override public KotlinTypeInfo visitObjectLiteralExpression( @NotNull final KtObjectLiteralExpression expression, diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java index 8de36885919..39f309e8f56 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java @@ -284,7 +284,7 @@ public class DescriptorUtils { return isKindOf(descriptor, ClassKind.OBJECT) && !((ClassDescriptor) descriptor).isCompanionObject(); } - public static boolean isObject(@NotNull DeclarationDescriptor descriptor) { + public static boolean isObject(@Nullable DeclarationDescriptor descriptor) { return isKindOf(descriptor, ClassKind.OBJECT); }