mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
DFA instruction visitor refactoring wave#7
Merge reporting of constant variables and constant conditions Fix DfaConstValue#createFromValue (for constants like Integer X = new Integer(1)) DfaMemoryState#getConstantValue now accepts DfaValue (and processes unboxed value as well) Report "will cause NPE", etc. messages when dereferenced value is statically known to be null (not only null literal) IDEA-194676
This commit is contained in:
+2
-8
@@ -507,14 +507,8 @@ public abstract class ContractReturnValue {
|
||||
|
||||
@Override
|
||||
public boolean isValueCompatible(DfaMemoryState state, DfaValue value) {
|
||||
if (value instanceof DfaVariableValue) {
|
||||
value = state.getConstantValue((DfaVariableValue)value);
|
||||
}
|
||||
if (value instanceof DfaConstValue) {
|
||||
Object constant = ((DfaConstValue)value).getValue();
|
||||
return Boolean.valueOf(myValue).equals(constant);
|
||||
}
|
||||
return true;
|
||||
DfaConstValue dfaConst = state.getConstantValue(value);
|
||||
return dfaConst == null || Boolean.valueOf(myValue).equals(dfaConst.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-6
@@ -19,7 +19,6 @@ import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaConstValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.CachedValueProvider;
|
||||
import com.intellij.psi.util.CachedValuesManager;
|
||||
@@ -223,11 +222,9 @@ class CustomMethodHandlers {
|
||||
return fact.min();
|
||||
}
|
||||
}
|
||||
if (value instanceof DfaVariableValue) {
|
||||
value = memoryState.getConstantValue((DfaVariableValue)value);
|
||||
}
|
||||
if (value instanceof DfaConstValue) {
|
||||
Object constant = ((DfaConstValue)value).getValue();
|
||||
DfaConstValue dfaConst = memoryState.getConstantValue(value);
|
||||
if (dfaConst != null) {
|
||||
Object constant = dfaConst.getValue();
|
||||
if (constant instanceof String && ((String)constant).length() > MAX_STRING_CONSTANT_LENGTH_TO_TRACK) return null;
|
||||
return constant;
|
||||
}
|
||||
|
||||
+113
-56
@@ -35,13 +35,17 @@ import com.siyeh.ig.psiutils.*;
|
||||
import one.util.streamex.IntStreamEx;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.*;
|
||||
|
||||
import static com.intellij.util.ObjectUtils.tryCast;
|
||||
|
||||
@SuppressWarnings("ConditionalExpressionWithIdenticalBranches")
|
||||
public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool {
|
||||
static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.dataFlow.DataFlowInspection");
|
||||
@@ -272,11 +276,7 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
|
||||
reportOptionalOfNullableImprovements(holder, reportedAnchors, visitor.getOfNullableCalls());
|
||||
|
||||
visitor.getBooleanExpressions().forEach((expression, state) -> {
|
||||
if (state != ThreeState.UNSURE) {
|
||||
reportConstantBoolean(holder, expression, reportedAnchors, state.toBoolean());
|
||||
}
|
||||
});
|
||||
reportConstants(holder, visitor, reportedAnchors);
|
||||
|
||||
reportMethodReferenceProblems(holder, visitor);
|
||||
|
||||
@@ -284,10 +284,6 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
|
||||
reportArrayStoreProblems(holder, visitor);
|
||||
|
||||
if (REPORT_CONSTANT_REFERENCE_VALUES) {
|
||||
reportConstantReferenceValues(holder, visitor, reportedAnchors);
|
||||
}
|
||||
|
||||
if (REPORT_NULLABLE_METHODS_RETURNING_NOT_NULL && visitor.isAlwaysReturnsNotNull(runner.getInstructions())) {
|
||||
reportAlwaysReturnsNotNull(holder, scope);
|
||||
}
|
||||
@@ -300,6 +296,76 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
reportDuplicateAssignments(holder, reportedAnchors, visitor);
|
||||
}
|
||||
|
||||
private void reportConstants(ProblemsHolder holder, DataFlowInstructionVisitor visitor, HashSet<PsiElement> reportedAnchors) {
|
||||
visitor.getConstantExpressions().forEach((expression, result) -> {
|
||||
if (result == DataFlowInstructionVisitor.ConstantResult.UNKNOWN) return;
|
||||
if (isCondition(expression)) {
|
||||
if (result.value() instanceof Boolean) {
|
||||
reportConstantBoolean(holder, expression, reportedAnchors, (Boolean)result.value());
|
||||
}
|
||||
}
|
||||
else {
|
||||
reportConstantReferenceValue(holder, reportedAnchors, expression, result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static boolean isCondition(@NotNull PsiExpression expression) {
|
||||
PsiType type = expression.getType();
|
||||
if (type == null || !PsiType.BOOLEAN.isAssignableFrom(type)) return false;
|
||||
if (!(expression instanceof PsiMethodCallExpression) && !(expression instanceof PsiReferenceExpression)) return true;
|
||||
PsiElement parent = PsiUtil.skipParenthesizedExprUp(expression.getParent());
|
||||
if (parent instanceof PsiStatement) return !(parent instanceof PsiReturnStatement);
|
||||
if (parent instanceof PsiPolyadicExpression) {
|
||||
IElementType tokenType = ((PsiPolyadicExpression)parent).getOperationTokenType();
|
||||
return tokenType.equals(JavaTokenType.ANDAND) || tokenType.equals(JavaTokenType.OROR);
|
||||
}
|
||||
if (parent instanceof PsiConditionalExpression) {
|
||||
return PsiTreeUtil.isAncestor(((PsiConditionalExpression)parent).getCondition(), expression, false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void reportConstantReferenceValue(ProblemsHolder holder, Set<PsiElement> reportedAnchors,
|
||||
PsiExpression ref, DataFlowInstructionVisitor.ConstantResult constant) {
|
||||
if (!REPORT_CONSTANT_REFERENCE_VALUES && ref instanceof PsiReferenceExpression) return;
|
||||
if (shouldBeSuppressed(ref)) return;
|
||||
if (constant == DataFlowInstructionVisitor.ConstantResult.UNKNOWN || !reportedAnchors.add(ref)) return;
|
||||
List<LocalQuickFix> fixes = new SmartList<>();
|
||||
String presentableName = constant.toString();
|
||||
fixes.add(new ReplaceWithConstantValueFix(presentableName, presentableName));
|
||||
Object value = constant.value();
|
||||
boolean isAssertion = value instanceof Boolean && isAssertionEffectively(ref, (Boolean)value);
|
||||
if (isAssertion && DONT_REPORT_TRUE_ASSERT_STATEMENTS) return;
|
||||
if (value instanceof Boolean) {
|
||||
ContainerUtil.addIfNotNull(fixes, createReplaceWithNullCheckFix(ref, (Boolean)value));
|
||||
}
|
||||
if (holder.isOnTheFly()) {
|
||||
if (ref instanceof PsiReferenceExpression) {
|
||||
fixes.add(new SetInspectionOptionFix(this, "REPORT_CONSTANT_REFERENCE_VALUES",
|
||||
InspectionsBundle.message("inspection.data.flow.turn.off.constant.references.quickfix"),
|
||||
false));
|
||||
}
|
||||
if (isAssertion) {
|
||||
fixes.add(new SetInspectionOptionFix(this, "DONT_REPORT_TRUE_ASSERT_STATEMENTS",
|
||||
InspectionsBundle.message("inspection.data.flow.turn.off.true.asserts.quickfix"), true));
|
||||
}
|
||||
}
|
||||
|
||||
String valueText;
|
||||
ProblemHighlightType type;
|
||||
if (ref instanceof PsiMethodCallExpression) {
|
||||
type = ProblemHighlightType.GENERIC_ERROR_OR_WARNING;
|
||||
valueText = "Result of";
|
||||
}
|
||||
else {
|
||||
type = ProblemHighlightType.WEAK_WARNING;
|
||||
valueText = "Value";
|
||||
}
|
||||
holder.registerProblem(ref, MessageFormat.format("{0} <code>#ref</code> #loc is always ''{1}''", valueText, presentableName),
|
||||
type, fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
|
||||
}
|
||||
|
||||
private void reportDuplicateAssignments(ProblemsHolder holder,
|
||||
HashSet<PsiElement> reportedAnchors,
|
||||
DataFlowInstructionVisitor visitor) {
|
||||
@@ -347,6 +413,7 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
private void reportNullabilityProblems(ProblemsHolder holder,
|
||||
DataFlowInstructionVisitor visitor,
|
||||
HashSet<PsiElement> reportedAnchors) {
|
||||
Map<PsiExpression, DataFlowInstructionVisitor.ConstantResult> expressions = visitor.getConstantExpressions();
|
||||
visitor.problems().forEach(problem -> {
|
||||
if (NullabilityProblemKind.passingNullableArgumentToNonAnnotatedParameter.isMyProblem(problem) ||
|
||||
NullabilityProblemKind.assigningNullableValueToNonAnnotatedField.isMyProblem(problem) ||
|
||||
@@ -357,33 +424,36 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
if (!reportedAnchors.add(problem.getAnchor())) return;
|
||||
NullabilityProblemKind.innerClassNPE.ifMyProblem(problem, newExpression -> {
|
||||
List<LocalQuickFix> fixes = createNPEFixes(newExpression.getQualifier(), newExpression, holder.isOnTheFly());
|
||||
holder.registerProblem(getElementToHighlight(newExpression), problem.getMessage(), fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
|
||||
holder.registerProblem(getElementToHighlight(newExpression), problem.getMessage(expressions), fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
|
||||
});
|
||||
NullabilityProblemKind.callMethodRefNPE.ifMyProblem(problem, methodRef ->
|
||||
holder.registerProblem(methodRef, InspectionsBundle.message("dataflow.message.npe.methodref.invocation"),
|
||||
createMethodReferenceNPEFixes(methodRef).toArray(LocalQuickFix.EMPTY_ARRAY)));
|
||||
NullabilityProblemKind.callNPE.ifMyProblem(problem, call -> reportCallMayProduceNpe(holder, call));
|
||||
NullabilityProblemKind.callNPE.ifMyProblem(problem, call -> reportCallMayProduceNpe(holder, problem.getMessage(expressions), call));
|
||||
NullabilityProblemKind.passingNullableToNotNullParameter.ifMyProblem(problem, expr -> reportNullableArgument(holder, expr));
|
||||
NullabilityProblemKind.arrayAccessNPE.ifMyProblem(problem, expression -> {
|
||||
LocalQuickFix[] fix =
|
||||
createNPEFixes(expression.getArrayExpression(), expression, holder.isOnTheFly()).toArray(LocalQuickFix.EMPTY_ARRAY);
|
||||
holder.registerProblem(expression, problem.getMessage(), fix);
|
||||
holder.registerProblem(expression, problem.getMessage(expressions), fix);
|
||||
});
|
||||
NullabilityProblemKind.fieldAccessNPE.ifMyProblem(problem, element -> {
|
||||
PsiElement parent = element.getParent();
|
||||
PsiExpression fieldAccess = parent instanceof PsiReferenceExpression ? (PsiExpression)parent : element;
|
||||
LocalQuickFix[] fix = createNPEFixes(element, fieldAccess, holder.isOnTheFly()).toArray(LocalQuickFix.EMPTY_ARRAY);
|
||||
holder.registerProblem(element, problem.getMessage(), fix);
|
||||
holder.registerProblem(element, problem.getMessage(expressions), fix);
|
||||
});
|
||||
NullabilityProblemKind.unboxingNullable.ifMyProblem(problem, element -> holder.registerProblem(element, problem.getMessage()));
|
||||
NullabilityProblemKind.nullableFunctionReturn.ifMyProblem(problem, expr -> holder.registerProblem(expr, problem.getMessage()));
|
||||
NullabilityProblemKind.assigningToNotNull.ifMyProblem(problem, expr -> reportNullabilityProblem(holder, problem, expr));
|
||||
NullabilityProblemKind.storingToNotNullArray.ifMyProblem(problem, expr -> reportNullabilityProblem(holder, problem, expr));
|
||||
NullabilityProblemKind.unboxingNullable.ifMyProblem(problem, element -> holder.registerProblem(element, problem.getMessage(expressions)));
|
||||
NullabilityProblemKind.nullableFunctionReturn.ifMyProblem(problem, expr -> holder.registerProblem(expr, problem.getMessage(expressions)));
|
||||
NullabilityProblemKind.assigningToNotNull.ifMyProblem(problem, expr -> reportNullabilityProblem(holder, problem, expr, expressions));
|
||||
NullabilityProblemKind.storingToNotNullArray.ifMyProblem(problem, expr -> reportNullabilityProblem(holder, problem, expr, expressions));
|
||||
});
|
||||
}
|
||||
|
||||
private void reportNullabilityProblem(ProblemsHolder holder, NullabilityProblem<?> problem, PsiExpression expr) {
|
||||
holder.registerProblem(expr, problem.getMessage(), createNPEFixes(expr, expr, holder.isOnTheFly()).toArray(LocalQuickFix.EMPTY_ARRAY));
|
||||
private void reportNullabilityProblem(ProblemsHolder holder,
|
||||
NullabilityProblem<?> problem,
|
||||
PsiExpression expr,
|
||||
Map<PsiExpression, DataFlowInstructionVisitor.ConstantResult> expressions) {
|
||||
holder.registerProblem(expr, problem.getMessage(expressions), createNPEFixes(expr, expr, holder.isOnTheFly()).toArray(LocalQuickFix.EMPTY_ARRAY));
|
||||
}
|
||||
|
||||
private static void reportArrayAccessProblems(ProblemsHolder holder, DataFlowInstructionVisitor visitor) {
|
||||
@@ -490,35 +560,6 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
});
|
||||
}
|
||||
|
||||
private void reportConstantReferenceValues(ProblemsHolder holder, DataFlowInstructionVisitor visitor, Set<PsiElement> reportedAnchors) {
|
||||
visitor.getConstantReferenceValues().forEach((ref, dfaConst) -> {
|
||||
if (ref.getParent() instanceof PsiReferenceExpression || DfaConstValue.isSentinel(dfaConst)) return;
|
||||
if (!reportedAnchors.add(ref)) return;
|
||||
|
||||
final Object value = dfaConst.getValue();
|
||||
PsiVariable constant = dfaConst.getConstant();
|
||||
final String exprText = String.valueOf(value);
|
||||
final String presentableName = constant != null ? constant.getName() : exprText;
|
||||
|
||||
List<LocalQuickFix> fixes = new SmartList<>();
|
||||
fixes.add(new ReplaceWithConstantValueFix(presentableName, exprText));
|
||||
boolean isAssertion = value instanceof Boolean && isAssertionEffectively(ref, (Boolean)value);
|
||||
if (isAssertion && DONT_REPORT_TRUE_ASSERT_STATEMENTS) return;
|
||||
if (holder.isOnTheFly()) {
|
||||
fixes.add(new SetInspectionOptionFix(this, "REPORT_CONSTANT_REFERENCE_VALUES",
|
||||
InspectionsBundle.message("inspection.data.flow.turn.off.constant.references.quickfix"),
|
||||
false));
|
||||
if (isAssertion) {
|
||||
fixes.add(new SetInspectionOptionFix(this, "DONT_REPORT_TRUE_ASSERT_STATEMENTS",
|
||||
InspectionsBundle.message("inspection.data.flow.turn.off.true.asserts.quickfix"), true));
|
||||
}
|
||||
}
|
||||
|
||||
holder.registerProblem(ref, "Value <code>#ref</code> #loc is always '" + presentableName + "'",
|
||||
ProblemHighlightType.WEAK_WARNING, fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
|
||||
});
|
||||
}
|
||||
|
||||
private void reportNullableArgumentsPassedToNonAnnotated(DataFlowInstructionVisitor visitor, ProblemsHolder holder, Set<PsiElement> reportedAnchors) {
|
||||
for (PsiElement expr : visitor.problems()
|
||||
.map(NullabilityProblemKind.passingNullableArgumentToNonAnnotatedParameter::asMyProblem).nonNull()
|
||||
@@ -581,20 +622,20 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
if (parent instanceof PsiAssignmentExpression) {
|
||||
PsiExpression lExpression = ((PsiAssignmentExpression)parent).getLExpression();
|
||||
PsiElement target = lExpression instanceof PsiReferenceExpression ? ((PsiReferenceExpression)lExpression).resolve() : null;
|
||||
return ObjectUtils.tryCast(target, PsiField.class);
|
||||
return tryCast(target, PsiField.class);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void reportCallMayProduceNpe(ProblemsHolder holder, PsiMethodCallExpression callExpression) {
|
||||
private void reportCallMayProduceNpe(ProblemsHolder holder,
|
||||
String message,
|
||||
PsiMethodCallExpression callExpression) {
|
||||
PsiReferenceExpression methodExpression = callExpression.getMethodExpression();
|
||||
List<LocalQuickFix> fixes = createNPEFixes(methodExpression.getQualifierExpression(), callExpression, holder.isOnTheFly());
|
||||
ContainerUtil.addIfNotNull(fixes, ReplaceWithObjectsEqualsFix.createFix(callExpression, methodExpression));
|
||||
|
||||
PsiElement toHighlight = getElementToHighlight(callExpression);
|
||||
holder.registerProblem(toHighlight,
|
||||
InspectionsBundle.message("dataflow.message.npe.method.invocation"),
|
||||
fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
|
||||
holder.registerProblem(toHighlight, message, fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
|
||||
}
|
||||
|
||||
private static void reportCastMayFail(ProblemsHolder holder, TypeCastInstruction instruction) {
|
||||
@@ -699,17 +740,33 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
holder.registerProblem(psiAnchor, message, fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
|
||||
}
|
||||
|
||||
@Contract("null -> false")
|
||||
private static boolean shouldBeSuppressed(PsiElement anchor) {
|
||||
if (!(anchor instanceof PsiExpression)) return false;
|
||||
// Don't report System.out.println(b = false)
|
||||
if (anchor instanceof PsiAssignmentExpression) return true;
|
||||
PsiExpression expression = (PsiExpression)anchor;
|
||||
// Dereference of null will be covered by other warning
|
||||
if (ExpressionUtils.isVoidContext(expression) || isDereferenceContext(expression)) return true;
|
||||
if (isFlagCheck(anchor)) return true;
|
||||
if (expression instanceof PsiReferenceExpression) {
|
||||
PsiField field = tryCast(((PsiReferenceExpression)expression).resolve(), PsiField.class);
|
||||
return field != null && field.hasModifierProperty(PsiModifier.STATIC) && ExpressionUtils.isNullLiteral(field.getInitializer());
|
||||
}
|
||||
while (expression != null && BoolUtils.isNegation(expression)) {
|
||||
expression = BoolUtils.getNegated(expression);
|
||||
}
|
||||
PsiMethodCallExpression call = ObjectUtils.tryCast(expression, PsiMethodCallExpression.class);
|
||||
PsiMethodCallExpression call = tryCast(expression, PsiMethodCallExpression.class);
|
||||
// Reported by "Equals with itself" inspection; avoid double reporting
|
||||
return call != null && EqualsWithItselfInspection.isEqualsWithItself(call);
|
||||
}
|
||||
|
||||
private static boolean isDereferenceContext(PsiExpression ref) {
|
||||
PsiElement parent = PsiUtil.skipParenthesizedExprUp(ref.getParent());
|
||||
return parent instanceof PsiReferenceExpression || parent instanceof PsiArrayAccessExpression
|
||||
|| parent instanceof PsiSwitchStatement || parent instanceof PsiSynchronizedStatement;
|
||||
}
|
||||
|
||||
private static LocalQuickFix createReplaceWithNullCheckFix(PsiElement psiAnchor, boolean evaluatesToTrue) {
|
||||
if (evaluatesToTrue) return null;
|
||||
if (!(psiAnchor instanceof PsiMethodCallExpression)) return null;
|
||||
@@ -843,7 +900,7 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
int index = ArrayUtil.indexOf(((PsiExpressionList)parent).getExpressions(), anchor);
|
||||
if (index >= 0) {
|
||||
ValueConstraint wantedConstraint = evaluatesToTrue ? ValueConstraint.FALSE_VALUE : ValueConstraint.TRUE_VALUE;
|
||||
PsiMethodCallExpression call = ObjectUtils.tryCast(parent.getParent(), PsiMethodCallExpression.class);
|
||||
PsiMethodCallExpression call = tryCast(parent.getParent(), PsiMethodCallExpression.class);
|
||||
if (call != null) {
|
||||
PsiMethod method = call.resolveMethod();
|
||||
if (method != null) {
|
||||
@@ -875,7 +932,7 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
return false;
|
||||
}
|
||||
|
||||
static boolean isFlagCheck(PsiElement element) {
|
||||
private static boolean isFlagCheck(PsiElement element) {
|
||||
PsiElement scope = PsiTreeUtil.getParentOfType(element, PsiStatement.class, PsiVariable.class);
|
||||
PsiExpression topExpression = scope instanceof PsiIfStatement ? ((PsiIfStatement)scope).getCondition() :
|
||||
scope instanceof PsiVariable ? ((PsiVariable)scope).getInitializer() :
|
||||
|
||||
+44
-68
@@ -9,10 +9,8 @@ import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiTypesUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.ThreeState;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.siyeh.ig.psiutils.ExpressionUtils;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -27,12 +25,11 @@ final class DataFlowInstructionVisitor extends StandardInstructionVisitor {
|
||||
private final Map<NullabilityProblemKind.NullabilityProblem<?>, StateInfo> myStateInfos = new LinkedHashMap<>();
|
||||
private final Set<Instruction> myCCEInstructions = ContainerUtil.newHashSet();
|
||||
private final Map<PsiCallExpression, Boolean> myFailingCalls = new HashMap<>();
|
||||
private final Map<PsiExpression, ThreeState> myBooleanExpressions = new HashMap<>();
|
||||
private final Map<PsiExpression, ConstantResult> myConstantExpressions = new HashMap<>();
|
||||
private final Map<PsiElement, ThreeState> myOfNullableCalls = new HashMap<>();
|
||||
private final Map<PsiAssignmentExpression, Pair<PsiType, PsiType>> myArrayStoreProblems = new HashMap<>();
|
||||
private final Map<PsiMethodReferenceExpression, DfaValue> myMethodReferenceResults = new HashMap<>();
|
||||
private final Map<PsiArrayAccessExpression, ThreeState> myOutOfBoundsArrayAccesses = new HashMap<>();
|
||||
private final Map<PsiReferenceExpression, DfaConstValue> myValues = new HashMap<>();
|
||||
private final Set<PsiElement> myReceiverMutabilityViolation = new HashSet<>();
|
||||
private final Set<PsiElement> myArgumentMutabilityViolation = new HashSet<>();
|
||||
private final Map<PsiExpression, Boolean> mySameValueAssigned = new HashMap<>();
|
||||
@@ -128,8 +125,8 @@ final class DataFlowInstructionVisitor extends StandardInstructionVisitor {
|
||||
return myOfNullableCalls;
|
||||
}
|
||||
|
||||
Map<PsiExpression, ThreeState> getBooleanExpressions() {
|
||||
return myBooleanExpressions;
|
||||
Map<PsiExpression, ConstantResult> getConstantExpressions() {
|
||||
return myConstantExpressions;
|
||||
}
|
||||
|
||||
Map<PsiMethodReferenceExpression, DfaValue> getMethodReferenceResults() {
|
||||
@@ -167,7 +164,9 @@ final class DataFlowInstructionVisitor extends StandardInstructionVisitor {
|
||||
@Nullable TextRange range,
|
||||
@NotNull DfaMemoryState memState) {
|
||||
expression.accept(new ExpressionVisitor(value, memState));
|
||||
handleBooleanResults(value, memState, expression);
|
||||
if (range == null) {
|
||||
handleBooleanResults(value, memState, expression);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -213,10 +212,6 @@ final class DataFlowInstructionVisitor extends StandardInstructionVisitor {
|
||||
return super.visitEndOfInitializer(instruction, runner, state);
|
||||
}
|
||||
|
||||
public Map<PsiReferenceExpression, DfaConstValue> getConstantReferenceValues() {
|
||||
return myValues;
|
||||
}
|
||||
|
||||
private static boolean hasNonTrivialFailingContracts(PsiCallExpression call) {
|
||||
List<? extends MethodContract> contracts = JavaMethodContractUtil.getMethodCallContracts(call);
|
||||
return !contracts.isEmpty() &&
|
||||
@@ -224,47 +219,18 @@ final class DataFlowInstructionVisitor extends StandardInstructionVisitor {
|
||||
}
|
||||
|
||||
private void handleBooleanResults(DfaValue value, DfaMemoryState memState, PsiExpression expression) {
|
||||
ThreeState curState = myBooleanExpressions.get(expression);
|
||||
if (curState == ThreeState.UNSURE) return;
|
||||
ThreeState nextState = ThreeState.UNSURE;
|
||||
value = value instanceof DfaVariableValue ? memState.getConstantValue((DfaVariableValue)value) : value;
|
||||
if (value instanceof DfaConstValue) {
|
||||
Object val = ((DfaConstValue)value).getValue();
|
||||
if (val instanceof Boolean) {
|
||||
nextState = ThreeState.fromBoolean((Boolean)val);
|
||||
if (curState != null && curState != nextState) {
|
||||
nextState = ThreeState.UNSURE;
|
||||
}
|
||||
if (expression instanceof PsiLiteralExpression) return;
|
||||
ConstantResult curState = myConstantExpressions.get(expression);
|
||||
if (curState == ConstantResult.UNKNOWN) return;
|
||||
ConstantResult nextState = ConstantResult.UNKNOWN;
|
||||
DfaConstValue dfaConst = memState.getConstantValue(value);
|
||||
if (dfaConst != null) {
|
||||
nextState = ConstantResult.fromConstValue(dfaConst);
|
||||
if (curState != null && curState != nextState) {
|
||||
nextState = ConstantResult.UNKNOWN;
|
||||
}
|
||||
}
|
||||
if (curState != null || shouldCollectBooleanResult(expression)) {
|
||||
myBooleanExpressions.put(expression, nextState);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean shouldCollectBooleanResult(PsiExpression expression) {
|
||||
if (expression instanceof PsiLiteralExpression) return false;
|
||||
PsiType type = expression.getType();
|
||||
if (type == null || !PsiType.BOOLEAN.isAssignableFrom(type)) return false;
|
||||
if (expression instanceof PsiPrefixExpression || expression instanceof PsiPolyadicExpression) {
|
||||
return !DataFlowInspectionBase.isFlagCheck(expression);
|
||||
}
|
||||
PsiPolyadicExpression polyadic = tryCast(PsiUtil.skipParenthesizedExprUp(expression.getParent()), PsiPolyadicExpression.class);
|
||||
if (polyadic != null) {
|
||||
if ((polyadic.getOperationTokenType().equals(JavaTokenType.ANDAND) || polyadic.getOperationTokenType().equals(JavaTokenType.OROR)) &&
|
||||
!DataFlowInspectionBase.isFlagCheck(expression)) return true;
|
||||
}
|
||||
if (expression instanceof PsiMethodCallExpression) {
|
||||
PsiMethodCallExpression call = (PsiMethodCallExpression)expression;
|
||||
if (ExpressionUtils.isVoidContext(call)) return false;
|
||||
PsiMethod method = call.resolveMethod();
|
||||
if (method == null || !JavaMethodContractUtil.isPure(method)) return false;
|
||||
List<? extends MethodContract> contracts = JavaMethodContractUtil.getMethodCallContracts(method, call);
|
||||
return CustomMethodHandlers.find(method) != null ||
|
||||
!contracts.isEmpty() &&
|
||||
contracts.stream().anyMatch(contract -> contract.getReturnValue().isBoolean() && !contract.isTrivial());
|
||||
}
|
||||
return false;
|
||||
myConstantExpressions.put(expression, nextState);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -302,10 +268,6 @@ final class DataFlowInstructionVisitor extends StandardInstructionVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean shouldReportConstValue(Object value) {
|
||||
return value == null || value instanceof Boolean;
|
||||
}
|
||||
|
||||
private static class StateInfo {
|
||||
boolean ephemeralNpe;
|
||||
boolean normalNpe;
|
||||
@@ -337,23 +299,37 @@ final class DataFlowInstructionVisitor extends StandardInstructionVisitor {
|
||||
myFailingCalls.put(call, DfaConstValue.isContractFail(myValue) && !Boolean.FALSE.equals(isFailing));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ConstantResult {
|
||||
TRUE, FALSE, NULL, UNKNOWN;
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public void visitReferenceExpression(PsiReferenceExpression expression) {
|
||||
super.visitReferenceExpression(expression);
|
||||
DfaConstValue oldValue = myValues.get(expression);
|
||||
if (DfaConstValue.isSentinel(oldValue)) return;
|
||||
if (myValue instanceof DfaVariableValue) {
|
||||
DfaConstValue constValue = myMemState.getConstantValue((DfaVariableValue)myValue);
|
||||
boolean report = constValue != null && shouldReportConstValue(constValue.getValue());
|
||||
if (!report) {
|
||||
constValue = null;
|
||||
}
|
||||
DfaConstValue newValue = constValue != null && (oldValue == null || oldValue == constValue)
|
||||
? constValue
|
||||
: myValue.getFactory().getConstFactory().getSentinel();
|
||||
myValues.put(expression, newValue);
|
||||
public String toString() {
|
||||
return name().toLowerCase(Locale.ENGLISH);
|
||||
}
|
||||
|
||||
public Object value() {
|
||||
switch (this) {
|
||||
case TRUE:
|
||||
return Boolean.TRUE;
|
||||
case FALSE:
|
||||
return Boolean.FALSE;
|
||||
case NULL:
|
||||
return null;
|
||||
default:
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
static ConstantResult fromConstValue(@NotNull DfaConstValue constant) {
|
||||
Object value = constant.getValue();
|
||||
if (value == null) return NULL;
|
||||
if (Boolean.TRUE.equals(value)) return TRUE;
|
||||
if (Boolean.FALSE.equals(value)) return FALSE;
|
||||
return UNKNOWN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-1
@@ -19,6 +19,7 @@ import com.intellij.codeInspection.dataFlow.value.DfaConstValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaPsiType;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -123,8 +124,15 @@ public interface DfaMemoryState {
|
||||
|
||||
boolean isNotNull(DfaValue dfaVar);
|
||||
|
||||
/**
|
||||
* Returns a constant value which equals to given value, if such.
|
||||
*
|
||||
* @param value a value to find a corresponding constant
|
||||
* @return found constant or null
|
||||
*/
|
||||
@Nullable
|
||||
DfaConstValue getConstantValue(@NotNull DfaVariableValue value);
|
||||
@Contract("null -> null")
|
||||
DfaConstValue getConstantValue(@Nullable DfaValue value);
|
||||
|
||||
/**
|
||||
* Ephemeral means a state that was created when considering a method contract and checking if one of its arguments is null.
|
||||
|
||||
+13
-5
@@ -36,6 +36,7 @@ import com.intellij.util.containers.Stack;
|
||||
import gnu.trove.TIntObjectHashMap;
|
||||
import gnu.trove.TIntObjectProcedure;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -581,7 +582,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
if (dfaVar instanceof DfaVariableValue) {
|
||||
if (getVariableState((DfaVariableValue)dfaVar).isNotNull()) return true;
|
||||
|
||||
DfaConstValue constantValue = getConstantValue((DfaVariableValue)dfaVar);
|
||||
DfaConstValue constantValue = getConstantValue(dfaVar);
|
||||
if (constantValue != null && constantValue.getValue() != null) return true;
|
||||
}
|
||||
|
||||
@@ -597,10 +598,17 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public DfaConstValue getConstantValue(@NotNull DfaVariableValue value) {
|
||||
int index = getEqClassIndex(value);
|
||||
EqClass ec = index == -1 ? null : myEqClasses.get(index);
|
||||
return ec == null ? null : (DfaConstValue)unwrap(ec.findConstant(true));
|
||||
@Contract("null -> null")
|
||||
public DfaConstValue getConstantValue(@Nullable DfaValue value) {
|
||||
if (value instanceof DfaConstValue) {
|
||||
return (DfaConstValue)value;
|
||||
}
|
||||
if (value instanceof DfaUnboxedValue || value instanceof DfaVariableValue) {
|
||||
int index = getEqClassIndex(value);
|
||||
EqClass ec = index == -1 ? null : myEqClasses.get(index);
|
||||
return ec == null ? null : (DfaConstValue)unwrap(ec.findConstant(true));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-3
@@ -196,9 +196,7 @@ public abstract class InstructionVisitor {
|
||||
|
||||
public DfaInstructionState[] visitObjectOfInstruction(ObjectOfInstruction instruction, DataFlowRunner runner, DfaMemoryState state) {
|
||||
DfaValue value = state.pop();
|
||||
DfaConstValue constant = value instanceof DfaConstValue ? (DfaConstValue)value :
|
||||
value instanceof DfaVariableValue ? state.getConstantValue((DfaVariableValue)value) :
|
||||
null;
|
||||
DfaConstValue constant = state.getConstantValue(value);
|
||||
PsiType type = constant == null ? null : ObjectUtils.tryCast(constant.getValue(), PsiType.class);
|
||||
state.push(runner.getFactory().createTypeValue(type, Nullability.NOT_NULL));
|
||||
return nextInstruction(instruction, runner, state);
|
||||
|
||||
+38
-14
@@ -2,14 +2,18 @@ package com.intellij.codeInspection.dataFlow;
|
||||
|
||||
import com.intellij.codeInspection.InspectionsBundle;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.siyeh.ig.psiutils.ExpressionUtils;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.PropertyKey;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static com.intellij.codeInspection.InspectionsBundle.BUNDLE;
|
||||
|
||||
@@ -18,14 +22,17 @@ import static com.intellij.codeInspection.InspectionsBundle.BUNDLE;
|
||||
* @param <T> a type of anchor element which could be associated with given nullability problem kind
|
||||
*/
|
||||
public class NullabilityProblemKind<T extends PsiElement> {
|
||||
private static final Function<Object, PsiExpression> DEFAULT_DEREFERENCED_ELEMENT_FN = e -> ObjectUtils.tryCast(e, PsiExpression.class);
|
||||
private final String myName;
|
||||
private final String myNullLiteralMessage;
|
||||
private final String myAlwaysNullMessage;
|
||||
private final String myNormalMessage;
|
||||
private final Function<? super T, ? extends PsiExpression> myDereferencedElementFunction;
|
||||
|
||||
private NullabilityProblemKind(@NotNull String name) {
|
||||
myName = name;
|
||||
myNullLiteralMessage = null;
|
||||
myAlwaysNullMessage = null;
|
||||
myNormalMessage = null;
|
||||
myDereferencedElementFunction = DEFAULT_DEREFERENCED_ELEMENT_FN;
|
||||
}
|
||||
|
||||
private NullabilityProblemKind(@NotNull String name, @NotNull @PropertyKey(resourceBundle = BUNDLE) String message) {
|
||||
@@ -33,22 +40,34 @@ public class NullabilityProblemKind<T extends PsiElement> {
|
||||
}
|
||||
|
||||
private NullabilityProblemKind(@NotNull String name,
|
||||
@NotNull @PropertyKey(resourceBundle = BUNDLE) String nullLiteralMessage,
|
||||
@NotNull @PropertyKey(resourceBundle = BUNDLE) String alwaysNullMessage,
|
||||
@NotNull @PropertyKey(resourceBundle = BUNDLE) String normalMessage) {
|
||||
myName = name;
|
||||
myNullLiteralMessage = InspectionsBundle.message(nullLiteralMessage);
|
||||
myNormalMessage = InspectionsBundle.message(normalMessage);
|
||||
this(name, alwaysNullMessage, normalMessage, DEFAULT_DEREFERENCED_ELEMENT_FN);
|
||||
}
|
||||
|
||||
public static final NullabilityProblemKind<PsiMethodCallExpression> callNPE = new NullabilityProblemKind<>("callNPE");
|
||||
private NullabilityProblemKind(@NotNull String name,
|
||||
@NotNull @PropertyKey(resourceBundle = BUNDLE) String alwaysNullMessage,
|
||||
@NotNull @PropertyKey(resourceBundle = BUNDLE) String normalMessage,
|
||||
Function<? super T, ? extends PsiExpression> dereferencedElementFunction) {
|
||||
myName = name;
|
||||
myAlwaysNullMessage = InspectionsBundle.message(alwaysNullMessage);
|
||||
myNormalMessage = InspectionsBundle.message(normalMessage);
|
||||
myDereferencedElementFunction = dereferencedElementFunction;
|
||||
}
|
||||
|
||||
public static final NullabilityProblemKind<PsiMethodCallExpression> callNPE =
|
||||
new NullabilityProblemKind<>("callNPE", "dataflow.message.npe.method.invocation.sure", "dataflow.message.npe.method.invocation",
|
||||
call -> call.getMethodExpression().getQualifierExpression());
|
||||
public static final NullabilityProblemKind<PsiMethodReferenceExpression> callMethodRefNPE =
|
||||
new NullabilityProblemKind<>("callMethodRefNPE", "dataflow.message.npe.methodref.invocation");
|
||||
public static final NullabilityProblemKind<PsiNewExpression> innerClassNPE =
|
||||
new NullabilityProblemKind<>("innerClassNPE", "dataflow.message.npe.inner.class.construction");
|
||||
new NullabilityProblemKind<>("innerClassNPE", "dataflow.message.npe.inner.class.construction.sure",
|
||||
"dataflow.message.npe.inner.class.construction", PsiNewExpression::getQualifier);
|
||||
public static final NullabilityProblemKind<PsiExpression> fieldAccessNPE =
|
||||
new NullabilityProblemKind<>("fieldAccessNPE", "dataflow.message.npe.field.access.sure", "dataflow.message.npe.field.access");
|
||||
public static final NullabilityProblemKind<PsiArrayAccessExpression> arrayAccessNPE =
|
||||
new NullabilityProblemKind<>("arrayAccessNPE", "dataflow.message.npe.array.access");
|
||||
new NullabilityProblemKind<>("arrayAccessNPE", "dataflow.message.npe.array.access.sure", "dataflow.message.npe.array.access",
|
||||
PsiArrayAccessExpression::getArrayExpression);
|
||||
public static final NullabilityProblemKind<PsiElement> unboxingNullable =
|
||||
new NullabilityProblemKind<>("unboxingNullable", "dataflow.message.unboxing");
|
||||
public static final NullabilityProblemKind<PsiExpression> assigningToNotNull =
|
||||
@@ -138,13 +157,18 @@ public class NullabilityProblemKind<T extends PsiElement> {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getMessage() {
|
||||
if (myKind.myNullLiteralMessage == null || myKind.myNormalMessage == null) {
|
||||
public String getMessage(Map<PsiExpression, DataFlowInstructionVisitor.ConstantResult> expressions) {
|
||||
if (myKind.myAlwaysNullMessage == null || myKind.myNormalMessage == null) {
|
||||
throw new IllegalStateException("This problem kind has no message associated: " + myKind);
|
||||
}
|
||||
return myAnchor instanceof PsiExpression && ExpressionUtils.isNullLiteral((PsiExpression)myAnchor)
|
||||
? myKind.myNullLiteralMessage
|
||||
: myKind.myNormalMessage;
|
||||
PsiExpression expression =
|
||||
PsiUtil.skipParenthesizedExprDown(ObjectUtils.tryCast(myKind.myDereferencedElementFunction.apply(myAnchor), PsiExpression.class));
|
||||
if (expression != null) {
|
||||
if (ExpressionUtils.isNullLiteral(expression) || expressions.get(expression) == DataFlowInstructionVisitor.ConstantResult.NULL) {
|
||||
return myKind.myAlwaysNullMessage;
|
||||
}
|
||||
}
|
||||
return myKind.myNormalMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-3
@@ -702,9 +702,7 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
boolean unknownTargetType = false;
|
||||
DfaValue condition = null;
|
||||
if (instruction.isClassObjectCheck()) {
|
||||
DfaConstValue constant = dfaRight instanceof DfaConstValue ? (DfaConstValue)dfaRight :
|
||||
dfaRight instanceof DfaVariableValue ? memState.getConstantValue((DfaVariableValue)dfaRight) :
|
||||
null;
|
||||
DfaConstValue constant = memState.getConstantValue(dfaRight);
|
||||
PsiType type = constant == null ? null : ObjectUtils.tryCast(constant.getValue(), PsiType.class);
|
||||
if (type == null || type instanceof PsiPrimitiveType) {
|
||||
// Unknown/primitive class: just execute contract "null -> false"
|
||||
|
||||
+4
-11
@@ -113,7 +113,10 @@ public class DfaConstValue extends DfaValue {
|
||||
|
||||
if (TypeConversionUtil.isNumericType(type) && !TypeConversionUtil.isFloatOrDoubleType(type)) {
|
||||
type = PsiType.LONG;
|
||||
value = TypeConversionUtil.computeCastTo(value, type);
|
||||
Object numeric = TypeConversionUtil.computeCastTo(value, type);
|
||||
if (numeric != null) {
|
||||
value = numeric;
|
||||
}
|
||||
}
|
||||
if (value instanceof Double || value instanceof Float) {
|
||||
double doubleValue = ((Number)value).doubleValue();
|
||||
@@ -202,14 +205,4 @@ public class DfaConstValue extends DfaValue {
|
||||
public static boolean isContractFail(DfaValue value) {
|
||||
return value instanceof DfaConstValue && ((DfaConstValue)value).getValue() == ourThrowable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether given value is a special internal sentinel value returned by {@link Factory#getSentinel()}.
|
||||
*
|
||||
* @param value value to check
|
||||
* @return true if specified value is a sentinel value
|
||||
*/
|
||||
public static boolean isSentinel(DfaValue value) {
|
||||
return value instanceof DfaConstValue && ((DfaConstValue)value).getValue() == SENTINEL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
<problem>
|
||||
<file>Test.java</file>
|
||||
<line>5</line>
|
||||
<description>Array access <code>path[0]</code> may produce <code>java.lang.NullPointerException</code></description>
|
||||
<description>Array access <code>path[0]</code> will produce <code>java.lang.NullPointerException</code></description>
|
||||
</problem>
|
||||
</problems>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<problem>
|
||||
<file>NullTest.java</file>
|
||||
<line>6</line>
|
||||
<description>Dereference of 't' may produce java.lang.NullPointerException</description>
|
||||
<description>Dereference of 't' will produce java.lang.NullPointerException</description>
|
||||
</problem>
|
||||
</problems>
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ class AdvancedArrayAccess {
|
||||
|
||||
void testMethodQualifier() {
|
||||
if(getData()[0] == null) {
|
||||
System.out.println(getData()[0].<warning descr="Method invocation 'trim' may produce 'java.lang.NullPointerException'">trim</warning>());
|
||||
System.out.println(getData()[0].<warning descr="Method invocation 'trim' will produce 'java.lang.NullPointerException'">trim</warning>());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ class S {
|
||||
|
||||
public void te3(boolean b){
|
||||
Boolean c = Boolean.FALSE;
|
||||
boolean o = !c;
|
||||
boolean o = <warning descr="Condition '!c' is always 'true'">!c</warning>;
|
||||
if (<warning descr="Condition 'o' is always 'true'">o</warning>) {
|
||||
}
|
||||
}
|
||||
@@ -41,13 +41,13 @@ class S {
|
||||
}
|
||||
public void te5(boolean b){
|
||||
Boolean c = Boolean.TRUE;
|
||||
boolean o = b||<warning descr="Condition 'c' is always 'true' when reached">c</warning>;
|
||||
boolean o = <warning descr="Condition 'b||c' is always 'true'">b||<warning descr="Condition 'c' is always 'true' when reached">c</warning></warning>;
|
||||
if (<warning descr="Condition 'o' is always 'true'">o</warning>) {
|
||||
}
|
||||
}
|
||||
public void te6(boolean b){
|
||||
Boolean c = Boolean.TRUE;
|
||||
boolean o = !c;
|
||||
boolean o = <warning descr="Condition '!c' is always 'false'">!c</warning>;
|
||||
<warning descr="Condition 'o' at the left side of assignment expression is always 'false'. Can be simplified">o</warning> |= c&b;
|
||||
if (o) {
|
||||
}
|
||||
@@ -57,7 +57,7 @@ class S {
|
||||
boolean o;
|
||||
{
|
||||
Boolean c = Boolean.FALSE;
|
||||
o = !c;
|
||||
o = <warning descr="Condition '!c' is always 'true'">!c</warning>;
|
||||
}
|
||||
if (<warning descr="Condition 'o' is always 'true'">o</warning>) {
|
||||
}
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ class Bar3 {
|
||||
System.out.println(first.hashCode());
|
||||
}
|
||||
if (first == null) {
|
||||
System.out.println(first.<warning descr="Method invocation 'hashCode' may produce 'java.lang.NullPointerException'">hashCode</warning>());
|
||||
System.out.println(first.<warning descr="Method invocation 'hashCode' will produce 'java.lang.NullPointerException'">hashCode</warning>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ class InitializerTest {
|
||||
z = "foo";
|
||||
}
|
||||
|
||||
boolean b = <warning descr="Condition 'z.startsWith(\"bar\")' is always 'false'">z.startsWith("bar")</warning>;
|
||||
boolean b = <warning descr="Result of 'z.startsWith(\"bar\")' is always 'false'">z.startsWith("bar")</warning>;
|
||||
|
||||
static final String ABC;
|
||||
static {
|
||||
|
||||
@@ -7,7 +7,7 @@ import java.lang.IllegalArgumentException;
|
||||
|
||||
class AssertIsNotNull {
|
||||
void bar(String s, String s1) {
|
||||
if (<warning descr="Condition 's == null && trimIfNotNull(s) != null' is always 'false'">s == null && <warning descr="Condition 'trimIfNotNull(s) != null' is always 'false' when reached">trimIfNotNull(s) != null</warning></warning>) {
|
||||
if (<warning descr="Condition 's == null && trimIfNotNull(s) != null' is always 'false'">s == null && <warning descr="Condition 'trimIfNotNull(s) != null' is always 'false' when reached"><warning descr="Result of 'trimIfNotNull(s)' is always 'null'">trimIfNotNull(s)</warning> != null</warning></warning>) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ class TestCase {
|
||||
String s = normalizeSpace(unknown()).trim();
|
||||
String s3 = normalizeSpaceInverted(unknown()).trim();
|
||||
|
||||
String s4 = normalizeSpace(null).<warning descr="Method invocation 'trim' may produce 'java.lang.NullPointerException'">trim</warning>();
|
||||
String s4 = normalizeSpace(null).<warning descr="Method invocation 'trim' will produce 'java.lang.NullPointerException'">trim</warning>();
|
||||
}
|
||||
|
||||
public static native String unknown();
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
class Foo {
|
||||
String field;
|
||||
String field2 = field.<warning descr="Method invocation 'substring' may produce 'java.lang.NullPointerException'">substring</warning>(1);
|
||||
String field2 = field.<warning descr="Method invocation 'substring' will produce 'java.lang.NullPointerException'">substring</warning>(1);
|
||||
int field3 = field2.length();
|
||||
Runnable r = new Runnable() {
|
||||
public void run() {
|
||||
@@ -74,7 +74,7 @@ class FieldInitNoLoop {
|
||||
|
||||
class NonFinalNotInitialized {
|
||||
String x;
|
||||
String y = x.<warning descr="Method invocation 'trim' may produce 'java.lang.NullPointerException'">trim</warning>();
|
||||
String y = x.<warning descr="Method invocation 'trim' will produce 'java.lang.NullPointerException'">trim</warning>();
|
||||
}
|
||||
|
||||
class NonFinalInitialized {
|
||||
@@ -87,7 +87,7 @@ class NonFinalInitialized {
|
||||
|
||||
class NonFinalAssignedInside {
|
||||
String x;
|
||||
String y = x.<warning descr="Method invocation 'trim' may produce 'java.lang.NullPointerException'">trim</warning>()+(x = " foo ").trim();
|
||||
String y = x.<warning descr="Method invocation 'trim' will produce 'java.lang.NullPointerException'">trim</warning>()+(x = " foo ").trim();
|
||||
String z = x.trim();
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -1,7 +1,7 @@
|
||||
class Foo {
|
||||
String field;
|
||||
String field2;
|
||||
int hash = field.<warning descr="Method invocation 'hashCode' may produce 'java.lang.NullPointerException'">hashCode</warning>();
|
||||
int hash = field.<warning descr="Method invocation 'hashCode' will produce 'java.lang.NullPointerException'">hashCode</warning>();
|
||||
|
||||
Foo(String f2) {
|
||||
field2 = f2;
|
||||
@@ -23,7 +23,7 @@ class Foo {
|
||||
|
||||
Instrumented() {
|
||||
System.out.println(s1.length()
|
||||
+s2.<warning descr="Method invocation 'length' may produce 'java.lang.NullPointerException'">length</warning>());
|
||||
+s2.<warning descr="Method invocation 'length' will produce 'java.lang.NullPointerException'">length</warning>());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,8 +32,8 @@ class Foo {
|
||||
String s2 = null;
|
||||
|
||||
NotInstrumented() {
|
||||
System.out.println(s1.<warning descr="Method invocation 'length' may produce 'java.lang.NullPointerException'">length</warning>()
|
||||
+s2.<warning descr="Method invocation 'length' may produce 'java.lang.NullPointerException'">length</warning>());
|
||||
System.out.println(s1.<warning descr="Method invocation 'length' will produce 'java.lang.NullPointerException'">length</warning>()
|
||||
+s2.<warning descr="Method invocation 'length' will produce 'java.lang.NullPointerException'">length</warning>());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ class LessThanRelations {
|
||||
void list(List<String> list, int index) {
|
||||
if(index >= list.size()) {
|
||||
System.out.println("Big index");
|
||||
} else if(index > 0 && <warning descr="Condition '!list.isEmpty()' is always 'true' when reached">!<warning descr="Condition 'list.isEmpty()' is always 'false' when reached">list.isEmpty()</warning></warning>) {
|
||||
} else if(index > 0 && <warning descr="Condition '!list.isEmpty()' is always 'true' when reached">!<warning descr="Result of 'list.isEmpty()' is always 'false'">list.isEmpty()</warning></warning>) {
|
||||
System.out.println("ok");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,8 +198,8 @@ public class LongRangeBasics {
|
||||
System.out.println(s2.trim());
|
||||
}
|
||||
if(code == 0) {
|
||||
System.out.println(s1.<warning descr="Method invocation 'trim' may produce 'java.lang.NullPointerException'">trim</warning>());
|
||||
System.out.println(s2.<warning descr="Method invocation 'trim' may produce 'java.lang.NullPointerException'">trim</warning>());
|
||||
System.out.println(s1.<warning descr="Method invocation 'trim' will produce 'java.lang.NullPointerException'">trim</warning>());
|
||||
System.out.println(s2.<warning descr="Method invocation 'trim' will produce 'java.lang.NullPointerException'">trim</warning>());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -162,8 +162,8 @@ public class LongRangeKnownMethods {
|
||||
|
||||
void testStringComparison(String name) {
|
||||
// Parentheses misplaced -- found in AndroidStudio
|
||||
if (!(name.equals("layout_width") && <warning descr="Condition '!(name.equals(\"layout_height\"))' is always 'true'">!(<warning descr="Condition 'name.equals(\"layout_height\")' is always 'false'">name.equals("layout_height")</warning>)</warning> &&
|
||||
<warning descr="Condition '!(name.equals(\"id\"))' is always 'true'">!(<warning descr="Condition 'name.equals(\"id\")' is always 'false'">name.equals("id")</warning>)</warning>)) {
|
||||
if (!(name.equals("layout_width") && <warning descr="Condition '!(name.equals(\"layout_height\"))' is always 'true'">!(<warning descr="Result of 'name.equals(\"layout_height\")' is always 'false'">name.equals("layout_height")</warning>)</warning> &&
|
||||
<warning descr="Condition '!(name.equals(\"id\"))' is always 'true'">!(<warning descr="Result of 'name.equals(\"id\")' is always 'false'">name.equals("id")</warning>)</warning>)) {
|
||||
System.out.println("ok");
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -6,7 +6,7 @@ class MergedInitializerAndConstructor {
|
||||
private Collection<Object> collection2 = null;
|
||||
|
||||
public Test1() {
|
||||
collection2.<warning descr="Method invocation 'add' may produce 'java.lang.NullPointerException'">add</warning>("");
|
||||
collection2.<warning descr="Method invocation 'add' will produce 'java.lang.NullPointerException'">add</warning>("");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ class MergedInitializerAndConstructor {
|
||||
}
|
||||
|
||||
{
|
||||
collection2.<warning descr="Method invocation 'add' may produce 'java.lang.NullPointerException'">add</warning>(""); //<- warning here
|
||||
collection2.<warning descr="Method invocation 'add' will produce 'java.lang.NullPointerException'">add</warning>(""); //<- warning here
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ class MergedInitializerAndConstructor {
|
||||
|
||||
public Test3(String s) {
|
||||
super();
|
||||
collection2.<warning descr="Method invocation 'add' may produce 'java.lang.NullPointerException'">add</warning>(s);
|
||||
collection2.<warning descr="Method invocation 'add' will produce 'java.lang.NullPointerException'">add</warning>(s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ public class MutabilityJdk {
|
||||
List<String> list3 = Collections.unmodifiableList(list2);
|
||||
if(<warning descr="Condition 'list1.isEmpty()' is always 'true'">list1.isEmpty()</warning>) System.out.println("ok");
|
||||
if(!list3.isEmpty()) return;
|
||||
if(<warning descr="Condition '!list3.isEmpty()' is always 'false'">!<warning descr="Condition 'list3.isEmpty()' is always 'true'">list3.isEmpty()</warning></warning>) return;
|
||||
if(<warning descr="Condition '!list3.isEmpty()' is always 'false'">!<warning descr="Result of 'list3.isEmpty()' is always 'true'">list3.isEmpty()</warning></warning>) return;
|
||||
list2.add("foo");
|
||||
// list1 size is not flushed (UNMODIFIABLE)
|
||||
if(<warning descr="Condition 'list1.isEmpty()' is always 'true'">list1.isEmpty()</warning>) System.out.println("ok");
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ class Foo {
|
||||
if (data != null) {
|
||||
return;
|
||||
}
|
||||
System.out.println(data.<warning descr="Method invocation 'hashCode' may produce 'java.lang.NullPointerException'">hashCode</warning>());
|
||||
System.out.println(data.<warning descr="Method invocation 'hashCode' will produce 'java.lang.NullPointerException'">hashCode</warning>());
|
||||
System.out.println(data.hashCode());
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ class Foo {
|
||||
if (data != null) {
|
||||
return;
|
||||
}
|
||||
System.out.println(data.<warning descr="Method invocation 'hashCode' may produce 'java.lang.NullPointerException'">hashCode</warning>());
|
||||
System.out.println(data.<warning descr="Method invocation 'hashCode' will produce 'java.lang.NullPointerException'">hashCode</warning>());
|
||||
System.out.println(data.hashCode());
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ public class OptionalInlining {
|
||||
|
||||
void testDeref(Optional<String> opt) {
|
||||
if (opt == null) {
|
||||
System.out.println(opt.<warning descr="Method invocation 'orElse' may produce 'java.lang.NullPointerException'">orElse</warning>("qq"));
|
||||
System.out.println(opt.<warning descr="Method invocation 'orElse' will produce 'java.lang.NullPointerException'">orElse</warning>("qq"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ public class OptionalInlining {
|
||||
}
|
||||
return "baz";
|
||||
});
|
||||
if (<warning descr="Condition 's.equals(\"bar\") && !opt.isPresent()' is always 'false'">s.equals("bar") && <warning descr="Condition '!opt.isPresent()' is always 'false' when reached">!<warning descr="Condition 'opt.isPresent()' is always 'true' when reached">opt.isPresent()</warning></warning></warning>) {
|
||||
if (<warning descr="Condition 's.equals(\"bar\") && !opt.isPresent()' is always 'false'">s.equals("bar") && <warning descr="Condition '!opt.isPresent()' is always 'false' when reached">!<warning descr="Result of 'opt.isPresent()' is always 'true'">opt.isPresent()</warning></warning></warning>) {
|
||||
System.out.println("Impossible");
|
||||
}
|
||||
}
|
||||
@@ -87,7 +87,7 @@ public class OptionalInlining {
|
||||
if (abc.equals("xyz") && <warning descr="Condition 'opt.isPresent()' is always 'true' when reached">opt.isPresent()</warning>) {
|
||||
System.out.println("always");
|
||||
}
|
||||
opt.filter(x -> x.length() > 5).filter(x -> <warning descr="Condition 'x.isEmpty()' is always 'false'">x.isEmpty()</warning>).ifPresent(x -> System.out.println(x));
|
||||
opt.filter(x -> x.length() > 5).filter(x -> <warning descr="Result of 'x.isEmpty()' is always 'false'">x.isEmpty()</warning>).ifPresent(x -> System.out.println(x));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -111,7 +111,7 @@ public class OptionalInlining {
|
||||
void testMap(Optional<String> opt) {
|
||||
opt.map(<warning descr="Passing 'null' argument to parameter annotated as @NotNull">null</warning>);
|
||||
String res = opt.<String>map(s -> null).orElse("abc");
|
||||
if (<warning descr="Condition '!res.equals(\"abc\")' is always 'false'">!<warning descr="Condition 'res.equals(\"abc\")' is always 'true'">res.equals("abc")</warning></warning>) {
|
||||
if (<warning descr="Condition '!res.equals(\"abc\")' is always 'false'">!<warning descr="Result of 'res.equals(\"abc\")' is always 'true'">res.equals("abc")</warning></warning>) {
|
||||
System.out.println("Never");
|
||||
}
|
||||
String trimmed = Optional.ofNullable(nullableMethod()).map(xx -> xx.trim()).orElse("");
|
||||
@@ -170,7 +170,7 @@ public class OptionalInlining {
|
||||
if (<warning descr="Condition 's.equals(\"qux\")' is always 'false'">s.equals("qux")</warning>) {
|
||||
System.out.println("Never");
|
||||
}
|
||||
boolean res = <warning descr="Condition 'opt.filter(x -> x.isEmpty()).flatMap(x -> x.length() <= 2 ? Optional.empty() : Optional.of(\"foo\")) ...' is always 'false'">opt.filter(x -> x.isEmpty()).flatMap(x -> <warning descr="Condition 'x.length() <= 2' is always 'true'">x.length() <= 2</warning> ? Optional.empty() : Optional.of("foo"))
|
||||
boolean res = <warning descr="Result of 'opt.filter(x -> x.isEmpty()).flatMap(x -> x.length() <= 2 ? Optional.empty() : Optional.of(\"foo\")) ...' is always 'false'">opt.filter(x -> x.isEmpty()).flatMap(x -> <warning descr="Condition 'x.length() <= 2' is always 'true'">x.length() <= 2</warning> ? Optional.empty() : Optional.of("foo"))
|
||||
.isPresent()</warning>;
|
||||
}
|
||||
|
||||
@@ -214,14 +214,14 @@ public class OptionalInlining {
|
||||
}
|
||||
|
||||
void testFilterChain(Optional<Holder> opt) {
|
||||
boolean present = <warning descr="Condition 'opt .filter(h -> h.x < 5) .filter(h -> h.x > 6) .map(h -> h.x).isPresent()' is always 'false'">opt
|
||||
boolean present = <warning descr="Result of 'opt .filter(h -> h.x < 5) .filter(h -> h.x > 6) .map(h -> h.x).isPresent()' is always 'false'">opt
|
||||
.filter(h -> h.x < 5)
|
||||
.filter(h -> <warning descr="Condition 'h.x > 6' is always 'false'">h.x > 6</warning>)
|
||||
.map(h -> h.x).isPresent()</warning>;
|
||||
}
|
||||
|
||||
void testFilterMap(Optional<Holder> opt) {
|
||||
boolean present = <warning descr="Condition 'opt .filter(h -> h.s == null) .map(h -> h.s) .isPresent()' is always 'false'">opt
|
||||
boolean present = <warning descr="Result of 'opt .filter(h -> h.s == null) .map(h -> h.s) .isPresent()' is always 'false'">opt
|
||||
.filter(h -> h.s == null)
|
||||
.map(h -> h.s)
|
||||
.isPresent()</warning>;
|
||||
|
||||
@@ -7,7 +7,7 @@ class Test {
|
||||
test = Optional.of("x");
|
||||
} else {
|
||||
test = Optional.empty();
|
||||
if(<warning descr="Condition '!test.isPresent()' is always 'true'">!<warning descr="Condition 'test.isPresent()' is always 'false'">test.isPresent()</warning></warning>) {
|
||||
if(<warning descr="Condition '!test.isPresent()' is always 'true'">!<warning descr="Result of 'test.isPresent()' is always 'false'">test.isPresent()</warning></warning>) {
|
||||
System.out.println("Always");
|
||||
}
|
||||
}
|
||||
@@ -30,8 +30,8 @@ class Test {
|
||||
System.out.println(maybe.<warning descr="The call to 'get' always fails, according to its method contracts">get</warning>());
|
||||
}
|
||||
boolean b = <warning descr="Condition '((maybe.isPresent())) && maybe.get() == 1' is always 'false'">((<warning descr="Condition 'maybe.isPresent()' is always 'false'">maybe.isPresent()</warning>)) && maybe.get() == 1</warning>;
|
||||
boolean c = <warning descr="Condition '(!maybe.isPresent()) || maybe.get() == 1' is always 'true'">(<warning descr="Condition '!maybe.isPresent()' is always 'true'">!<warning descr="Condition 'maybe.isPresent()' is always 'false'">maybe.isPresent()</warning></warning>) || maybe.get() == 1</warning>;
|
||||
Integer value = <warning descr="Condition '!maybe.isPresent()' is always 'true'">!<warning descr="Condition 'maybe.isPresent()' is always 'false'">maybe.isPresent()</warning></warning> ? 0 : maybe.get();
|
||||
boolean c = <warning descr="Condition '(!maybe.isPresent()) || maybe.get() == 1' is always 'true'">(<warning descr="Condition '!maybe.isPresent()' is always 'true'">!<warning descr="Result of 'maybe.isPresent()' is always 'false'">maybe.isPresent()</warning></warning>) || maybe.get() == 1</warning>;
|
||||
Integer value = <warning descr="Condition '!maybe.isPresent()' is always 'true'">!<warning descr="Result of 'maybe.isPresent()' is always 'false'">maybe.isPresent()</warning></warning> ? 0 : maybe.get();
|
||||
}
|
||||
|
||||
Optional<Integer> getIntegerOptional() {
|
||||
@@ -40,7 +40,7 @@ class Test {
|
||||
|
||||
private static void a() {
|
||||
Optional<String> optional = Optional.empty();
|
||||
final boolean present = <warning descr="Condition 'optional.isPresent()' is always 'false'">optional.isPresent()</warning>;
|
||||
final boolean present = <warning descr="Result of 'optional.isPresent()' is always 'false'">optional.isPresent()</warning>;
|
||||
// optional = Optional.empty();
|
||||
if (<warning descr="Condition 'present' is always 'false'">present</warning>) {
|
||||
final String string = optional.get();
|
||||
@@ -50,7 +50,7 @@ class Test {
|
||||
|
||||
private static void b() {
|
||||
Optional<String> optional = Optional.empty();
|
||||
final boolean present = <warning descr="Condition 'optional.isPresent()' is always 'false'">optional.isPresent()</warning>;
|
||||
final boolean present = <warning descr="Result of 'optional.isPresent()' is always 'false'">optional.isPresent()</warning>;
|
||||
optional = Optional.empty();
|
||||
if (<warning descr="Condition 'present' is always 'false'">present</warning>) {
|
||||
final String string = optional.get();
|
||||
@@ -81,7 +81,7 @@ class Test {
|
||||
|
||||
private void checkAsserts2() {
|
||||
Optional<String> o3 = Optional.empty();
|
||||
org.testng.Assert.<warning descr="The call to 'assertTrue' always fails, according to its method contracts">assertTrue</warning>(<warning descr="Condition 'o3.isPresent()' is always 'false'">o3.isPresent()</warning>);
|
||||
org.testng.Assert.<warning descr="The call to 'assertTrue' always fails, according to its method contracts">assertTrue</warning>(<warning descr="Result of 'o3.isPresent()' is always 'false'">o3.isPresent()</warning>);
|
||||
System.out.println(o3.get());
|
||||
}
|
||||
|
||||
@@ -100,9 +100,9 @@ class Test {
|
||||
public static String demo() {
|
||||
Optional<String> holder = Optional.empty();
|
||||
|
||||
if (<warning descr="Condition '! holder.isPresent()' is always 'true'">! <warning descr="Condition 'holder.isPresent()' is always 'false'">holder.isPresent()</warning></warning>) {
|
||||
if (<warning descr="Condition '! holder.isPresent()' is always 'true'">! <warning descr="Result of 'holder.isPresent()' is always 'false'">holder.isPresent()</warning></warning>) {
|
||||
holder = Optional.of("hello world");
|
||||
if (<warning descr="Condition '!holder.isPresent()' is always 'false'">!<warning descr="Condition 'holder.isPresent()' is always 'true'">holder.isPresent()</warning></warning>) {
|
||||
if (<warning descr="Condition '!holder.isPresent()' is always 'false'">!<warning descr="Result of 'holder.isPresent()' is always 'true'">holder.isPresent()</warning></warning>) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import org.jetbrains.annotations.*;
|
||||
import java.util.List;
|
||||
|
||||
class Test {
|
||||
static final Object RES = null;
|
||||
final Object xyz = null;
|
||||
static final Integer TEST = new Integer(0);
|
||||
|
||||
void test() {
|
||||
doSmth(RES);
|
||||
doSmth(<weak_warning descr="Value 'xyz' is always 'null'">xyz</weak_warning>);
|
||||
System.out.println(process(<weak_warning descr="Value 'xyz' is always 'null'">xyz</weak_warning>).<warning descr="Method invocation 'hashCode' will produce 'java.lang.NullPointerException'">hashCode</warning>());
|
||||
Integer x = TEST;
|
||||
System.out.println(TEST);
|
||||
System.out.println(x);
|
||||
}
|
||||
|
||||
@Contract("null -> null")
|
||||
native Object process(@Nullable Object obj);
|
||||
|
||||
native void doSmth(@Nullable Object obj);
|
||||
}
|
||||
@@ -20,7 +20,7 @@ class Test {
|
||||
|
||||
public void testDontReplaceQualifierWithNull(Object bar) {
|
||||
if (bar == null) {
|
||||
bar.<warning descr="Method invocation 'hashCode' may produce 'java.lang.NullPointerException'">hashCode</warning>();
|
||||
bar.<warning descr="Method invocation 'hashCode' will produce 'java.lang.NullPointerException'">hashCode</warning>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,11 +12,11 @@ public class StreamInlining {
|
||||
list.stream().flatMap(<warning descr="Passing 'null' argument to parameter annotated as @NotNull">null</warning>).forEach(System.out::println);
|
||||
list.stream().filter(x -> x != null).forEach(<warning descr="Passing 'null' argument to parameter annotated as @NotNull">null</warning>);
|
||||
List<String> l = null;
|
||||
l.<warning descr="Method invocation 'stream' may produce 'java.lang.NullPointerException'">stream</warning>().count();
|
||||
l.<warning descr="Method invocation 'stream' will produce 'java.lang.NullPointerException'">stream</warning>().count();
|
||||
int[] arr = null;
|
||||
Arrays.stream(<warning descr="Argument 'arr' might be null">arr</warning>).count();
|
||||
Stream<String> stream = null;
|
||||
stream.<warning descr="Method invocation 'filter' may produce 'java.lang.NullPointerException'">filter</warning>(x -> x != null).forEach(System.out::println);
|
||||
stream.<warning descr="Method invocation 'filter' will produce 'java.lang.NullPointerException'">filter</warning>(x -> x != null).forEach(System.out::println);
|
||||
}
|
||||
|
||||
void testMethodRef(List<String> list, int[] data) {
|
||||
@@ -125,10 +125,10 @@ public class StreamInlining {
|
||||
|
||||
boolean flatMap(List<String> list, List<List<String>> ll) {
|
||||
System.out.println(ll.stream().flatMap(l -> l.stream()).count());
|
||||
return list.stream().map(s -> s.isEmpty() ? null : s)
|
||||
return <warning descr="Result of 'list.stream().map(s -> s.isEmpty() ? null : s) .flatMap(s -> Stream.of(s, s.trim()) ...' is always 'false'">list.stream().map(s -> s.isEmpty() ? null : s)
|
||||
.flatMap(s -> Stream.of(s, s.<warning descr="Method invocation 'trim' may produce 'java.lang.NullPointerException'">trim</warning>())
|
||||
.filter(r -> <warning descr="Condition 'r != null' is always 'true'">r != null</warning>))
|
||||
.anyMatch(x -> <warning descr="Condition 'x == null' is always 'false'">x == null</warning>);
|
||||
.anyMatch(x -> <warning descr="Condition 'x == null' is always 'false'">x == null</warning>)</warning>;
|
||||
}
|
||||
|
||||
String blockLambda(List<String> list) {
|
||||
@@ -169,7 +169,7 @@ public class StreamInlining {
|
||||
|
||||
void testGenerate() {
|
||||
List<String> list1 = Stream.generate(() -> Math.random() > 0.5 ? "foo" : "baz")
|
||||
.limit(10).filter((xyz -> <warning descr="Condition '\"bar\".equals(xyz)' is always 'false'">"bar".equals(xyz)</warning>)).collect(Collectors.toList());
|
||||
.limit(10).filter((xyz -> <warning descr="Result of '\"bar\".equals(xyz)' is always 'false'">"bar".equals(xyz)</warning>)).collect(Collectors.toList());
|
||||
List<String> list2 = Stream.generate(() -> "xyz").limit(20).filter(<warning descr="Method reference result is always 'false'">"bar"::equals</warning>).collect(Collectors.toList());
|
||||
Stream.generate(() -> Optional.of("xyz")).filter(<warning descr="Method reference result is always 'true'">Optional::isPresent</warning>).forEach(System.out::println);
|
||||
LongStream.generate(() -> 5).limit(10).filter(x -> <warning descr="Condition 'x > 6' is always 'false'">x > 6</warning>).forEach(s -> System.out.println(s));
|
||||
|
||||
@@ -38,15 +38,15 @@ public class StreamKnownSource {
|
||||
System.out.println("Probably");
|
||||
}
|
||||
|
||||
boolean emptyAll = Stream.empty().allMatch(Objects::nonNull);
|
||||
boolean emptyAll = <warning descr="Result of 'Stream.empty().allMatch(Objects::nonNull)' is always 'true'">Stream.empty().allMatch(Objects::nonNull)</warning>;
|
||||
if (<warning descr="Condition 'emptyAll' is always 'true'">emptyAll</warning>) {
|
||||
System.out.println("True");
|
||||
}
|
||||
boolean emptyAny = Stream.empty().anyMatch(Objects::nonNull);
|
||||
boolean emptyAny = <warning descr="Result of 'Stream.empty().anyMatch(Objects::nonNull)' is always 'false'">Stream.empty().anyMatch(Objects::nonNull)</warning>;
|
||||
if (<warning descr="Condition 'emptyAny' is always 'false'">emptyAny</warning>) {
|
||||
System.out.println("False");
|
||||
}
|
||||
boolean emptyNone = Stream.empty().noneMatch(Objects::nonNull);
|
||||
boolean emptyNone = <warning descr="Result of 'Stream.empty().noneMatch(Objects::nonNull)' is always 'true'">Stream.empty().noneMatch(Objects::nonNull)</warning>;
|
||||
if (<warning descr="Condition 'emptyNone' is always 'true'">emptyNone</warning>) {
|
||||
System.out.println("True");
|
||||
}
|
||||
@@ -119,7 +119,7 @@ public class StreamKnownSource {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean hasNoNulls = list.stream().allMatch(Objects::nonNull);
|
||||
boolean hasNoNulls = <warning descr="Result of 'list.stream().allMatch(Objects::nonNull)' is always 'true'">list.stream().allMatch(Objects::nonNull)</warning>;
|
||||
|
||||
if(<warning descr="Condition 'hasNoNulls' is always 'true'">hasNoNulls</warning>) {
|
||||
System.out.println("Always");
|
||||
|
||||
@@ -93,7 +93,7 @@ class ThisAsVariable {
|
||||
} else {
|
||||
Runnable r = new Runnable() {
|
||||
public void run() {
|
||||
System.out.println(s.<warning descr="Method invocation 'trim' may produce 'java.lang.NullPointerException'">trim</warning>());
|
||||
System.out.println(s.<warning descr="Method invocation 'trim' will produce 'java.lang.NullPointerException'">trim</warning>());
|
||||
}
|
||||
};
|
||||
r.run();
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ class Test {
|
||||
|
||||
void m1() throws Exception {
|
||||
MyResourceProvider provider = null;
|
||||
try (MyResource r = provider.<warning descr="Method invocation 'getResource' may produce 'java.lang.NullPointerException'">getResource</warning>()) {
|
||||
try (MyResource r = provider.<warning descr="Method invocation 'getResource' will produce 'java.lang.NullPointerException'">getResource</warning>()) {
|
||||
System.out.println(r);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ class Test {
|
||||
|
||||
private void method() {
|
||||
if (timeStamp == null) {
|
||||
timeStamp.<warning descr="Method invocation 'set' may produce 'java.lang.NullPointerException'">set</warning>(Calendar.getInstance().getTimeInMillis()); // not reported
|
||||
timeStamp.<warning descr="Method invocation 'set' will produce 'java.lang.NullPointerException'">set</warning>(Calendar.getInstance().getTimeInMillis()); // not reported
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ class Some {
|
||||
while (parent != null) {
|
||||
parent = parent.getParentFile();
|
||||
}
|
||||
System.out.println(parent.<warning descr="Method invocation 'getName' may produce 'java.lang.NullPointerException'">getName</warning>());
|
||||
System.out.println(parent.<warning descr="Method invocation 'getName' will produce 'java.lang.NullPointerException'">getName</warning>());
|
||||
}
|
||||
System.out.println(file.getName());
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<problem>
|
||||
<file>Test.java</file>
|
||||
<line>6</line>
|
||||
<description>'equals' may produce NullPointerException</description>
|
||||
<description>'equals' will produce NullPointerException</description>
|
||||
</problem>
|
||||
</problems>
|
||||
|
||||
|
||||
@@ -625,6 +625,12 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase {
|
||||
public void testBoxingBoolean() { doTest(); }
|
||||
public void testOrWithAssignment() { doTest(); }
|
||||
public void testAndAndLastOperand() { doTest(); }
|
||||
public void testReportAlwaysNull() {
|
||||
DataFlowInspection inspection = new DataFlowInspection();
|
||||
inspection.REPORT_CONSTANT_REFERENCE_VALUES = true;
|
||||
myFixture.enableInspections(inspection);
|
||||
myFixture.testHighlighting(true, false, true, getTestName(false) + ".java");
|
||||
}
|
||||
public void testAndAndWithOr() { doTest(); }
|
||||
public void testBoxUnboxArrayElement() { doTest(); }
|
||||
public void testExactInstanceOf() { doTest(); }
|
||||
|
||||
@@ -56,9 +56,12 @@ configure.annotations.option=Configure annotations
|
||||
|
||||
#messages from dataflow inspection
|
||||
dataflow.message.npe.method.invocation=Method invocation <code>#ref</code> #loc may produce <code>java.lang.NullPointerException</code>
|
||||
dataflow.message.npe.method.invocation.sure=Method invocation <code>#ref</code> #loc will produce <code>java.lang.NullPointerException</code>
|
||||
dataflow.message.npe.inner.class.construction=Inner class construction may produce <code>java.lang.NullPointerException</code>
|
||||
dataflow.message.npe.inner.class.construction.sure=Inner class construction will produce <code>java.lang.NullPointerException</code>
|
||||
dataflow.message.npe.methodref.invocation=Method reference invocation <code>#ref</code> #loc may produce <code>java.lang.NullPointerException</code>
|
||||
dataflow.message.npe.array.access=Array access <code>#ref</code> #loc may produce <code>java.lang.NullPointerException</code>
|
||||
dataflow.message.npe.array.access.sure=Array access <code>#ref</code> #loc will produce <code>java.lang.NullPointerException</code>
|
||||
dataflow.message.npe.field.access.sure=Dereference of <code>#ref</code> #loc will produce <code>java.lang.NullPointerException</code>
|
||||
dataflow.message.npe.field.access=Dereference of <code>#ref</code> #loc may produce <code>java.lang.NullPointerException</code>
|
||||
dataflow.message.cce=Casting <code>{0}</code> to <code>#ref</code> #loc may produce <code>java.lang.ClassCastException</code>
|
||||
|
||||
Reference in New Issue
Block a user