From dab8efdeaf72a398eaa72b6f91f6ebea28387993 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Thu, 20 Apr 2017 18:25:09 +0700 Subject: [PATCH] IDEA-170892 Inspection: Suspicious Arrays.fill() call --- .../SuspiciousArrayMethodCallInspection.java | 95 +++++++++++++++++++ .../SuspiciousArrayMethodCall.java | 30 ++++++ ...spiciousArrayMethodCallInspectionTest.java | 39 ++++++++ .../src/messages/InspectionsBundle.properties | 4 + .../SuspiciousArrayMethodCall.html | 9 ++ resources/src/META-INF/IdeaPlugin.xml | 4 + 6 files changed, 181 insertions(+) create mode 100644 java/java-analysis-impl/src/com/intellij/codeInspection/SuspiciousArrayMethodCallInspection.java create mode 100644 java/java-tests/testData/inspection/suspiciousArrayMethodCall/SuspiciousArrayMethodCall.java create mode 100644 java/java-tests/testSrc/com/intellij/codeInspection/SuspiciousArrayMethodCallInspectionTest.java create mode 100644 resources-en/src/inspectionDescriptions/SuspiciousArrayMethodCall.html diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/SuspiciousArrayMethodCallInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/SuspiciousArrayMethodCallInspection.java new file mode 100644 index 000000000000..371f865a63dd --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/SuspiciousArrayMethodCallInspection.java @@ -0,0 +1,95 @@ +/* + * 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; + +import com.intellij.psi.*; +import com.intellij.psi.util.TypeConversionUtil; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.Set; + +/** + * @author Tagir Valeev + */ +public class SuspiciousArrayMethodCallInspection extends BaseJavaBatchLocalInspectionTool { + private static final Set INTERESTING_NAMES = ContainerUtil.set("fill", "binarySearch", "equals", "mismatch"); + + @NotNull + @Override + public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) { + return new JavaElementVisitor() { + @Override + public void visitMethodCallExpression(PsiMethodCallExpression call) { + PsiElement nameElement = call.getMethodExpression().getReferenceNameElement(); + if (nameElement == null) return; + String name = nameElement.getText(); + if (!INTERESTING_NAMES.contains(name)) return; + PsiMethod method = call.resolveMethod(); + if (method == null) return; + PsiClass aClass = method.getContainingClass(); + if (aClass == null || !CommonClassNames.JAVA_UTIL_ARRAYS.equals(aClass.getQualifiedName())) return; + PsiExpression[] args = call.getArgumentList().getExpressions(); + switch (name) { + case "fill": + case "binarySearch": + if (args.length == 2) { + handleArrayElement(args[0], args[1]); + } + else if (args.length == 4) { + handleArrayElement(args[0], args[3]); + } + break; + case "equals": + case "mismatch": + if (args.length == 2) { + handleArrays(nameElement, args[0], args[1]); + } + else if (args.length == 6) { + handleArrays(nameElement, args[0], args[3]); + } + break; + } + } + + private void handleArrayElement(PsiExpression array, PsiExpression element) { + PsiType arrayType = array.getType(); + PsiType elementType = element.getType(); + if (elementType == null || !(arrayType instanceof PsiArrayType)) return; + PsiType arrayElementType = ((PsiArrayType)arrayType).getComponentType(); + // incompatible primitive array will be reported anyways as compilation error + if (arrayElementType instanceof PsiPrimitiveType) return; + if (!TypeConversionUtil.areTypesConvertible(elementType, arrayElementType)) { + holder.registerProblem(element, InspectionsBundle.message("inspection.suspicious.array.method.call.problem.element")); + } + } + + private void handleArrays(PsiElement context, PsiExpression array1, PsiExpression array2) { + PsiType array1Type = array1.getType(); + PsiType array2Type = array2.getType(); + if (!(array1Type instanceof PsiArrayType) || !(array2Type instanceof PsiArrayType)) return; + PsiType array1ElementType = ((PsiArrayType)array1Type).getComponentType(); + PsiType array2ElementType = ((PsiArrayType)array2Type).getComponentType(); + // incompatible primitive array will be reported anyways as compilation error + if (array1ElementType instanceof PsiPrimitiveType || array2ElementType instanceof PsiPrimitiveType) return; + if (!TypeConversionUtil.areTypesConvertible(array1ElementType, array2ElementType) || + !TypeConversionUtil.areTypesConvertible(array2ElementType, array1ElementType)) { + holder.registerProblem(context, InspectionsBundle.message("inspection.suspicious.array.method.call.problem.arrays")); + } + } + }; + } +} diff --git a/java/java-tests/testData/inspection/suspiciousArrayMethodCall/SuspiciousArrayMethodCall.java b/java/java-tests/testData/inspection/suspiciousArrayMethodCall/SuspiciousArrayMethodCall.java new file mode 100644 index 000000000000..4d865b6cde2e --- /dev/null +++ b/java/java-tests/testData/inspection/suspiciousArrayMethodCall/SuspiciousArrayMethodCall.java @@ -0,0 +1,30 @@ +import java.util.Arrays; + +public class SuspiciousArrayMethodCall { + void test() { + int[][] data = new int[10][]; + Arrays.fill(data, -1); + } + + void testBoxing() { + int[] data = new int[10]; + Arrays.fill(data, -1); + } + + int findString(String[] data) { + return Arrays.binarySearch(data, 1); + } + + int findCharSequence(CharSequence[] data, Integer i) { + return Arrays.binarySearch(data, i); + } + + int findCharSequenceNumber(CharSequence[] data, Number n) { + return Arrays.binarySearch(data, n); + } + + boolean testEquality(String[] arr, Integer[] arr2) { + return Arrays.equals(arr, arr2) || + Arrays.equals(arr2, arr); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/SuspiciousArrayMethodCallInspectionTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/SuspiciousArrayMethodCallInspectionTest.java new file mode 100644 index 000000000000..745dc18b6ca1 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInspection/SuspiciousArrayMethodCallInspectionTest.java @@ -0,0 +1,39 @@ +/* + * 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; + +import com.intellij.JavaTestUtil; +import com.siyeh.ig.LightInspectionTestCase; +import org.jetbrains.annotations.Nullable; + +/** + * @author Tagir Valeev + */ +public class SuspiciousArrayMethodCallInspectionTest extends LightInspectionTestCase { + @Override + protected String getBasePath() { + return JavaTestUtil.getRelativeJavaTestDataPath() + "/inspection/suspiciousArrayMethodCall"; + } + + @Nullable + @Override + protected InspectionProfileEntry getInspection() { + return new SuspiciousArrayMethodCallInspection(); + } + + public void testSuspiciousArrayMethodCall() throws Exception { doTest();} +} diff --git a/platform/platform-resources-en/src/messages/InspectionsBundle.properties b/platform/platform-resources-en/src/messages/InspectionsBundle.properties index 2e2ffa1d3f61..eac595468de6 100644 --- a/platform/platform-resources-en/src/messages/InspectionsBundle.properties +++ b/platform/platform-resources-en/src/messages/InspectionsBundle.properties @@ -223,6 +223,10 @@ inspection.suspicious.collections.method.calls.display.name=Suspicious collectio inspection.suspicious.collections.method.calls.problem.descriptor=''{0}'' may not contain objects of type ''{1}'' inspection.suspicious.collections.method.calls.problem.descriptor1=Suspicious call to ''{0}'' +inspection.suspicious.array.method.call.display.name=Suspicious array method calls +inspection.suspicious.array.method.call.problem.element=Element type is not compatible with array type +inspection.suspicious.array.method.call.problem.arrays=Array types are incompatible: arrays are always different + inspection.raw.variable.type.can.be.generic.name=Raw type can be generic inspection.raw.variable.type.can.be.generic.quickfix=Change type of {0} to {1} inspection.raw.variable.type.can.be.generic.family.quickfix=Add generic parameters to the type diff --git a/resources-en/src/inspectionDescriptions/SuspiciousArrayMethodCall.html b/resources-en/src/inspectionDescriptions/SuspiciousArrayMethodCall.html new file mode 100644 index 000000000000..5cdf5d97d875 --- /dev/null +++ b/resources-en/src/inspectionDescriptions/SuspiciousArrayMethodCall.html @@ -0,0 +1,9 @@ + + +

Reports when non-generic array manipulation method like Arrays.fill is called with mismatched argument types. + Such call will not do anything useless and likely to be a mistake. +

+ +

New in 2017.2

+ + \ No newline at end of file diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index c70fa93b9482..a8752c38c684 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -649,6 +649,10 @@ key="inspection.suspicious.collections.method.calls.display.name" groupKey="group.names.probable.bugs" enabledByDefault="true" level="WARNING" implementationClass="com.intellij.codeInspection.miscGenerics.SuspiciousCollectionsMethodCallsInspection"/> +