From c85f6e7d0e019006243cc4de6c3c1a8ef196e6cf Mon Sep 17 00:00:00 2001 From: Mikhail Zarechenskiy Date: Mon, 20 Mar 2017 03:25:37 +0300 Subject: [PATCH] Resolve collection literal expression as special array-like function --- .../src/org/jetbrains/kotlin/psi/Call.java | 2 +- .../psi/KtCollectionLiteralExpression.kt | 11 +++- .../org/jetbrains/kotlin/psi/KtVisitor.java | 6 +- .../jetbrains/kotlin/psi/KtVisitorVoid.java | 6 +- .../psi/KtVisitorVoidWithParameter.java | 12 +++- .../kotlin/resolve/BindingContext.java | 4 +- .../kotlin/resolve/calls/CallResolver.java | 2 +- .../kotlin/resolve/calls/util/CallMaker.java | 12 +++- .../BasicExpressionTypingVisitor.java | 62 +++++++++++++++++-- .../basicCollectionLiterals.kt | 23 +++++++ .../basicCollectionLiterals.txt | 4 ++ .../collectionLiteralsAsPrimitiveArrays.kt | 18 ++++++ .../collectionLiteralsAsPrimitiveArrays.txt | 4 ++ .../checkers/DiagnosticsTestGenerated.java | 21 +++++++ 14 files changed, 175 insertions(+), 12 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/collectionLiterals/basicCollectionLiterals.kt create mode 100644 compiler/testData/diagnostics/tests/collectionLiterals/basicCollectionLiterals.txt create mode 100644 compiler/testData/diagnostics/tests/collectionLiterals/collectionLiteralsAsPrimitiveArrays.kt create mode 100644 compiler/testData/diagnostics/tests/collectionLiterals/collectionLiteralsAsPrimitiveArrays.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/Call.java b/compiler/frontend/src/org/jetbrains/kotlin/psi/Call.java index 73c12630f49..e38843bd970 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/Call.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/Call.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. diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtCollectionLiteralExpression.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtCollectionLiteralExpression.kt index 0f53b8f135e..c783ab31f3c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtCollectionLiteralExpression.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtCollectionLiteralExpression.kt @@ -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) \ No newline at end of file +class KtCollectionLiteralExpression(node: ASTNode) : KtExpressionImpl(node), KtReferenceExpression { + override fun accept(visitor: KtVisitor, data: D): R { + return visitor.visitCollectionLiteralExpression(this, data) + } + + fun getInnerExpressions(): List { + return PsiTreeUtil.getChildrenOfTypeAsList(this, KtExpression::class.java) + } +} \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtVisitor.java index 9c3217da9b5..484676f2d65 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtVisitor.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 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); } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtVisitorVoid.java b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtVisitorVoid.java index 3883d8c12bf..8a86efcdd03 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtVisitorVoid.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtVisitorVoid.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. @@ -229,6 +229,10 @@ public class KtVisitorVoid extends KtVisitor { super.visitWhenExpression(expression, null); } + public void visitCollectionLiteralExpression(@NotNull KtCollectionLiteralExpression expression) { + super.visitCollectionLiteralExpression(expression, null); + } + public void visitTryExpression(@NotNull KtTryExpression expression) { super.visitTryExpression(expression, null); } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtVisitorVoidWithParameter.java b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtVisitorVoidWithParameter.java index d281dc393d9..a1999c0c649 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtVisitorVoidWithParameter.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtVisitorVoidWithParameter.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. @@ -370,6 +370,10 @@ public class KtVisitorVoidWithParameter

extends KtVisitor { 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

extends KtVisitor { 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); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java index 76cfa3b6c43..fd775952da8 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java @@ -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> INDEXED_LVALUE_GET = Slices.createSimpleSlice(); WritableSlice> INDEXED_LVALUE_SET = Slices.createSimpleSlice(); + WritableSlice> COLLECTION_LITERAL_CALL = Slices.createSimpleSlice(); + WritableSlice SMARTCAST = new BasicWritableSlice(DO_NOTHING); WritableSlice SMARTCAST_NULL = Slices.createSimpleSlice(); WritableSlice IMPLICIT_RECEIVER_SMARTCAST = new BasicWritableSlice(DO_NOTHING); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolver.java index c9009b4f312..c6f4a6460e0 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolver.java @@ -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. diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/util/CallMaker.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/util/CallMaker.java index f1c8fdb90ed..e22f0ecc5d6 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/util/CallMaker.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/util/CallMaker.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. @@ -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); 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 6ebf9659da7..7ed438cd875 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java @@ -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 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); @@ -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 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); + } } diff --git a/compiler/testData/diagnostics/tests/collectionLiterals/basicCollectionLiterals.kt b/compiler/testData/diagnostics/tests/collectionLiterals/basicCollectionLiterals.kt new file mode 100644 index 00000000000..977bc8ab79a --- /dev/null +++ b/compiler/testData/diagnostics/tests/collectionLiterals/basicCollectionLiterals.kt @@ -0,0 +1,23 @@ +// !CHECK_TYPE +// !DIAGNOSTICS: -UNUSED_VARIABLE + +fun test() { + val a = [] + val b: Array = [] + val c = [1, 2] + val d: Array = [1, 2] + val e: Array = [1] + + val f: IntArray = [1, 2] + val g = [f] +} + +fun check() { + [1, 2] checkType { _>() } + [""] checkType { _>() } + + val f: IntArray = [1] + [f] checkType { _>() } + + [1, ""] checkType { _>() } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/collectionLiterals/basicCollectionLiterals.txt b/compiler/testData/diagnostics/tests/collectionLiterals/basicCollectionLiterals.txt new file mode 100644 index 00000000000..ef67c60ee2f --- /dev/null +++ b/compiler/testData/diagnostics/tests/collectionLiterals/basicCollectionLiterals.txt @@ -0,0 +1,4 @@ +package + +public fun check(): kotlin.Unit +public fun test(): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/collectionLiterals/collectionLiteralsAsPrimitiveArrays.kt b/compiler/testData/diagnostics/tests/collectionLiterals/collectionLiteralsAsPrimitiveArrays.kt new file mode 100644 index 00000000000..08ae16f6b19 --- /dev/null +++ b/compiler/testData/diagnostics/tests/collectionLiterals/collectionLiteralsAsPrimitiveArrays.kt @@ -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 = [1.0] + val b: ShortArray = [1.0] + val c: CharArray = ["a"] +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/collectionLiterals/collectionLiteralsAsPrimitiveArrays.txt b/compiler/testData/diagnostics/tests/collectionLiterals/collectionLiteralsAsPrimitiveArrays.txt new file mode 100644 index 00000000000..f3624ca5753 --- /dev/null +++ b/compiler/testData/diagnostics/tests/collectionLiterals/collectionLiteralsAsPrimitiveArrays.txt @@ -0,0 +1,4 @@ +package + +public fun basicTypes(): kotlin.Unit +public fun basicTypesWithErrors(): kotlin.Unit diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 7774442c3b4..2f2f0e50151 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -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)