Assert that callable references cannot have expression on LHS

This commit is contained in:
Alexander Udalov
2016-04-11 12:19:49 +03:00
parent 92f3795a11
commit d8263eb0b8
3 changed files with 89 additions and 13 deletions
@@ -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<VariableDescriptor> {
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?,
@@ -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<VariableDescriptor> resolutionResult =
components.callExpressionResolver.resolveSimpleName(c.replaceTraceAndCache(traceAndCache), simpleNameExpression);
Collection<? extends ResolvedCall<VariableDescriptor>> resultingCalls =
CollectionsKt.filter(resolutionResult.getResultingCalls(), new Function1<ResolvedCall<VariableDescriptor>, Boolean>() {
@Override
public Boolean invoke(ResolvedCall<VariableDescriptor> 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<ResolvedCall<VariableDescriptor>, VariableDescriptor>() {
@Override
public VariableDescriptor invoke(ResolvedCall<VariableDescriptor> call) {
return call.getResultingDescriptor();
}
}
),
originalResult
));
}
@Override
public KotlinTypeInfo visitObjectLiteralExpression(
@NotNull final KtObjectLiteralExpression expression,
@@ -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);
}