Introduce inspection "replace arrayOf with literal" #KT-17164 Fixed

This commit is contained in:
Mikhail Glukhikh
2017-05-11 13:18:13 +03:00
committed by Mikhail Glukhikh
parent fb9d88315a
commit 9990550429
21 changed files with 259 additions and 2 deletions
@@ -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<PrimitiveType, Name> = hashMapOf(
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"),
@@ -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,
@@ -0,0 +1,5 @@
<html>
<body>
This inspection reports 'arrayOf' calls that can be replaced with array literals [...].
</body>
</html>
+8
View File
@@ -2151,6 +2151,14 @@
language="kotlin"
/>
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.ReplaceArrayOfWithLiteralInspection"
displayName="'arrayOf' call can be replaced with array literal [...]"
groupName="Kotlin"
enabledByDefault="true"
level="WARNING"
language="kotlin"
/>
<referenceImporter implementation="org.jetbrains.kotlin.idea.quickfix.KotlinReferenceImporter"/>
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
@@ -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)
}
}
}
@@ -0,0 +1 @@
org.jetbrains.kotlin.idea.inspections.ReplaceArrayOfWithLiteralInspection
@@ -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<String>)
@Some(strings = <caret>arrayOf("alpha", "beta", "omega"))
class My
@@ -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<String>)
@Some(strings = ["alpha", "beta", "omega"])
class My
@@ -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<String> = <caret>arrayOf("default"))
@@ -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<String> = ["default"])
@@ -0,0 +1,2 @@
// PROBLEM: none
class Some(val strings: Array<String> = <caret>arrayOf("default"))
@@ -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<String>)
@Some(strings = <caret>emptyArray())
class My
@@ -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<String>)
@Some(strings = [])
class My
@@ -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 = <caret>intArrayOf(1, 2))
class My
@@ -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
@@ -0,0 +1,5 @@
// PROBLEM: none
fun foo() {
val x = <caret>arrayOf("1")
}
@@ -0,0 +1,9 @@
// PROBLEM: none
// ERROR: Type mismatch: inferred type is Int but Array<String> was expected
annotation class Some(val arg: Array<String>)
fun create(x: Int) = x
@Some(arg = <caret>create(123))
class My
@@ -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<String>)
@Some(<caret>arrayOf("alpha", "beta", "omega"))
class My
@@ -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<String>)
@Some(["alpha", "beta", "omega"])
class My
@@ -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 = *<caret>arrayOf("alpha", "beta", "omega"))
class My
@@ -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
@@ -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);
}
}
}