Array can be replaced with enum values inspection added (IDEA-195323)

This commit is contained in:
Olga Klisho
2018-12-17 14:59:38 +01:00
committed by Anna.Kozlova
parent 28098f0d76
commit e498a28646
20 changed files with 398 additions and 0 deletions
@@ -2158,6 +2158,10 @@ junit5.converter.fix.name=Migrate to JUnit 5
call.to.suspicious.string.method.display.name=Call to suspicious String method
call.to.suspicious.string.method.problem.descriptor=<code>String.#ref()</code> called in internationalized context #loc
array.can.be.replaced.with.enum.values = Array can be replaced with enum values
array.can.be.replaced.with.enum.values.quickfix = Replace array with {0}.values()
array.can.be.replaced.with.enum.values.family.quickfix = Replace array with EnumType.values()
string.concatenation.replace.fix=Replace with StringBuilder
string.concatenation.replace.fix.name=Convert variable ''{0}'' from String to {1}
string.concatenation.introduce.fix=Introduce StringBuilder
@@ -2536,6 +2536,10 @@
key="inspection.simplifiable.if.statement.display.name" groupBundle="messages.InspectionsBundle"
groupKey="group.names.code.style.issues" enabledByDefault="true" level="INFORMATION"
implementationClass="com.siyeh.ig.style.SimplifiableIfStatementInspection"/>
<localInspection groupPath="Java" language="JAVA" shortName="ArrayCanBeReplacedWithEnumValues" bundle="com.siyeh.InspectionGadgetsBundle"
key="array.can.be.replaced.with.enum.values" groupBundle="messages.InspectionsBundle"
groupKey="group.names.code.style.issues" enabledByDefault="true" level="INFORMATION"
implementationClass="com.siyeh.ig.style.ArrayCanBeReplacedWithEnumValuesInspection"/>
<!--group.names.threading.issues-->
<localInspection groupPath="Java" language="JAVA" suppressId="AccessToNonThreadSafeStaticField" shortName="AccessToNonThreadSafeStaticFieldFromInstance"
@@ -0,0 +1,134 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.siyeh.ig.style;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiUtil;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.BaseInspection;
import com.siyeh.ig.BaseInspectionVisitor;
import com.siyeh.ig.InspectionGadgetsFix;
import com.siyeh.ig.PsiReplacementUtil;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author okli
*/
public class ArrayCanBeReplacedWithEnumValuesInspection extends BaseInspection {
@Nls
@NotNull
@Override
public String getDisplayName() {
return InspectionGadgetsBundle.message("array.can.be.replaced.with.enum.values");
}
@NotNull
@Override
protected String buildErrorString(Object... infos) {
return getDisplayName();
}
@Override
public BaseInspectionVisitor buildVisitor() {
return new ArrayCreationExpressionVisitor();
}
@Nullable
@Override
protected InspectionGadgetsFix buildFix(Object... infos) {
if (infos.length == 1 && infos[0] instanceof String) {
return new ArrayToEnumValueFix((String)infos[0]);
}
return null;
}
private static class ArrayToEnumValueFix extends InspectionGadgetsFix {
private final String myEnumName;
private ArrayToEnumValueFix(String enumName) {
myEnumName = enumName;
}
@Nls
@NotNull
@Override
public String getName() {
return InspectionGadgetsBundle.message("array.can.be.replaced.with.enum.values.quickfix", StringUtil.getShortName(myEnumName));
}
@Nls(capitalization = Nls.Capitalization.Sentence)
@NotNull
@Override
public String getFamilyName() {
return InspectionGadgetsBundle.message("array.can.be.replaced.with.enum.values.family.quickfix");
}
@Override
protected void doFix(Project project, ProblemDescriptor descriptor) {
if (myEnumName == null) {
return;
}
final PsiElement element = descriptor.getPsiElement();
if (element instanceof PsiNewExpression || element instanceof PsiArrayInitializerExpression) {
PsiReplacementUtil.replaceExpression((PsiExpression)element, myEnumName + ".values()");
}
}
}
private static class ArrayCreationExpressionVisitor extends BaseInspectionVisitor {
@Override
public void visitArrayInitializerExpression(PsiArrayInitializerExpression expression) {
super.visitArrayInitializerExpression(expression);
final PsiType type = expression.getType();
if (!(type instanceof PsiArrayType)) {
return;
}
final PsiType initExprType = ((PsiArrayType)type).getComponentType();
final PsiClass initClass = PsiUtil.resolveClassInClassTypeOnly(initExprType);
if (initClass == null || !initClass.isEnum()) {
return;
}
final List<String> enumValues = Stream.of(initClass.getFields())
.filter(ev -> ev instanceof PsiEnumConstant)
.map(ev -> ev.getName())
.collect(Collectors.toList());
final PsiExpression[] initializers = expression.getInitializers();
if (enumValues.size() != initializers.length) {
return;
}
for (int i = 0; i < initializers.length; i++) {
if (!(initializers[i] instanceof PsiReferenceExpression &&
enumValues.get(i).equals(((PsiReferenceExpression)initializers[i]).getReferenceName()) &&
initExprType.equals(initializers[i].getType()))) {
return;
}
}
final PsiElement parent = expression.getParent();
final String enumName = initClass.getQualifiedName();
if (parent instanceof PsiNewExpression) {
registerError(parent, enumName);
}
else {
registerError(expression, enumName);
}
}
}
}
@@ -0,0 +1,6 @@
<!-- Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -->
<html>
<body>
Reports arrays of enum constants which may be replaced with <code>EnumType.values()</code>.
</body>
</html>
@@ -0,0 +1,12 @@
class ClassWithEnum {
public static void foo() {
testMethod(TestEnum.values());
}
private static void testMethod(TestEnum[] values) { }
public enum TestEnum {
ONE, TWO, THREE
}
}
@@ -0,0 +1,12 @@
class ClassWithEnum {
public static void foo() {
testMethod(new TestEnum[]<caret>{TestEnum.ONE, TestEnum.TWO, TestEnum.THREE});
}
private static void testMethod(TestEnum[] values) { }
public enum TestEnum {
ONE, TWO, THREE
}
}
@@ -0,0 +1,12 @@
class EnumRevOrder {
public static void foo() {
testMethod(new TestEnum[]<caret>{TestEnum.THREE, TestEnum.TWO, TestEnum.ONE});
}
private static void testMethod(TestEnum[] values) { }
public enum TestEnum {
ONE, TWO, THREE;
}
}
@@ -0,0 +1,18 @@
class EnumWithField {
public static void foo() {
testMethod(TestEnum.values());
}
private static void testMethod(TestEnum[] values) { }
public enum TestEnum {
ONE(1), TWO(2), THREE(3);
private final int levelCode;
TestEnum(int levelCode) {
this.levelCode = levelCode;
}
}
}
@@ -0,0 +1,18 @@
class EnumWithField {
public static void foo() {
testMethod(new TestEnum[]{TestE<caret>num.ONE, TestEnum.TWO, TestEnum.THREE});
}
private static void testMethod(TestEnum[] values) { }
public enum TestEnum {
ONE(1), TWO(2), THREE(3);
private final int levelCode;
TestEnum(int levelCode) {
this.levelCode = levelCode;
}
}
}
@@ -0,0 +1,11 @@
class ErrorInMultiDArray {
public static void foo() {
testMethod(new TestEnum[]{{TestE<caret>num.ONE, TestEnum.TWO, TestEnum.THREE}});
}
private static void testMethod(TestEnum[] values) { }
public enum TestEnum {
ONE, TWO, THREE;
}
@@ -0,0 +1,10 @@
class Foo {
enum En {A;}
static En foo() {
return En.A;
}
{
En[] array = En.values();
}
}
@@ -0,0 +1,10 @@
class Foo {
enum En {A;}
static En foo() {
return En.A;
}
{
En[] array = new En[] {fo<caret>o()};
}
}
@@ -0,0 +1,15 @@
class InnerEnum {
public static void foo() {
testMethod(TestEnum.Inner.values());
}
private static void testMethod(TestEnum.Inner[] values) {
}
public enum TestEnum {
ONE, TWO, THREE;
enum Inner {FIVE, SIX, SEVEN}
}
}
@@ -0,0 +1,15 @@
class InnerEnum {
public static void foo() {
testMethod(new TestEnum.Inner[]{TestEnum.Inn<caret>er.FIVE, TestEnum.Inner.SIX, TestEnum.Inner.SEVEN});
}
private static void testMethod(TestEnum.Inner[] values) {
}
public enum TestEnum {
ONE, TWO, THREE;
enum Inner {FIVE, SIX, SEVEN}
}
}
@@ -0,0 +1,14 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
class MultiDArrayNoError {
public static void foo() {
testMethod(new TestEnum[][]{{Test<caret>Enum.ONE, TestEnum.THREE, TestEnum.TWO}});
}
private static void testMethod(TestEnum[][] values) {
}
public enum TestEnum {
ONE, TWO, THREE;
}
}
@@ -0,0 +1,12 @@
class NotEnumInit {
public static void foo() {
testMethod(new TestEnum[]<caret>{"", TestEnum.ONE});
}
private static void testMethod(TestEnum[] values) { }
public enum TestEnum {
ONE, TWO, THREE
}
}
@@ -0,0 +1,12 @@
class NotEnumMulti {
public static void foo() {
testMethod(new TestEnum[][]<caret>{{TestEnum.ONE}});
}
private static void testMethod(TestEnum[] values) { }
public enum TestEnum {
ONE, TWO, THREE
}
}
@@ -0,0 +1,8 @@
class OuterEnumUse {
public static void foo() {
testMethod(OuterEnum.TestEnum.values());
}
private static void testMethod(OuterEnum.TestEnum[] values) {
}
}
@@ -0,0 +1,8 @@
class OuterEnumUse {
public static void foo() {
testMethod(new OuterEnum.TestEnum[]{OuterEnum.TestEnum.<caret>A, OuterEnum.TestEnum.B, OuterEnum.TestEnum.C});
}
private static void testMethod(OuterEnum.TestEnum[] values) {
}
}
@@ -0,0 +1,63 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.siyeh.ig.fixes.style;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.IGQuickFixesTestCase;
import com.siyeh.ig.style.ArrayCanBeReplacedWithEnumValuesInspection;
public class ArrayCanBeReplacedWithEnumValuesFixTest extends IGQuickFixesTestCase {
public void testClassWithEnum() { doTest("TestEnum"); }
public void testEnumWithField() {doTest("TestEnum");}
public void testNotEnumInit() { assertQuickfixNotAvailable(); }
public void testNotEnumMulti() { assertQuickfixNotAvailable(); }
public void testEnumRevOrder() { assertQuickfixNotAvailable();}
public void testErrorInMultiDArray() { assertQuickfixNotAvailable();}
public void testMultiDArrayNoError() {assertQuickfixNotAvailable();}
public void testInnerEnum() {doTest("Inner");}
public void testFooInit() {assertQuickfixNotAvailable();}
public void testOuterEnumUse() {doTest("TestEnum");}
@Override
protected void doTest(String hint) {
super.doTest(InspectionGadgetsBundle.message("array.can.be.replaced.with.enum.values.quickfix", hint));
}
@Override
protected void assertQuickfixNotAvailable() {
String message = InspectionGadgetsBundle.message("array.can.be.replaced.with.enum.values.quickfix", "@");
super.assertQuickfixNotAvailable(message.substring(0, message.indexOf('@')));
}
@Override
public void setUp() throws Exception {
super.setUp();
myFixture.enableInspections(new ArrayCanBeReplacedWithEnumValuesInspection());
myRelativePath = "style/array_replaced_enum_values";
}
@Override
protected String[] getEnvironmentClasses() {
return new String[] {
"public class OuterEnum {\n" +
" public enum TestEnum {\n" +
" A, B, C\n" +
" }\n" +
"}"
};
}
}