Resolve collection literal expression as special array-like function

This commit is contained in:
Mikhail Zarechenskiy
2017-03-20 03:25:37 +03:00
parent d3fd96ceed
commit c85f6e7d0e
14 changed files with 175 additions and 12 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
* 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.
@@ -17,5 +17,14 @@
package org.jetbrains.kotlin.psi
import com.intellij.lang.ASTNode
import com.intellij.psi.util.PsiTreeUtil
class KtCollectionLiteralExpression(node: ASTNode) : KtExpressionImpl(node)
class KtCollectionLiteralExpression(node: ASTNode) : KtExpressionImpl(node), KtReferenceExpression {
override fun <R, D> accept(visitor: KtVisitor<R, D>, data: D): R {
return visitor.visitCollectionLiteralExpression(this, data)
}
fun getInnerExpressions(): List<KtExpression> {
return PsiTreeUtil.getChildrenOfTypeAsList(this, KtExpression::class.java)
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
* 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.
@@ -242,6 +242,10 @@ public class KtVisitor<R, D> extends PsiElementVisitor {
return visitExpression(expression, data);
}
public R visitCollectionLiteralExpression(@NotNull KtCollectionLiteralExpression expression, D data) {
return visitExpression(expression, data);
}
public R visitTryExpression(@NotNull KtTryExpression expression, D data) {
return visitExpression(expression, data);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
* 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.
@@ -229,6 +229,10 @@ public class KtVisitorVoid extends KtVisitor<Void, Void> {
super.visitWhenExpression(expression, null);
}
public void visitCollectionLiteralExpression(@NotNull KtCollectionLiteralExpression expression) {
super.visitCollectionLiteralExpression(expression, null);
}
public void visitTryExpression(@NotNull KtTryExpression expression) {
super.visitTryExpression(expression, null);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
* 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.
@@ -370,6 +370,10 @@ public class KtVisitorVoidWithParameter<P> extends KtVisitor<Void, P> {
super.visitWhenEntry(jetWhenEntry, data);
}
public void visitCollectionLiteralExpressionVoid(@NotNull KtCollectionLiteralExpression expression, P data) {
super.visitCollectionLiteralExpression(expression, data);
}
public void visitIsExpressionVoid(@NotNull KtIsExpression expression, P data) {
super.visitIsExpression(expression, data);
}
@@ -705,6 +709,12 @@ public class KtVisitorVoidWithParameter<P> extends KtVisitor<Void, P> {
return null;
}
@Override
public Void visitCollectionLiteralExpression(@NotNull KtCollectionLiteralExpression expression, P data) {
visitCollectionLiteralExpressionVoid(expression, data);
return null;
}
@Override
public final Void visitTryExpression(@NotNull KtTryExpression expression, P data) {
visitTryExpressionVoid(expression, data);
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
* 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.
@@ -149,6 +149,8 @@ public interface BindingContext {
WritableSlice<KtExpression, ResolvedCall<FunctionDescriptor>> INDEXED_LVALUE_GET = Slices.createSimpleSlice();
WritableSlice<KtExpression, ResolvedCall<FunctionDescriptor>> INDEXED_LVALUE_SET = Slices.createSimpleSlice();
WritableSlice<KtCollectionLiteralExpression, ResolvedCall<FunctionDescriptor>> COLLECTION_LITERAL_CALL = Slices.createSimpleSlice();
WritableSlice<KtExpression, ExplicitSmartCasts> SMARTCAST = new BasicWritableSlice<KtExpression, ExplicitSmartCasts>(DO_NOTHING);
WritableSlice<KtExpression, Boolean> SMARTCAST_NULL = Slices.createSimpleSlice();
WritableSlice<KtExpression, ImplicitSmartCasts> IMPLICIT_RECEIVER_SMARTCAST = new BasicWritableSlice<KtExpression, ImplicitSmartCasts>(DO_NOTHING);
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
* 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
* 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.
@@ -250,6 +250,16 @@ public class CallMaker {
return makeCallWithExpressions(arrayAccessExpression, arrayAsReceiver, null, arrayAccessExpression, arrayAccessExpression.getIndexExpressions(), callType);
}
public static Call makeCallForCollectionLiteral(@NotNull KtCollectionLiteralExpression collectionLiteralExpression) {
return makeCallWithExpressions(
collectionLiteralExpression,
null,
null,
collectionLiteralExpression,
collectionLiteralExpression.getInnerExpressions(),
CallType.DEFAULT);
}
@NotNull
public static ValueArgument makeValueArgument(@NotNull KtExpression expression) {
return makeValueArgument(expression, expression);
@@ -29,6 +29,7 @@ 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.*;
@@ -36,6 +37,7 @@ 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;
@@ -75,10 +77,7 @@ import org.jetbrains.kotlin.types.expressions.unqualifiedSuper.UnqualifiedSuperK
import org.jetbrains.kotlin.util.OperatorNameConventions;
import org.jetbrains.kotlin.util.slicedMap.WritableSlice;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -98,6 +97,19 @@ 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);
@@ -1501,6 +1513,13 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
return components.dataFlowAnalyzer.checkType(resolveArrayAccessGetMethod(expression, context), expression, context);
}
@Override
public KotlinTypeInfo visitCollectionLiteralExpression(
@NotNull KtCollectionLiteralExpression expression, ExpressionTypingContext context
) {
return resolveCollectionLiteralSpecialMethod(expression, context);
}
@Override
public KotlinTypeInfo visitClass(@NotNull KtClass klass, ExpressionTypingContext context) {
// analyze class in illegal position and write descriptor to trace but do not write to any scope
@@ -1761,4 +1780,39 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
functionResults.getResultingCall());
return resultTypeInfo.replaceType(functionResults.getResultingDescriptor().getReturnType());
}
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> functionResults = components.callResolver.resolveCallWithGivenName(
context, call, collectionLiteralExpression, collectionLiteralCallName);
if (!functionResults.isSuccess() || !functionResults.isSingleResult()) {
// TODO: report an error
return TypeInfoFactoryKt.noTypeInfo(context);
}
context.trace.record(COLLECTION_LITERAL_CALL, collectionLiteralExpression, functionResults.getResultingCall());
return TypeInfoFactoryKt.createTypeInfo(functionResults.getResultingDescriptor().getReturnType(), context);
}
}
@@ -0,0 +1,23 @@
// !CHECK_TYPE
// !DIAGNOSTICS: -UNUSED_VARIABLE
fun test() {
val a = <!TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>[]<!>
val b: Array<Int> = []
val c = [1, 2]
val d: Array<Int> = [1, 2]
val e: Array<String> = <!TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH!>[1]<!>
val f: IntArray = [1, 2]
val g = [f]
}
fun check() {
[1, 2] checkType { _<Array<Int>>() }
[""] checkType { _<Array<String>>() }
val f: IntArray = [1]
[f] checkType { _<Array<IntArray>>() }
[1, ""] checkType { _<Array<Any>>() }
}
@@ -0,0 +1,4 @@
package
public fun check(): kotlin.Unit
public fun test(): kotlin.Unit
@@ -0,0 +1,18 @@
// !DIAGNOSTICS: -UNUSED_VARIABLE
fun basicTypes() {
val a: IntArray = [1]
val b: ByteArray = [1]
val c: BooleanArray = [true, false]
val d: CharArray = ['a']
val e: ShortArray = [1]
val f: FloatArray = [1.0f]
val g: LongArray = [1]
val h: DoubleArray = [1.0]
}
fun basicTypesWithErrors() {
val a: IntArray = [<!CONSTANT_EXPECTED_TYPE_MISMATCH!>1.0<!>]
val b: ShortArray = [<!CONSTANT_EXPECTED_TYPE_MISMATCH!>1.0<!>]
val c: CharArray = [<!TYPE_MISMATCH!>"a"<!>]
}
@@ -0,0 +1,4 @@
package
public fun basicTypes(): kotlin.Unit
public fun basicTypesWithErrors(): kotlin.Unit
@@ -3332,6 +3332,27 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
}
}
@TestMetadata("compiler/testData/diagnostics/tests/collectionLiterals")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class CollectionLiterals extends AbstractDiagnosticsTest {
public void testAllFilesPresentInCollectionLiterals() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/collectionLiterals"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("basicCollectionLiterals.kt")
public void testBasicCollectionLiterals() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/collectionLiterals/basicCollectionLiterals.kt");
doTest(fileName);
}
@TestMetadata("collectionLiteralsAsPrimitiveArrays.kt")
public void testCollectionLiteralsAsPrimitiveArrays() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/collectionLiterals/collectionLiteralsAsPrimitiveArrays.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/diagnostics/tests/constructorConsistency")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)