diff --git a/java/java-impl/src/com/intellij/codeInspection/java18api/Java8ArraySetAllInspection.java b/java/java-impl/src/com/intellij/codeInspection/java18api/Java8ArraySetAllInspection.java new file mode 100644 index 000000000000..c974cc8e849d --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInspection/java18api/Java8ArraySetAllInspection.java @@ -0,0 +1,104 @@ +/* + * Copyright 2000-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 com.intellij.codeInspection.java18api; + +import com.intellij.codeInspection.*; +import com.intellij.codeInspection.util.LambdaGenerationUtil; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.TextRange; +import com.intellij.profile.codeInspection.InspectionProjectProfileManager; +import com.intellij.psi.*; +import com.intellij.psi.codeStyle.CodeStyleManager; +import com.intellij.psi.codeStyle.JavaCodeStyleManager; +import com.intellij.psi.util.PsiUtil; +import com.intellij.util.ObjectUtils; +import com.siyeh.ig.psiutils.*; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Predicate; + +/** + * @author Tagir Valeev + */ +public class Java8ArraySetAllInspection extends BaseJavaBatchLocalInspectionTool { + private static final Logger LOG = Logger.getInstance(Java8ArraySetAllInspection.class); + + @NotNull + @Override + public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) { + if (!PsiUtil.isLanguageLevel8OrHigher(holder.getFile())) { + return PsiElementVisitor.EMPTY_VISITOR; + } + return new JavaElementVisitor() { + @Override + public void visitForStatement(PsiForStatement statement) { + super.visitForStatement(statement); + CountingLoop loop = CountingLoop.from(statement); + if (loop == null || loop.isIncluding()) return; + IndexedContainer container = IndexedContainer.fromLengthExpression(loop.getBound()); + if (container == null || !(container.getQualifier().getType() instanceof PsiArrayType)) return; + if (!StreamApiUtil.isSupportedStreamElement(container.getElementType())) return; + PsiAssignmentExpression assignment = ExpressionUtils.getAssignment(ControlFlowUtils.stripBraces(statement.getBody())); + if (assignment == null) return; + PsiExpression index = container.extractIndexFromGetExpression(assignment.getLExpression()); + if (!ExpressionUtils.isReferenceTo(index, loop.getCounter())) return; + if (!LambdaGenerationUtil.canBeUncheckedLambda(assignment.getRExpression(), Predicate.isEqual(loop.getCounter()))) return; + PsiStatement initialization = statement.getInitialization(); + LOG.assertTrue(initialization != null); + TextRange range = TextRange.from(initialization.getStartOffsetInParent(), initialization.getTextLength()); + boolean wholeStatement = isOnTheFly && InspectionProjectProfileManager.isInformationLevel(getShortName(), statement); + PsiJavaToken rParenth = statement.getRParenth(); + if (wholeStatement && rParenth != null) { + range = new TextRange(0, rParenth.getStartOffsetInParent() + 1); + } + holder.registerProblem(statement, range, InspectionsBundle.message("inspection.replace.loop.with.arrays.setall.message"), + new ReplaceWithArraysSetAllFix()); + } + }; + } + + private static class ReplaceWithArraysSetAllFix implements LocalQuickFix { + @Nls + @NotNull + @Override + public String getFamilyName() { + return InspectionsBundle.message("inspection.replace.loop.with.arrays.setall.fix.family.name"); + } + + @Override + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + PsiForStatement statement = ObjectUtils.tryCast(descriptor.getStartElement(), PsiForStatement.class); + if (statement == null) return; + CountingLoop loop = CountingLoop.from(statement); + if (loop == null) return; + IndexedContainer container = IndexedContainer.fromLengthExpression(loop.getBound()); + if (container == null) return; + PsiAssignmentExpression assignment = ExpressionUtils.getAssignment(ControlFlowUtils.stripBraces(statement.getBody())); + if (assignment == null) return; + PsiExpression rValue = assignment.getRExpression(); + if (rValue == null) return; + CommentTracker ct = new CommentTracker(); + String replacement = CommonClassNames.JAVA_UTIL_ARRAYS + ".setAll(" + + ct.text(container.getQualifier()) + ", " + loop.getCounter().getName() + "->" + ct.text(rValue) + ");"; + PsiElement result = ct.replaceAndRestoreComments(statement, replacement); + LambdaCanBeMethodReferenceInspection.replaceAllLambdasWithMethodReferences(result); + result = JavaCodeStyleManager.getInstance(project).shortenClassReferences(result); + CodeStyleManager.getInstance(project).reformat(result); + } + } +} diff --git a/java/java-tests/testData/inspection/java8ArraysSetAll/afterFieldComments.java b/java/java-tests/testData/inspection/java8ArraysSetAll/afterFieldComments.java new file mode 100644 index 000000000000..c507775fcaea --- /dev/null +++ b/java/java-tests/testData/inspection/java8ArraysSetAll/afterFieldComments.java @@ -0,0 +1,12 @@ +import java.util.Arrays; + +// "Replace loop with Arrays.setAll" "true" +public class Test { + private Object[] data; + + public void fill() { + /*in body*/ + /*in lvalue*/ + Arrays.setAll(this./*comment*/data, idx -> "Hello!" + /* we need index here */ idx); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/java8ArraysSetAll/afterSimple.java b/java/java-tests/testData/inspection/java8ArraysSetAll/afterSimple.java new file mode 100644 index 000000000000..1430a3b0afdd --- /dev/null +++ b/java/java-tests/testData/inspection/java8ArraysSetAll/afterSimple.java @@ -0,0 +1,11 @@ +// "Replace loop with Arrays.setAll" "true" +import java.util.Arrays; +import java.util.List; + +public class Test { + public void test(List ints) { + int[] arr = new int[ints.size()]; + Arrays.setAll(arr, ints::get); + System.out.println(Arrays.toString(arr)); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/java8ArraysSetAll/beforeFieldComments.java b/java/java-tests/testData/inspection/java8ArraysSetAll/beforeFieldComments.java new file mode 100644 index 000000000000..a73a869dff7e --- /dev/null +++ b/java/java-tests/testData/inspection/java8ArraysSetAll/beforeFieldComments.java @@ -0,0 +1,11 @@ +// "Replace loop with Arrays.setAll" "true" +public class Test { + private Object[] data; + + public void fill() { + for(int idx = 0; (this./*comment*/data).length > idx; idx+=1) { + /*in body*/ + this./*in lvalue*/data[idx] = "Hello!" + /* we need index here */ idx; + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/java8ArraysSetAll/beforeSimple.java b/java/java-tests/testData/inspection/java8ArraysSetAll/beforeSimple.java new file mode 100644 index 000000000000..ff1f4719ddcd --- /dev/null +++ b/java/java-tests/testData/inspection/java8ArraysSetAll/beforeSimple.java @@ -0,0 +1,13 @@ +// "Replace loop with Arrays.setAll" "true" +import java.util.Arrays; +import java.util.List; + +public class Test { + public void test(List ints) { + int[] arr = new int[ints.size()]; + for(int i = 0; arr.length > i; i++) { + arr[i] = ints.get(i); + } + System.out.println(Arrays.toString(arr)); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/java8ArraysSetAll/beforeUnsupportedType.java b/java/java-tests/testData/inspection/java8ArraysSetAll/beforeUnsupportedType.java new file mode 100644 index 000000000000..97df535ea14b --- /dev/null +++ b/java/java-tests/testData/inspection/java8ArraysSetAll/beforeUnsupportedType.java @@ -0,0 +1,13 @@ +// "Replace loop with Arrays.setAll" "false" +import java.util.Arrays; +import java.util.List; + +public class Test { + public void test(List bytes) { + byte[] arr = new byte[bytes.size()]; + for(int i = 0; arr.length > i; i++) { + arr[i] = bytes.get(i); + } + System.out.println(Arrays.toString(arr)); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/java18api/Java8ArraySetAllInspectionTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/java18api/Java8ArraySetAllInspectionTest.java new file mode 100644 index 000000000000..25e7b8cfe339 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInspection/java18api/Java8ArraySetAllInspectionTest.java @@ -0,0 +1,40 @@ +/* + * Copyright 2000-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 com.intellij.codeInspection.java18api; + +import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase; +import com.intellij.codeInspection.LocalInspectionTool; +import org.jetbrains.annotations.NotNull; + +/** + * @author Tagir Valeev + */ +public class Java8ArraySetAllInspectionTest extends LightQuickFixParameterizedTestCase { + @NotNull + @Override + protected LocalInspectionTool[] configureLocalInspectionTools() { + return new LocalInspectionTool[]{new Java8ArraySetAllInspection()}; + } + + public void test() throws Exception { + doAllTests(); + } + + @Override + protected String getBasePath() { + return "/inspection/java8ArraysSetAll"; + } +} diff --git a/platform/platform-resources-en/src/messages/InspectionsBundle.properties b/platform/platform-resources-en/src/messages/InspectionsBundle.properties index e106cf759e83..7e087f7dec24 100644 --- a/platform/platform-resources-en/src/messages/InspectionsBundle.properties +++ b/platform/platform-resources-en/src/messages/InspectionsBundle.properties @@ -758,3 +758,6 @@ inspection.replace.with.bulk.message=Iteration can be replaced with bulk ''{0}'' inspection.replace.with.bulk.fix.name=Replace iteration with bulk ''{0}'' call inspection.replace.with.bulk.fix.family.name=Replace with bulk method call inspection.replace.with.bulk.wrap.arrays=Use Arrays.asList() to wrap arrays + +inspection.replace.loop.with.arrays.setall.message=Loop can be replaced with Arrays.setAll +inspection.replace.loop.with.arrays.setall.fix.family.name=Replace loop with Arrays.setAll diff --git a/resources-en/src/inspectionDescriptions/Java8ArraySetAll.html b/resources-en/src/inspectionDescriptions/Java8ArraySetAll.html new file mode 100644 index 000000000000..0e86a0bc9d0a --- /dev/null +++ b/resources-en/src/inspectionDescriptions/Java8ArraySetAll.html @@ -0,0 +1,14 @@ + + +This inspection looks for loops which could be replaced with Arrays.setAll() call. + +

The loops like this are reported:

+
+  for (int i=0; i<array.length; i++) {
+     array[i] = calc(i);
+  }
+
+

This conversion is available since Java 8 only.

+New in 2017.1 + + \ No newline at end of file diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index a71f092f67fd..a4e34fd52800 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -840,6 +840,11 @@ groupKey="group.names.language.level.specific.issues.and.migration.aids" enabledByDefault="true" level="WARNING" implementationClass="com.intellij.codeInspection.java18api.Java8ListSortInspection" displayName="Collections.sort() can be replaced with List.sort()"/> +