Extract collection literal resolve components to separate Kotlin file

This commit is contained in:
Mikhail Zarechenskiy
2017-03-21 19:00:39 +03:00
parent 8466270bdb
commit 222f101d10
2 changed files with 119 additions and 72 deletions
@@ -0,0 +1,113 @@
/*
* Copyright 2010-2017 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.resolve
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.PrimitiveType
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.diagnostics.Errors.UNSUPPORTED
import org.jetbrains.kotlin.diagnostics.Errors.UNSUPPORTED_FEATURE
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtCollectionLiteralExpression
import org.jetbrains.kotlin.resolve.BindingContext.COLLECTION_LITERAL_CALL
import org.jetbrains.kotlin.resolve.calls.CallResolver
import org.jetbrains.kotlin.resolve.calls.util.CallMaker
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE
import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext
import org.jetbrains.kotlin.types.expressions.KotlinTypeInfo
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.createTypeInfo
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.noTypeInfo
object CollectionLiteralResolver {
private val PRIMITIVE_TYPE_TO_ARRAY: Map<PrimitiveType, Name> = hashMapOf(
PrimitiveType.BOOLEAN to Name.identifier("booleanArrayOf"),
PrimitiveType.CHAR to Name.identifier("charArrayOf"),
PrimitiveType.INT to Name.identifier("intArrayOf"),
PrimitiveType.BYTE to Name.identifier("byteArrayOf"),
PrimitiveType.SHORT to Name.identifier("shortArrayOf"),
PrimitiveType.FLOAT to Name.identifier("floatArrayOf"),
PrimitiveType.LONG to Name.identifier("longArrayOf"),
PrimitiveType.DOUBLE to Name.identifier("doubleArrayOf")
)
private val ARRAY_OF_FUNCTION = Name.identifier("arrayOf")
fun resolveCollectionLiteral(collectionLiteralExpression: KtCollectionLiteralExpression,
context: ExpressionTypingContext,
callResolver: CallResolver,
languageVersionSettings: LanguageVersionSettings
): KotlinTypeInfo {
if (!isInsideAnnotationEntryOrClass(collectionLiteralExpression)) {
context.trace.report(UNSUPPORTED.on(collectionLiteralExpression, "Collection literals outside of annotations"))
}
checkSupportsArrayLiterals(collectionLiteralExpression, context, languageVersionSettings)
return resolveCollectionLiteralSpecialMethod(collectionLiteralExpression, context, callResolver)
}
private fun resolveCollectionLiteralSpecialMethod(
collectionLiteralExpression: KtCollectionLiteralExpression,
context: ExpressionTypingContext,
callResolver: CallResolver
): KotlinTypeInfo {
val collectionLiteralCallName = getArrayFunctionCallName(context.expectedType)
val call = CallMaker.makeCallForCollectionLiteral(collectionLiteralExpression)
val resolutionResults = callResolver.resolveCallWithGivenName(context, call, collectionLiteralExpression, collectionLiteralCallName)
// TODO: check that resolved function is from package `kotlin`, otherwise report an error
if (!resolutionResults.isSingleResult) {
return noTypeInfo(context)
}
context.trace.record(COLLECTION_LITERAL_CALL, collectionLiteralExpression, resolutionResults.resultingCall)
return createTypeInfo(resolutionResults.resultingDescriptor.returnType, context)
}
fun checkSupportsArrayLiterals(expression: KtCollectionLiteralExpression,
context: ExpressionTypingContext,
languageVersionSettings: LanguageVersionSettings
) {
if (isInsideAnnotationEntryOrClass(expression) &&
!languageVersionSettings.supportsFeature(LanguageFeature.ArrayLiteralsInAnnotations)) {
context.trace.report(UNSUPPORTED_FEATURE.on(expression, LanguageFeature.ArrayLiteralsInAnnotations to languageVersionSettings))
}
}
private fun isInsideAnnotationEntryOrClass(expression: KtCollectionLiteralExpression): Boolean {
val parent = PsiTreeUtil.getParentOfType(expression, KtAnnotationEntry::class.java, KtClass::class.java)
return parent is KtAnnotationEntry || (parent is KtClass && parent.isAnnotation())
}
private fun getArrayFunctionCallName(expectedType: KotlinType): Name {
if (NO_EXPECTED_TYPE === expectedType || !KotlinBuiltIns.isPrimitiveArray(expectedType)) {
return ARRAY_OF_FUNCTION
}
val descriptor = expectedType.constructor.declarationDescriptor ?: return ARRAY_OF_FUNCTION
val arrayFqName = DescriptorUtils.getFqName(descriptor)
val primitiveType = KotlinBuiltIns.getPrimitiveTypeByArrayClassFqName(arrayFqName)
return PRIMITIVE_TYPE_TO_ARRAY[primitiveType] ?: ARRAY_OF_FUNCTION
}
}
@@ -29,7 +29,6 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.KtNodeTypes;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.builtins.PrimitiveType;
import org.jetbrains.kotlin.config.LanguageFeature;
import org.jetbrains.kotlin.config.LanguageVersionSettings;
import org.jetbrains.kotlin.descriptors.*;
@@ -37,7 +36,6 @@ import org.jetbrains.kotlin.diagnostics.Diagnostic;
import org.jetbrains.kotlin.diagnostics.Errors;
import org.jetbrains.kotlin.lexer.KtKeywordToken;
import org.jetbrains.kotlin.lexer.KtTokens;
import org.jetbrains.kotlin.name.FqNameUnsafe;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.psi.psiUtil.KtPsiUtilKt;
@@ -77,7 +75,10 @@ import org.jetbrains.kotlin.types.expressions.unqualifiedSuper.UnqualifiedSuperK
import org.jetbrains.kotlin.util.OperatorNameConventions;
import org.jetbrains.kotlin.util.slicedMap.WritableSlice;
import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -97,20 +98,6 @@ import static org.jetbrains.kotlin.types.expressions.TypeReconstructionUtil.reco
@SuppressWarnings("SuspiciousMethodCalls")
public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
private static final Map<PrimitiveType, Name> PRIMITIVE_TYPE_TO_ARRAY = new HashMap<PrimitiveType, Name>();
static {
PRIMITIVE_TYPE_TO_ARRAY.put(PrimitiveType.BOOLEAN, Name.identifier("booleanArrayOf"));
PRIMITIVE_TYPE_TO_ARRAY.put(PrimitiveType.CHAR, Name.identifier("charArrayOf"));
PRIMITIVE_TYPE_TO_ARRAY.put(PrimitiveType.INT, Name.identifier("intArrayOf"));
PRIMITIVE_TYPE_TO_ARRAY.put(PrimitiveType.BYTE, Name.identifier("byteArrayOf"));
PRIMITIVE_TYPE_TO_ARRAY.put(PrimitiveType.SHORT, Name.identifier("shortArrayOf"));
PRIMITIVE_TYPE_TO_ARRAY.put(PrimitiveType.FLOAT, Name.identifier("floatArrayOf"));
PRIMITIVE_TYPE_TO_ARRAY.put(PrimitiveType.LONG, Name.identifier("longArrayOf"));
PRIMITIVE_TYPE_TO_ARRAY.put(PrimitiveType.DOUBLE, Name.identifier("doubleArrayOf"));
}
private static final Name ARRAY_OF_FUNCTION = Name.identifier("arrayOf");
private static final TokenSet BARE_TYPES_ALLOWED = TokenSet.create(AS_KEYWORD, AS_SAFE);
protected BasicExpressionTypingVisitor(@NotNull ExpressionTypingInternals facade) {
@@ -1517,12 +1504,8 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
public KotlinTypeInfo visitCollectionLiteralExpression(
@NotNull KtCollectionLiteralExpression expression, ExpressionTypingContext context
) {
if (!isInsideAnnotationEntryOrClass(expression)) {
context.trace.report(UNSUPPORTED.on(expression, "Collection literals outside of annotations"));
}
checkSupportsArrayLiterals(expression, context);
return resolveCollectionLiteralSpecialMethod(expression, context);
return CollectionLiteralResolver.INSTANCE.resolveCollectionLiteral(
expression, context, components.callResolver, components.languageVersionSettings);
}
@Override
@@ -1785,53 +1768,4 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
functionResults.getResultingCall());
return resultTypeInfo.replaceType(functionResults.getResultingDescriptor().getReturnType());
}
private void checkSupportsArrayLiterals(KtCollectionLiteralExpression expression, ExpressionTypingContext context) {
if (isInsideAnnotationEntryOrClass(expression) &&
!components.languageVersionSettings.supportsFeature(LanguageFeature.ArrayLiteralsInAnnotations)) {
context.trace.report(UNSUPPORTED_FEATURE.on(
expression, TuplesKt.to(LanguageFeature.ArrayLiteralsInAnnotations, components.languageVersionSettings)));
}
}
private static boolean isInsideAnnotationEntryOrClass(KtCollectionLiteralExpression expression) {
//noinspection unchecked
PsiElement parent = PsiTreeUtil.getParentOfType(expression, KtAnnotationEntry.class, KtClass.class);
return parent instanceof KtAnnotationEntry || (parent instanceof KtClass && ((KtClass) parent).isAnnotation());
}
private KotlinTypeInfo resolveCollectionLiteralSpecialMethod(
@NotNull KtCollectionLiteralExpression collectionLiteralExpression,
@NotNull ExpressionTypingContext context
) {
Name collectionLiteralCallName;
KotlinType expectedType = context.expectedType;
if (NO_EXPECTED_TYPE == expectedType || !KotlinBuiltIns.isPrimitiveArray(expectedType)) {
collectionLiteralCallName = ARRAY_OF_FUNCTION;
}
else {
ClassifierDescriptor descriptor = expectedType.getConstructor().getDeclarationDescriptor();
if (descriptor != null) {
FqNameUnsafe arrayFqName = DescriptorUtils.getFqName(descriptor);
PrimitiveType primitiveType = KotlinBuiltIns.getPrimitiveTypeByArrayClassFqName(arrayFqName);
collectionLiteralCallName = PRIMITIVE_TYPE_TO_ARRAY.get(primitiveType);
}
else {
collectionLiteralCallName = ARRAY_OF_FUNCTION;
}
}
Call call = CallMaker.makeCallForCollectionLiteral(collectionLiteralExpression);
OverloadResolutionResults<FunctionDescriptor> resolutionResults = components.callResolver.resolveCallWithGivenName(
context, call, collectionLiteralExpression, collectionLiteralCallName);
// TODO: check that resolved function is from package `kotlin`, otherwise report an error
if (!resolutionResults.isSingleResult()) {
return TypeInfoFactoryKt.noTypeInfo(context);
}
context.trace.record(COLLECTION_LITERAL_CALL, collectionLiteralExpression, resolutionResults.getResultingCall());
return TypeInfoFactoryKt.createTypeInfo(resolutionResults.getResultingDescriptor().getReturnType(), context);
}
}