diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/CollectionLiteralResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/CollectionLiteralResolver.kt new file mode 100644 index 00000000000..03eee005077 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/CollectionLiteralResolver.kt @@ -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 = 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 + } +} \ No newline at end of file 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 a0e90647482..f625b17c859 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java @@ -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 PRIMITIVE_TYPE_TO_ARRAY = new HashMap(); - 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 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); - } }