From 9990550429fe34a2567edd876097911c0aa40922 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Thu, 11 May 2017 13:18:13 +0300 Subject: [PATCH] Introduce inspection "replace arrayOf with literal" #KT-17164 Fixed --- .../resolve/CollectionLiteralResolver.kt | 4 +- .../ReplaceArrayOfWithLiteral.html | 5 + idea/src/META-INF/plugin.xml | 8 ++ .../ReplaceArrayOfWithLiteralInspection.kt | 98 +++++++++++++++++++ .../replaceArrayOfWithLiteral/.inspection | 1 + .../replaceArrayOfWithLiteral/base.kt | 6 ++ .../replaceArrayOfWithLiteral/base.kt.after | 6 ++ .../replaceArrayOfWithLiteral/default.kt | 3 + .../default.kt.after | 3 + .../defaultNotAnnotation.kt | 2 + .../replaceArrayOfWithLiteral/empty.kt | 6 ++ .../replaceArrayOfWithLiteral/empty.kt.after | 6 ++ .../replaceArrayOfWithLiteral/intArray.kt | 6 ++ .../intArray.kt.after | 6 ++ .../replaceArrayOfWithLiteral/noAnnotation.kt | 5 + .../replaceArrayOfWithLiteral/noArray.kt | 9 ++ .../replaceArrayOfWithLiteral/unnamed.kt | 6 ++ .../unnamed.kt.after | 6 ++ .../replaceArrayOfWithLiteral/vararg.kt | 6 ++ .../replaceArrayOfWithLiteral/vararg.kt.after | 6 ++ .../LocalInspectionTestGenerated.java | 63 ++++++++++++ 21 files changed, 259 insertions(+), 2 deletions(-) create mode 100644 idea/resources/inspectionDescriptions/ReplaceArrayOfWithLiteral.html create mode 100644 idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceArrayOfWithLiteralInspection.kt create mode 100644 idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/.inspection create mode 100644 idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/base.kt create mode 100644 idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/base.kt.after create mode 100644 idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/default.kt create mode 100644 idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/default.kt.after create mode 100644 idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/defaultNotAnnotation.kt create mode 100644 idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/empty.kt create mode 100644 idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/empty.kt.after create mode 100644 idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/intArray.kt create mode 100644 idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/intArray.kt.after create mode 100644 idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/noAnnotation.kt create mode 100644 idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/noArray.kt create mode 100644 idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/unnamed.kt create mode 100644 idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/unnamed.kt.after create mode 100644 idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/vararg.kt create mode 100644 idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/vararg.kt.after diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/CollectionLiteralResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/CollectionLiteralResolver.kt index 3968599aafe..ce9e3b9e819 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/CollectionLiteralResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/CollectionLiteralResolver.kt @@ -40,7 +40,7 @@ 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( + 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"), @@ -51,7 +51,7 @@ object CollectionLiteralResolver { PrimitiveType.DOUBLE to Name.identifier("doubleArrayOf") ) - private val ARRAY_OF_FUNCTION = Name.identifier("arrayOf") + val ARRAY_OF_FUNCTION = Name.identifier("arrayOf") fun resolveCollectionLiteral(collectionLiteralExpression: KtCollectionLiteralExpression, context: ExpressionTypingContext, diff --git a/idea/resources/inspectionDescriptions/ReplaceArrayOfWithLiteral.html b/idea/resources/inspectionDescriptions/ReplaceArrayOfWithLiteral.html new file mode 100644 index 00000000000..e870185f37f --- /dev/null +++ b/idea/resources/inspectionDescriptions/ReplaceArrayOfWithLiteral.html @@ -0,0 +1,5 @@ + + +This inspection reports 'arrayOf' calls that can be replaced with array literals [...]. + + diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 51c6f132827..65531bc12c3 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -2151,6 +2151,14 @@ language="kotlin" /> + + diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceArrayOfWithLiteralInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceArrayOfWithLiteralInspection.kt new file mode 100644 index 00000000000..df0f582db41 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceArrayOfWithLiteralInspection.kt @@ -0,0 +1,98 @@ +/* + * 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.idea.inspections + +import com.intellij.codeInspection.LocalQuickFix +import com.intellij.codeInspection.ProblemDescriptor +import com.intellij.codeInspection.ProblemHighlightType +import com.intellij.codeInspection.ProblemsHolder +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiElementVisitor +import org.jetbrains.kotlin.builtins.BuiltInsPackageFragment +import org.jetbrains.kotlin.config.LanguageFeature.ArrayLiteralsInAnnotations +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.project.getLanguageVersionSettings +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.CollectionLiteralResolver +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall + +class ReplaceArrayOfWithLiteralInspection : AbstractKotlinInspection() { + + private val acceptableNames = setOf(CollectionLiteralResolver.ARRAY_OF_FUNCTION) + + CollectionLiteralResolver.PRIMITIVE_TYPE_TO_ARRAY.values.toSet() + + Name.identifier("emptyArray") + + override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { + return object : KtVisitorVoid() { + override fun visitCallExpression(expression: KtCallExpression) { + super.visitCallExpression(expression) + + val project = expression.project + if (!project.getLanguageVersionSettings().supportsFeature(ArrayLiteralsInAnnotations) && + !ApplicationManager.getApplication().isUnitTestMode) return + + val calleeExpression = expression.calleeExpression as? KtNameReferenceExpression ?: return + val resolvedCall = expression.getResolvedCall(expression.analyze()) ?: return + val descriptor = resolvedCall.candidateDescriptor + if (descriptor.containingDeclaration !is BuiltInsPackageFragment) return + if (descriptor.name !in acceptableNames) return + + val parent = expression.parent + when (parent) { + is KtValueArgument -> parent.parent.parent as? KtAnnotationEntry ?: return + is KtParameter -> { + val constructor = parent.parent.parent as? KtPrimaryConstructor ?: return + val containingClass = constructor.getContainingClassOrObject() + if (!containingClass.isAnnotation()) return + } + else -> return + } + + val calleeName = calleeExpression.getReferencedName() + holder.registerProblem( + calleeExpression, + "'$calleeName' call can be replaced with array literal [...]", + ProblemHighlightType.GENERIC_ERROR_OR_WARNING, + ReplaceWithArrayLiteralFix() + ) + } + } + } + + private class ReplaceWithArrayLiteralFix : LocalQuickFix { + override fun getFamilyName() = "Replace with [...]" + + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { + val calleeExpression = descriptor.psiElement as KtExpression + val callExpression = calleeExpression.parent as KtCallExpression + val arguments = callExpression.valueArguments + val arrayLiteral = KtPsiFactory(callExpression).buildExpression { + appendFixedText("[") + for ((index, argument) in arguments.withIndex()) { + appendExpression(argument.getArgumentExpression()) + if (index != arguments.size - 1) { + appendFixedText(", ") + } + } + appendFixedText("]") + } as KtCollectionLiteralExpression + callExpression.replace(arrayLiteral) + } + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/.inspection b/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/.inspection new file mode 100644 index 00000000000..9946d2d0b61 --- /dev/null +++ b/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/.inspection @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.inspections.ReplaceArrayOfWithLiteralInspection diff --git a/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/base.kt b/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/base.kt new file mode 100644 index 00000000000..6c3fc99e910 --- /dev/null +++ b/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/base.kt @@ -0,0 +1,6 @@ +// ERROR: The feature "array literals in annotations" is only available since language version 1.2 + +annotation class Some(val strings: Array) + +@Some(strings = arrayOf("alpha", "beta", "omega")) +class My diff --git a/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/base.kt.after b/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/base.kt.after new file mode 100644 index 00000000000..6e78f5e46b7 --- /dev/null +++ b/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/base.kt.after @@ -0,0 +1,6 @@ +// ERROR: The feature "array literals in annotations" is only available since language version 1.2 + +annotation class Some(val strings: Array) + +@Some(strings = ["alpha", "beta", "omega"]) +class My diff --git a/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/default.kt b/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/default.kt new file mode 100644 index 00000000000..f0027c35812 --- /dev/null +++ b/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/default.kt @@ -0,0 +1,3 @@ +// ERROR: The feature "array literals in annotations" is only available since language version 1.2 + +annotation class Some(val strings: Array = arrayOf("default")) \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/default.kt.after b/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/default.kt.after new file mode 100644 index 00000000000..ea735dd047d --- /dev/null +++ b/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/default.kt.after @@ -0,0 +1,3 @@ +// ERROR: The feature "array literals in annotations" is only available since language version 1.2 + +annotation class Some(val strings: Array = ["default"]) \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/defaultNotAnnotation.kt b/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/defaultNotAnnotation.kt new file mode 100644 index 00000000000..546edc921a1 --- /dev/null +++ b/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/defaultNotAnnotation.kt @@ -0,0 +1,2 @@ +// PROBLEM: none +class Some(val strings: Array = arrayOf("default")) \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/empty.kt b/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/empty.kt new file mode 100644 index 00000000000..26584ad00eb --- /dev/null +++ b/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/empty.kt @@ -0,0 +1,6 @@ +// ERROR: The feature "array literals in annotations" is only available since language version 1.2 + +annotation class Some(val strings: Array) + +@Some(strings = emptyArray()) +class My diff --git a/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/empty.kt.after b/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/empty.kt.after new file mode 100644 index 00000000000..3b13fc1f591 --- /dev/null +++ b/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/empty.kt.after @@ -0,0 +1,6 @@ +// ERROR: The feature "array literals in annotations" is only available since language version 1.2 + +annotation class Some(val strings: Array) + +@Some(strings = []) +class My diff --git a/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/intArray.kt b/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/intArray.kt new file mode 100644 index 00000000000..a76f683df5b --- /dev/null +++ b/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/intArray.kt @@ -0,0 +1,6 @@ +// ERROR: The feature "array literals in annotations" is only available since language version 1.2 + +annotation class Some(val nums: IntArray) + +@Some(nums = intArrayOf(1, 2)) +class My diff --git a/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/intArray.kt.after b/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/intArray.kt.after new file mode 100644 index 00000000000..b8f406a2db2 --- /dev/null +++ b/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/intArray.kt.after @@ -0,0 +1,6 @@ +// ERROR: The feature "array literals in annotations" is only available since language version 1.2 + +annotation class Some(val nums: IntArray) + +@Some(nums = [1, 2]) +class My diff --git a/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/noAnnotation.kt b/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/noAnnotation.kt new file mode 100644 index 00000000000..4e6912901fb --- /dev/null +++ b/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/noAnnotation.kt @@ -0,0 +1,5 @@ +// PROBLEM: none + +fun foo() { + val x = arrayOf("1") +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/noArray.kt b/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/noArray.kt new file mode 100644 index 00000000000..a233ee98822 --- /dev/null +++ b/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/noArray.kt @@ -0,0 +1,9 @@ +// PROBLEM: none +// ERROR: Type mismatch: inferred type is Int but Array was expected + +annotation class Some(val arg: Array) + +fun create(x: Int) = x + +@Some(arg = create(123)) +class My \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/unnamed.kt b/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/unnamed.kt new file mode 100644 index 00000000000..d0b1e512948 --- /dev/null +++ b/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/unnamed.kt @@ -0,0 +1,6 @@ +// ERROR: The feature "array literals in annotations" is only available since language version 1.2 + +annotation class Some(val strings: Array) + +@Some(arrayOf("alpha", "beta", "omega")) +class My diff --git a/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/unnamed.kt.after b/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/unnamed.kt.after new file mode 100644 index 00000000000..f4842540db6 --- /dev/null +++ b/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/unnamed.kt.after @@ -0,0 +1,6 @@ +// ERROR: The feature "array literals in annotations" is only available since language version 1.2 + +annotation class Some(val strings: Array) + +@Some(["alpha", "beta", "omega"]) +class My diff --git a/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/vararg.kt b/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/vararg.kt new file mode 100644 index 00000000000..f3fcebcf628 --- /dev/null +++ b/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/vararg.kt @@ -0,0 +1,6 @@ +// ERROR: The feature "array literals in annotations" is only available since language version 1.2 + +annotation class Some(vararg val strings: String) + +@Some(strings = *arrayOf("alpha", "beta", "omega")) +class My diff --git a/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/vararg.kt.after b/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/vararg.kt.after new file mode 100644 index 00000000000..bd60cdfb2fc --- /dev/null +++ b/idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/vararg.kt.after @@ -0,0 +1,6 @@ +// ERROR: The feature "array literals in annotations" is only available since language version 1.2 + +annotation class Some(vararg val strings: String) + +@Some(strings = *["alpha", "beta", "omega"]) +class My diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java index 068f192eef5..a346f1504c1 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -131,4 +131,67 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { doTest(fileName); } } + + @TestMetadata("idea/testData/inspectionsLocal/replaceArrayOfWithLiteral") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ReplaceArrayOfWithLiteral extends AbstractLocalInspectionTest { + public void testAllFilesPresentInReplaceArrayOfWithLiteral() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/replaceArrayOfWithLiteral"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("base.kt") + public void testBase() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/base.kt"); + doTest(fileName); + } + + @TestMetadata("default.kt") + public void testDefault() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/default.kt"); + doTest(fileName); + } + + @TestMetadata("defaultNotAnnotation.kt") + public void testDefaultNotAnnotation() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/defaultNotAnnotation.kt"); + doTest(fileName); + } + + @TestMetadata("empty.kt") + public void testEmpty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/empty.kt"); + doTest(fileName); + } + + @TestMetadata("intArray.kt") + public void testIntArray() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/intArray.kt"); + doTest(fileName); + } + + @TestMetadata("noAnnotation.kt") + public void testNoAnnotation() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/noAnnotation.kt"); + doTest(fileName); + } + + @TestMetadata("noArray.kt") + public void testNoArray() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/noArray.kt"); + doTest(fileName); + } + + @TestMetadata("unnamed.kt") + public void testUnnamed() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/unnamed.kt"); + doTest(fileName); + } + + @TestMetadata("vararg.kt") + public void testVararg() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/replaceArrayOfWithLiteral/vararg.kt"); + doTest(fileName); + } + } }