diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/SuspiciousToArrayCallInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/SuspiciousToArrayCallInspection.java index 0f5a662a043b..fe72e8782ab6 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/SuspiciousToArrayCallInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/SuspiciousToArrayCallInspection.java @@ -28,10 +28,8 @@ import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.InspectionGadgetsFix; -import com.siyeh.ig.psiutils.CommentTracker; -import com.siyeh.ig.psiutils.FunctionalExpressionUtils; -import com.siyeh.ig.psiutils.StreamApiUtil; -import com.siyeh.ig.psiutils.TypeUtils; +import com.siyeh.ig.callMatcher.CallMatcher; +import com.siyeh.ig.psiutils.*; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -77,10 +75,8 @@ public class SuspiciousToArrayCallInspection extends BaseInspection { if (qualifierExpression == null) { return; } - final PsiType type = qualifierExpression.getType(); - if (!(type instanceof PsiClassType)) { - return; - } + final PsiClassType classType = ObjectUtils.tryCast(qualifierExpression.getType(), PsiClassType.class); + if (classType == null || classType.isRaw()) return; final PsiExpressionList argumentList = expression.getArgumentList(); final PsiExpression[] arguments = argumentList.getExpressions(); if (arguments.length != 1) { @@ -89,8 +85,6 @@ public class SuspiciousToArrayCallInspection extends BaseInspection { final PsiExpression argument = PsiUtil.skipParenthesizedExprDown(arguments[0]); if (argument == null) return; - final PsiClassType classType = (PsiClassType)type; - if (classType.isRaw()) return; final PsiClass aClass = classType.resolve(); if (aClass == null) { return; @@ -106,11 +100,52 @@ public class SuspiciousToArrayCallInspection extends BaseInspection { else if (InheritanceUtil.isInheritor(aClass, CommonClassNames.JAVA_UTIL_STREAM_STREAM)) { PsiType argumentType = getIntFunctionParameterType(argument); if (argumentType != null) { - checkArrayTypes(argument, expression, argumentType, StreamApiUtil.getStreamElementType(classType, false)); + checkArrayTypes(argument, expression, argumentType, getStreamElementType(qualifierExpression)); } } } + private static final CallMatcher STREAM_FILTER = CallMatcher.instanceCall(CommonClassNames.JAVA_UTIL_STREAM_STREAM, "filter") + .parameterTypes(CommonClassNames.JAVA_UTIL_FUNCTION_PREDICATE); + private static final CallMatcher CLASS_INSTANCEOF = CallMatcher.instanceCall(CommonClassNames.JAVA_LANG_CLASS, "isInstance") + .parameterCount(1); + + /** + * @param expression stream expression + * @return type of elements inside the stream. Tries to take into account previous filters by element type + */ + private static @Nullable PsiType getStreamElementType(PsiExpression expression) { + PsiMethodCallExpression call = + ObjectUtils.tryCast(PsiUtil.skipParenthesizedExprDown(expression), PsiMethodCallExpression.class); + while (STREAM_FILTER.test(call)) { + PsiExpression predicate = PsiUtil.skipParenthesizedExprDown(call.getArgumentList().getExpressions()[0]); + if (predicate instanceof PsiMethodReferenceExpression) { + if (CLASS_INSTANCEOF.methodReferenceMatches((PsiMethodReferenceExpression)predicate)) { + PsiExpression qualifier = PsiUtil.skipParenthesizedExprDown(((PsiMethodReferenceExpression)predicate).getQualifierExpression()); + if (qualifier instanceof PsiClassObjectAccessExpression) { + return ((PsiClassObjectAccessExpression)qualifier).getOperand().getType(); + } + } + } + else if (predicate instanceof PsiLambdaExpression) { + PsiParameter[] parameters = ((PsiLambdaExpression)predicate).getParameterList().getParameters(); + if (parameters.length == 1) { + PsiExpression lambdaBody = + PsiUtil.skipParenthesizedExprDown(LambdaUtil.extractSingleExpressionFromBody(((PsiLambdaExpression)predicate).getBody())); + if (lambdaBody instanceof PsiInstanceOfExpression && + ExpressionUtils.isReferenceTo(((PsiInstanceOfExpression)lambdaBody).getOperand(), parameters[0])) { + PsiTypeElement checkType = ((PsiInstanceOfExpression)lambdaBody).getCheckType(); + if (checkType != null) { + return checkType.getType(); + } + } + } + } + call = MethodCallUtils.getQualifierMethodCall(call); + } + return StreamApiUtil.getStreamElementType(expression.getType(), false); + } + private static PsiType getIntFunctionParameterType(PsiExpression argument) { PsiType argumentType = FunctionalExpressionUtils.getFunctionalExpressionType(argument); return PsiUtil.substituteTypeParameter(argumentType, "java.util.function.IntFunction", 0, false); diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/SuspiciousToArrayCallInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/SuspiciousToArrayCallInspectionTest.java index 528be01b7a5f..ffba4f98221b 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/SuspiciousToArrayCallInspectionTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/SuspiciousToArrayCallInspectionTest.java @@ -136,6 +136,26 @@ public class SuspiciousToArrayCallInspectionTest extends LightJavaInspectionTest "class Bar extends Foo {}"); } + public void testStreamFilter() { + doTest("import java.util.Arrays;\n" + + "class Parent {}\n" + + "class Child extends Parent {}\n" + + "class Test {\n" + + " void test(Parent[] parent) {\n" + + " Child[] children = Arrays.stream(parent)\n" + + " .filter(t -> t instanceof Child)\n" + + " .toArray(Child[]::new);\n" + + " Child[] children2 = Arrays.stream(parent)\n" + + " .filter(Child.class::isInstance)\n" + + " .toArray(Child[]::new);\n" + + " Child[] children3 = Arrays.stream(parent)\n" + + " .filter(Child.class::isInstance)\n" + + " .filter(c -> c.hashCode() > 0)\n" + + " .toArray(Child[]::new);\n" + + " }\n" + + "}"); + } + @NotNull @Override protected LightProjectDescriptor getProjectDescriptor() {