diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java index fb47e2fa92cd..ff003657b462 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java @@ -701,7 +701,7 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec } private void reportFailingCasts(ProblemReporter reporter, DataFlowInstructionVisitor visitor) { - for (PsiTypeCastExpression typeCast : visitor.getFailingCastExpressions()) { + visitor.getFailingCastExpressions().forKeyValue((typeCast, alwaysFails) -> { PsiExpression operand = typeCast.getOperand(); PsiTypeElement castType = typeCast.getCastType(); assert castType != null; @@ -710,8 +710,11 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec if (reporter.isOnTheFly()) { fixes.add(createExplainFix(typeCast, new TrackingRunner.CastDfaProblemType())); } - reporter.registerProblem(castType, InspectionsBundle.message("dataflow.message.cce", operand.getText()), fixes.toArray(LocalQuickFix.EMPTY_ARRAY)); - } + String message = alwaysFails ? + InspectionsBundle.message("dataflow.message.cce.always", operand.getText()) : + InspectionsBundle.message("dataflow.message.cce", operand.getText()); + reporter.registerProblem(castType, message, fixes.toArray(LocalQuickFix.EMPTY_ARRAY)); + }); } private void handleBranchingInstruction(ProblemReporter reporter, diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInstructionVisitor.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInstructionVisitor.java index 03a5752ec917..0120e01dce47 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInstructionVisitor.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInstructionVisitor.java @@ -1,7 +1,10 @@ // Copyright 2000-2019 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.intellij.codeInspection.dataFlow; -import com.intellij.codeInspection.dataFlow.instructions.*; +import com.intellij.codeInspection.dataFlow.instructions.AssignInstruction; +import com.intellij.codeInspection.dataFlow.instructions.EndOfInitializerInstruction; +import com.intellij.codeInspection.dataFlow.instructions.Instruction; +import com.intellij.codeInspection.dataFlow.instructions.ReturnInstruction; import com.intellij.codeInspection.dataFlow.value.*; import com.intellij.codeInspection.util.OptionalUtil; import com.intellij.openapi.application.Application; @@ -18,6 +21,7 @@ import com.intellij.util.ThreeState; import com.intellij.util.containers.ContainerUtil; import com.siyeh.ig.callMatcher.CallMatcher; import com.siyeh.ig.psiutils.TypeUtils; +import one.util.streamex.EntryStream; import one.util.streamex.StreamEx; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -152,8 +156,8 @@ final class DataFlowInstructionVisitor extends StandardInstructionVisitor { return myMethodReferenceResults; } - StreamEx getFailingCastExpressions() { - return StreamEx.ofKeys(myClassCastProblems, StateInfo::shouldReport); + EntryStream getFailingCastExpressions() { + return EntryStream.of(myClassCastProblems).filterValues(StateInfo::shouldReport).mapValues(StateInfo::alwaysFails); } Set getMutabilityViolations(boolean receiver) { @@ -328,6 +332,10 @@ final class DataFlowInstructionVisitor extends StandardInstructionVisitor { // (e.g. if it's inside "if (var == null)" check after contract method invocation return normalException || ephemeralException && !normalOk; } + + boolean alwaysFails() { + return (normalException || ephemeralException) && !normalOk; + } } private class ExpressionVisitor extends JavaElementVisitor { diff --git a/java/java-tests/testData/inspection/dataFlow/ancient/Cce.java b/java/java-tests/testData/inspection/dataFlow/ancient/Cce.java index 14aea115a24c..abfc587d354f 100644 --- a/java/java-tests/testData/inspection/dataFlow/ancient/Cce.java +++ b/java/java-tests/testData/inspection/dataFlow/ancient/Cce.java @@ -17,7 +17,7 @@ public class Cce { Object o = getObject(); if (o instanceof A) { - B b = (B) o; + B b = (B) o; } } diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/CovariantReturn.java b/java/java-tests/testData/inspection/dataFlow/fixture/CovariantReturn.java index aeff0c10550a..3b5c61510b13 100644 --- a/java/java-tests/testData/inspection/dataFlow/fixture/CovariantReturn.java +++ b/java/java-tests/testData/inspection/dataFlow/fixture/CovariantReturn.java @@ -71,7 +71,7 @@ class CovariantReturn { void testCast(Super s) { if(s instanceof Sub) { - Integer i = (Integer)s.get(); + Integer i = (Integer)s.get(); System.out.println(i); } } diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/NullCheckDoesntAffectUncheckedCast.java b/java/java-tests/testData/inspection/dataFlow/fixture/NullCheckDoesntAffectUncheckedCast.java index ecd9b273b966..a431d49e5adc 100644 --- a/java/java-tests/testData/inspection/dataFlow/fixture/NullCheckDoesntAffectUncheckedCast.java +++ b/java/java-tests/testData/inspection/dataFlow/fixture/NullCheckDoesntAffectUncheckedCast.java @@ -2,11 +2,11 @@ class DataFlowBug { public int add2(Object left, Object right) { if (left != null && !(left instanceof String)) { - return ((String) left).length(); + return ((String) left).length(); } if (!(right instanceof String)) { - return ((String) right).length(); + return ((String) right).length(); } return 2; diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/StreamInlining.java b/java/java-tests/testData/inspection/dataFlow/fixture/StreamInlining.java index 4ed9d1f87b44..86cbda30dda1 100644 --- a/java/java-tests/testData/inspection/dataFlow/fixture/StreamInlining.java +++ b/java/java-tests/testData/inspection/dataFlow/fixture/StreamInlining.java @@ -61,7 +61,7 @@ public class StreamInlining { void testIsInstanceIncomplete(List objects) { IntStream is = objects.stream() .filter(String.class::isInstance) - .mapToInt(x -> (Integer)x); + .mapToInt(x -> (Integer)x); objects.stream() .filter(String.class::isInstance) diff --git a/platform/platform-resources-en/src/messages/InspectionsBundle.properties b/platform/platform-resources-en/src/messages/InspectionsBundle.properties index 67e428617e48..cccbb85d78d6 100644 --- a/platform/platform-resources-en/src/messages/InspectionsBundle.properties +++ b/platform/platform-resources-en/src/messages/InspectionsBundle.properties @@ -65,6 +65,7 @@ dataflow.message.npe.array.access.sure=Array access #ref #loc will dataflow.message.npe.field.access.sure=Dereference of #ref #loc will produce NullPointerException dataflow.message.npe.field.access=Dereference of #ref #loc may produce NullPointerException dataflow.message.cce=Casting {0} to #ref #loc may produce ClassCastException +dataflow.message.cce.always=Casting {0} to #ref #loc will produce ClassCastException for any non-null value dataflow.message.arraystore=Storing element of type {0} to array of {1} elements may produce ArrayStoreException dataflow.message.redundant.instanceof=Condition #ref #loc is redundant and can be replaced with a null check dataflow.message.contract.fail=The call to '#ref' always fails, according to its method contracts