Extract '::' type-checking code to DoubleColonExpressionResolver

This commit is contained in:
Alexander Udalov
2016-04-18 12:48:32 +03:00
parent 4706d4eaea
commit be62caf335
3 changed files with 232 additions and 229 deletions
@@ -21,7 +21,6 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import com.intellij.psi.util.PsiTreeUtil;
import kotlin.collections.CollectionsKt;
import kotlin.jvm.functions.Function0;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull;
@@ -29,24 +28,19 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.KtNodeTypes;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
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;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.*;
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;
@@ -61,7 +55,6 @@ 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;
@@ -78,7 +71,6 @@ import org.jetbrains.kotlin.util.slicedMap.WritableSlice;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import static org.jetbrains.kotlin.diagnostics.Errors.*;
@@ -595,197 +587,12 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
@Override
public KotlinTypeInfo visitClassLiteralExpression(@NotNull KtClassLiteralExpression expression, ExpressionTypingContext c) {
KotlinType type = resolveClassLiteral(expression, c);
if (type != null && !type.isError()) {
return components.dataFlowAnalyzer.createCheckedTypeInfo(
components.reflectionTypes.getKClassType(Annotations.Companion.getEMPTY(), type), c, expression
);
}
return TypeInfoFactoryKt.createTypeInfo(ErrorUtils.createErrorType("Unresolved class"), c);
}
@Nullable
private KotlinType resolveClassLiteral(@NotNull KtClassLiteralExpression expression, ExpressionTypingContext c) {
KtTypeReference typeReference = expression.getTypeReference();
if (typeReference == null) {
// "::class" will mean "this::class", a class of "this" instance
c.trace.report(UNSUPPORTED.on(expression, "Class literals with empty left hand side are not yet supported"));
return null;
}
TypeResolutionContext context =
new TypeResolutionContext(c.scope, c.trace, /* checkBounds = */ false, /* allowBareTypes = */ true, /* isDebuggerContext */ false);
PossiblyBareType possiblyBareType =
components.typeResolver.resolvePossiblyBareType(context, typeReference);
KotlinType type = null;
if (possiblyBareType.isBare()) {
if (!possiblyBareType.isNullable()) {
ClassifierDescriptor descriptor = possiblyBareType.getBareTypeConstructor().getDeclarationDescriptor();
if (descriptor instanceof ClassDescriptor) {
ClassDescriptor classDescriptor = (ClassDescriptor) descriptor;
if (KotlinBuiltIns.isNonPrimitiveArray(classDescriptor)) {
context.trace.report(ARRAY_CLASS_LITERAL_REQUIRES_ARGUMENT.on(expression));
return null;
}
type = substituteWithStarProjections(classDescriptor);
}
}
}
else {
KotlinType actualType = possiblyBareType.getActualType();
if (actualType.isError()) return null;
if (isAllowedInClassLiteral(actualType)) {
type = actualType;
}
}
if (type != null) {
return type;
}
context.trace.report(CLASS_LITERAL_LHS_NOT_A_CLASS.on(expression));
return null;
}
@NotNull
private static KotlinType substituteWithStarProjections(@NotNull ClassDescriptor descriptor) {
TypeConstructor typeConstructor = descriptor.getTypeConstructor();
List<TypeProjection> arguments =
CollectionsKt.map(typeConstructor.getParameters(), new Function1<TypeParameterDescriptor, TypeProjection>() {
@Override
public TypeProjection invoke(TypeParameterDescriptor descriptor) {
return TypeUtils.makeStarProjection(descriptor);
}
});
return KotlinTypeImpl.create(Annotations.Companion.getEMPTY(), descriptor, false, arguments);
}
private static boolean isAllowedInClassLiteral(@NotNull KotlinType type) {
return isClassAvailableAtRuntime(type, false);
}
private static boolean isClassAvailableAtRuntime(@NotNull KotlinType type, boolean canBeNullable) {
if (type.isMarkedNullable() && !canBeNullable) return false;
TypeConstructor typeConstructor = type.getConstructor();
ClassifierDescriptor typeDeclarationDescriptor = typeConstructor.getDeclarationDescriptor();
boolean typeIsArray = KotlinBuiltIns.isArray(type);
if (typeDeclarationDescriptor instanceof ClassDescriptor) {
List<TypeParameterDescriptor> parameters = typeConstructor.getParameters();
if (parameters.size() != type.getArguments().size()) return false;
Iterator<TypeProjection> typeArgumentsIterator = type.getArguments().iterator();
for (TypeParameterDescriptor parameter : parameters) {
if (!typeIsArray && !parameter.isReified()) return false;
TypeProjection typeArgument = typeArgumentsIterator.next();
if (typeArgument == null) return false;
if (typeArgument.isStarProjection()) return false;
if (!isClassAvailableAtRuntime(typeArgument.getType(), true)) return false;
}
return true;
}
else if (typeDeclarationDescriptor instanceof TypeParameterDescriptor) {
return ((TypeParameterDescriptor) typeDeclarationDescriptor).isReified();
}
return false;
return components.doubleColonExpressionResolver.visitClassLiteralExpression(expression, c);
}
@Override
public KotlinTypeInfo visitCallableReferenceExpression(@NotNull KtCallableReferenceExpression expression, ExpressionTypingContext c) {
KtTypeReference typeReference = expression.getTypeReference();
KotlinType receiverType =
typeReference == null
? null
: components.typeResolver.resolveType(c.scope, typeReference, c.trace, false);
KtSimpleNameExpression callableReference = expression.getCallableReference();
if (callableReference.getReferencedName().isEmpty()) {
c.trace.report(UNRESOLVED_REFERENCE.on(callableReference, callableReference));
KotlinType errorType = ErrorUtils.createErrorType("Empty callable reference");
return components.dataFlowAnalyzer.createCheckedTypeInfo(errorType, c, expression);
}
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
));
return components.doubleColonExpressionResolver.visitCallableReferenceExpression(expression, c);
}
@Override
@@ -847,40 +654,6 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
return resultTypeInfo;
}
@Nullable
private KotlinType getCallableReferenceType(
@NotNull KtCallableReferenceExpression expression,
@Nullable KotlinType lhsType,
@NotNull ExpressionTypingContext context
) {
KtSimpleNameExpression reference = expression.getCallableReference();
boolean[] resolved = new boolean[1];
CallableDescriptor descriptor = CallableReferencesResolutionUtilsKt.resolveCallableReferenceTarget(
expression, lhsType, context, resolved, components.callResolver);
if (!resolved[0]) {
context.trace.report(UNRESOLVED_REFERENCE.on(reference, reference));
}
if (descriptor == null) return null;
if (expression.getTypeReference() == null &&
(descriptor.getDispatchReceiverParameter() != null || descriptor.getExtensionReceiverParameter() != null)) {
context.trace.report(CALLABLE_REFERENCE_TO_MEMBER_OR_EXTENSION_WITH_EMPTY_LHS.on(reference));
}
DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
if (DescriptorUtils.isObject(containingDeclaration)) {
context.trace.report(CALLABLE_REFERENCE_TO_OBJECT_MEMBER.on(reference));
}
if (descriptor instanceof ConstructorDescriptor && DescriptorUtils.isAnnotationClass(containingDeclaration)) {
context.trace.report(CALLABLE_REFERENCE_TO_ANNOTATION_CONSTRUCTOR.on(reference));
}
return CallableReferencesResolutionUtilsKt.createReflectionTypeForResolvedCallableReference(
expression, lhsType, descriptor, context, components.reflectionTypes
);
}
@Override
public KotlinTypeInfo visitQualifiedExpression(@NotNull KtQualifiedExpression expression, ExpressionTypingContext context) {
CallExpressionResolver callExpressionResolver = components.callExpressionResolver;
@@ -0,0 +1,224 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.types.expressions
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.ReflectionTypes
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.diagnostics.Errors.*
import org.jetbrains.kotlin.diagnostics.Severity
import org.jetbrains.kotlin.psi.KtCallableReferenceExpression
import org.jetbrains.kotlin.psi.KtClassLiteralExpression
import org.jetbrains.kotlin.psi.KtUserType
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.TemporaryBindingTrace
import org.jetbrains.kotlin.resolve.TypeResolutionContext
import org.jetbrains.kotlin.resolve.TypeResolver
import org.jetbrains.kotlin.resolve.callableReferences.createReflectionTypeForResolvedCallableReference
import org.jetbrains.kotlin.resolve.callableReferences.resolveCallableReferenceTarget
import org.jetbrains.kotlin.resolve.calls.CallExpressionResolver
import org.jetbrains.kotlin.resolve.calls.CallResolver
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.context.TemporaryTraceAndCache
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject
import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.KotlinTypeImpl
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.createTypeInfo
class DoubleColonExpressionResolver(
val callResolver: CallResolver,
val callExpressionResolver: CallExpressionResolver,
val dataFlowAnalyzer: DataFlowAnalyzer,
val reflectionTypes: ReflectionTypes,
val typeResolver: TypeResolver
) {
fun visitClassLiteralExpression(expression: KtClassLiteralExpression, c: ExpressionTypingContext): KotlinTypeInfo {
val type = resolveClassLiteral(expression, c)
if (type != null && !type.isError) {
return dataFlowAnalyzer.createCheckedTypeInfo(reflectionTypes.getKClassType(Annotations.EMPTY, type), c, expression)
}
return createTypeInfo(ErrorUtils.createErrorType("Unresolved class"), c)
}
private fun resolveClassLiteral(expression: KtClassLiteralExpression, c: ExpressionTypingContext): KotlinType? {
val typeReference = expression.typeReference
if (typeReference == null) {
// "::class" will maybe mean "this::class", a class of "this" instance
c.trace.report(UNSUPPORTED.on(expression, "Class literals with empty left hand side are not yet supported"))
return null
}
val context = TypeResolutionContext(
c.scope, c.trace, /* checkBounds = */ false, /* allowBareTypes = */ true, /* isDebuggerContext = */ false
)
val possiblyBareType = typeResolver.resolvePossiblyBareType(context, typeReference)
var type: KotlinType? = null
if (possiblyBareType.isBare) {
if (!possiblyBareType.isNullable) {
val descriptor = possiblyBareType.bareTypeConstructor.declarationDescriptor
if (descriptor is ClassDescriptor) {
if (KotlinBuiltIns.isNonPrimitiveArray(descriptor)) {
context.trace.report(ARRAY_CLASS_LITERAL_REQUIRES_ARGUMENT.on(expression))
return null
}
type = KotlinTypeImpl.create(
Annotations.EMPTY, descriptor, false, descriptor.typeConstructor.parameters.map(TypeUtils::makeStarProjection)
)
}
}
}
else {
val actualType = possiblyBareType.actualType
if (actualType.isError) return null
if (isAllowedInClassLiteral(actualType)) {
type = actualType
}
}
if (type != null) {
return type
}
context.trace.report(CLASS_LITERAL_LHS_NOT_A_CLASS.on(expression))
return null
}
private fun isAllowedInClassLiteral(type: KotlinType): Boolean =
isClassifierAvailableAtRuntime(type, false)
private fun isClassifierAvailableAtRuntime(type: KotlinType, canBeNullable: Boolean): Boolean {
if (type.isMarkedNullable && !canBeNullable) return false
val typeConstructor = type.constructor
val typeDeclarationDescriptor = typeConstructor.declarationDescriptor
val typeIsArray = KotlinBuiltIns.isArray(type)
when (typeDeclarationDescriptor) {
is ClassDescriptor -> {
val parameters = typeConstructor.parameters
if (parameters.size != type.arguments.size) return false
val typeArgumentsIterator = type.arguments.iterator()
for (parameter in parameters) {
if (!typeIsArray && !parameter.isReified) return false
val typeArgument = typeArgumentsIterator.next() ?: return false
if (typeArgument.isStarProjection) return false
if (!isClassifierAvailableAtRuntime(typeArgument.type, true)) return false
}
return true
}
is TypeParameterDescriptor -> return typeDeclarationDescriptor.isReified
else -> return false
}
}
fun visitCallableReferenceExpression(expression: KtCallableReferenceExpression, c: ExpressionTypingContext): KotlinTypeInfo {
val typeReference = expression.typeReference
val receiverType = typeReference?.let { typeReference ->
typeResolver.resolveType(c.scope, typeReference, c.trace, false)
}
val callableReference = expression.callableReference
if (callableReference.getReferencedName().isEmpty()) {
c.trace.report(UNRESOLVED_REFERENCE.on(callableReference, callableReference))
val errorType = ErrorUtils.createErrorType("Empty callable reference")
return dataFlowAnalyzer.createCheckedTypeInfo(errorType, c, expression)
}
val trace = TemporaryBindingTrace.create(c.trace, "Callable reference type")
val result = getCallableReferenceType(expression, receiverType, c.replaceBindingTrace(trace))
val hasErrors = hasErrors(trace) // Do not inline this local variable (execution order is important)
trace.commit()
if (!hasErrors && result != null) {
checkNoExpressionOnLHS(expression, c)
}
return dataFlowAnalyzer.createCheckedTypeInfo(result, c, expression)
}
private fun hasErrors(trace: TemporaryBindingTrace): Boolean =
trace.bindingContext.diagnostics.all().any { diagnostic -> diagnostic.severity == Severity.ERROR }
private fun checkNoExpressionOnLHS(expression: KtCallableReferenceExpression, c: ExpressionTypingContext) {
val typeReference = expression.typeReference ?: return
var typeElement = typeReference.typeElement as? KtUserType ?: return
while (true) {
if (typeElement.typeArgumentList != null) return
typeElement = typeElement.qualifier ?: break
}
val simpleNameExpression = typeElement.referenceExpression ?: return
val traceAndCache = TemporaryTraceAndCache.create(c, "Resolve expression on LHS of callable reference", simpleNameExpression)
val resolutionResult = callExpressionResolver.resolveSimpleName(c.replaceTraceAndCache(traceAndCache), simpleNameExpression)
val resultingCalls = resolutionResult.resultingCalls.filter { call -> call.status.possibleTransformToSuccess() }
if (resultingCalls.isEmpty()) return
if (resultingCalls.singleOrNull()?.resultingDescriptor is FakeCallableDescriptorForObject) return
throw AssertionError(String.format(
"Expressions on left-hand side of callable reference are not supported yet.\n" +
"Resolution result: %s\n" +
"Original result: %s",
resultingCalls.map { call -> call.resultingDescriptor },
expression.callableReference.getResolvedCall(c.trace.bindingContext)?.resultingDescriptor
))
}
private fun getCallableReferenceType(
expression: KtCallableReferenceExpression,
lhsType: KotlinType?,
context: ExpressionTypingContext
): KotlinType? {
val reference = expression.callableReference
val resolved = BooleanArray(1)
val descriptor = resolveCallableReferenceTarget(expression, lhsType, context, resolved, callResolver)
if (!resolved[0]) {
context.trace.report(UNRESOLVED_REFERENCE.on(reference, reference))
}
if (descriptor == null) return null
if (expression.typeReference == null &&
(descriptor.dispatchReceiverParameter != null || descriptor.extensionReceiverParameter != null)) {
context.trace.report(CALLABLE_REFERENCE_TO_MEMBER_OR_EXTENSION_WITH_EMPTY_LHS.on(reference))
}
val containingDeclaration = descriptor.containingDeclaration
if (DescriptorUtils.isObject(containingDeclaration)) {
context.trace.report(CALLABLE_REFERENCE_TO_OBJECT_MEMBER.on(reference))
}
if (descriptor is ConstructorDescriptor && DescriptorUtils.isAnnotationClass(containingDeclaration)) {
context.trace.report(CALLABLE_REFERENCE_TO_ANNOTATION_CONSTRUCTOR.on(reference))
}
return createReflectionTypeForResolvedCallableReference(expression, lhsType, descriptor, context, reflectionTypes)
}
}
@@ -46,6 +46,7 @@ public class ExpressionTypingComponents {
/*package*/ LocalClassifierAnalyzer localClassifierAnalyzer;
/*package*/ FunctionDescriptorResolver functionDescriptorResolver;
/*package*/ CallExpressionResolver callExpressionResolver;
/*package*/ DoubleColonExpressionResolver doubleColonExpressionResolver;
/*package*/ DescriptorResolver descriptorResolver;
/*package*/ TypeResolver typeResolver;
/*package*/ AnnotationResolver annotationResolver;
@@ -126,6 +127,11 @@ public class ExpressionTypingComponents {
this.callExpressionResolver = callExpressionResolver;
}
@Inject
public void setDoubleColonExpressionResolver(DoubleColonExpressionResolver doubleColonExpressionResolver) {
this.doubleColonExpressionResolver = doubleColonExpressionResolver;
}
@Inject
public void setDescriptorResolver(DescriptorResolver descriptorResolver) {
this.descriptorResolver = descriptorResolver;