mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
DataFlowInspection: detect possible ArrayStoreException
This commit is contained in:
+20
@@ -305,6 +305,8 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
|
||||
reportArrayAccessProblems(holder, visitor);
|
||||
|
||||
reportArrayStoreProblems(holder, visitor);
|
||||
|
||||
if (REPORT_CONSTANT_REFERENCE_VALUES) {
|
||||
reportConstantReferenceValues(holder, visitor, reportedAnchors);
|
||||
}
|
||||
@@ -323,6 +325,12 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
});
|
||||
}
|
||||
|
||||
private static void reportArrayStoreProblems(ProblemsHolder holder, DataFlowInstructionVisitor visitor) {
|
||||
visitor.getArrayStoreProblems().forEach(
|
||||
(assignment, types) -> holder.registerProblem(assignment.getOperationSign(), InspectionsBundle
|
||||
.message("dataflow.message.arraystore", types.getFirst().getCanonicalText(), types.getSecond().getCanonicalText())));
|
||||
}
|
||||
|
||||
private void reportMethodReferenceProblems(ProblemsHolder holder, DataFlowInstructionVisitor visitor) {
|
||||
visitor.getMethodReferenceResults().forEach((methodRef, dfaValue) -> {
|
||||
if (dfaValue instanceof DfaConstValue) {
|
||||
@@ -915,6 +923,7 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
private final Map<PsiMethodCallExpression, ThreeState> myOptionalCalls = new HashMap<>();
|
||||
private final Map<PsiMethodCallExpression, ThreeState> myBooleanCalls = new HashMap<>();
|
||||
private final Map<MethodCallInstruction, 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 List<PsiExpression> myOptionalQualifiers = new ArrayList<>();
|
||||
@@ -935,6 +944,10 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
});
|
||||
}
|
||||
|
||||
public Map<PsiAssignmentExpression, Pair<PsiType, PsiType>> getArrayStoreProblems() {
|
||||
return myArrayStoreProblems;
|
||||
}
|
||||
|
||||
Map<PsiMethodCallExpression, ThreeState> getOptionalCalls() {
|
||||
return myOptionalCalls;
|
||||
}
|
||||
@@ -1032,6 +1045,13 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
myOutOfBoundsArrayAccesses.merge(expression, ThreeState.fromBoolean(alwaysOutOfBounds), ThreeState::merge);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processArrayStoreTypeMismatch(PsiAssignmentExpression assignmentExpression, PsiType fromType, PsiType toType) {
|
||||
if (assignmentExpression != null) {
|
||||
myArrayStoreProblems.put(assignmentExpression, Pair.create(fromType, toType));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processMethodReferenceResult(PsiMethodReferenceExpression methodRef,
|
||||
List<? extends MethodContract> contracts,
|
||||
|
||||
+46
-9
@@ -23,10 +23,7 @@ import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.InheritanceUtil;
|
||||
import com.intellij.psi.util.MethodSignatureUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.intellij.psi.util.*;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
@@ -72,8 +69,16 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
dfaDest = instruction.getAssignedValue();
|
||||
}
|
||||
|
||||
NullabilityProblem problem = PsiUtil.skipParenthesizedExprDown(instruction.getLExpression()) instanceof PsiArrayAccessExpression ?
|
||||
NullabilityProblem.storingToNotNullArray : NullabilityProblem.assigningToNotNull;
|
||||
PsiExpression lValue = PsiUtil.skipParenthesizedExprDown(instruction.getLExpression());
|
||||
PsiExpression rValue = instruction.getRExpression();
|
||||
NullabilityProblem problem;
|
||||
if (lValue instanceof PsiArrayAccessExpression) {
|
||||
problem = NullabilityProblem.storingToNotNullArray;
|
||||
checkArrayElementAssignability(runner, memState, dfaSource, lValue, rValue);
|
||||
}
|
||||
else {
|
||||
problem = NullabilityProblem.assigningToNotNull;
|
||||
}
|
||||
|
||||
if (dfaDest instanceof DfaVariableValue) {
|
||||
DfaVariableValue var = (DfaVariableValue) dfaDest;
|
||||
@@ -81,7 +86,7 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
final PsiModifierListOwner psi = var.getPsiVariable();
|
||||
boolean forceDeclaredNullity = !(psi instanceof PsiParameter && psi.getParent() instanceof PsiParameterList);
|
||||
if (forceDeclaredNullity && var.getInherentNullability() == Nullness.NOT_NULL) {
|
||||
checkNotNullable(memState, dfaSource, problem, instruction.getRExpression());
|
||||
checkNotNullable(memState, dfaSource, problem, rValue);
|
||||
}
|
||||
if (!(psi instanceof PsiField) || !psi.hasModifierProperty(PsiModifier.VOLATILE)) {
|
||||
memState.setVarValue(var, dfaSource);
|
||||
@@ -90,9 +95,8 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
DfaMemoryStateImpl stateImpl = (DfaMemoryStateImpl)memState;
|
||||
stateImpl.setVariableState(var, stateImpl.getVariableState(var).withFact(DfaFactType.CAN_BE_NULL, true));
|
||||
}
|
||||
|
||||
} else if (dfaDest instanceof DfaTypeValue && ((DfaTypeValue)dfaDest).isNotNull()) {
|
||||
checkNotNullable(memState, dfaSource, problem, instruction.getRExpression());
|
||||
checkNotNullable(memState, dfaSource, problem, rValue);
|
||||
}
|
||||
|
||||
memState.push(dfaDest);
|
||||
@@ -100,6 +104,39 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
return nextInstruction(instruction, runner, memState);
|
||||
}
|
||||
|
||||
private void checkArrayElementAssignability(DataFlowRunner runner,
|
||||
DfaMemoryState memState,
|
||||
DfaValue dfaSource,
|
||||
PsiExpression lValue,
|
||||
PsiExpression rValue) {
|
||||
if (rValue == null) return;
|
||||
PsiType rCodeType = rValue.getType();
|
||||
PsiType lCodeType = lValue.getType();
|
||||
// If types known from source are not convertible, a compilation error is displayed, additional warning is unnecessary
|
||||
if (rCodeType == null || lCodeType == null || !TypeConversionUtil.areTypesConvertible(rCodeType, lCodeType)) return;
|
||||
PsiExpression array = ((PsiArrayAccessExpression)lValue).getArrayExpression();
|
||||
DfaValue arrayValue = runner.getFactory().createValue(array);
|
||||
PsiType arrayType = getType(array, arrayValue, memState);
|
||||
if (!(arrayType instanceof PsiArrayType)) return;
|
||||
PsiType componentType = ((PsiArrayType)arrayType).getComponentType();
|
||||
PsiType sourceType = getType(rValue, dfaSource, memState);
|
||||
if (sourceType == null || TypeConversionUtil.areTypesConvertible(sourceType, componentType)) return;
|
||||
PsiAssignmentExpression assignmentExpression =
|
||||
PsiTreeUtil.getParentOfType(rValue, PsiAssignmentExpression.class);
|
||||
processArrayStoreTypeMismatch(assignmentExpression, sourceType, componentType);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiType getType(@Nullable PsiExpression expression, @Nullable DfaValue value, @NotNull DfaMemoryState memState) {
|
||||
TypeConstraint fact = value == null ? null : memState.getValueFact(DfaFactType.TYPE_CONSTRAINT, value);
|
||||
PsiType type = fact == null ? null : fact.getPsiType();
|
||||
if (type != null) return type;
|
||||
return expression == null ? null : expression.getType();
|
||||
}
|
||||
|
||||
protected void processArrayStoreTypeMismatch(PsiAssignmentExpression assignmentExpression, PsiType fromType, PsiType toType) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DfaInstructionState[] visitCheckReturnValue(CheckReturnValueInstruction instruction,
|
||||
DataFlowRunner runner,
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
class ArrayStoreProblems {
|
||||
void test(String[] args, Integer[] args2) {
|
||||
Object[] arr = args;
|
||||
arr[0] <warning descr="Storing element of type 'int' to array of 'java.lang.String' elements may produce 'java.lang.ArrayStoreException'">=</warning> 123;
|
||||
arr = args2;
|
||||
arr[1] = 124;
|
||||
arr[2] <warning descr="Storing element of type 'java.lang.String' to array of 'java.lang.Integer' elements may produce 'java.lang.ArrayStoreException'">=</warning> "foo";
|
||||
arr = args;
|
||||
arr[3] = "bar";
|
||||
arr[4] <warning descr="Storing element of type 'int' to array of 'java.lang.String' elements may produce 'java.lang.ArrayStoreException'">=</warning> 125;
|
||||
}
|
||||
}
|
||||
@@ -548,4 +548,5 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase {
|
||||
public void testGetterOfNullableFieldIsNotNull() { doTest(); }
|
||||
|
||||
public void testTryWithResourceExpressions() { doTest(); }
|
||||
public void testArrayStoreProblems() { doTest(); }
|
||||
}
|
||||
|
||||
@@ -78,6 +78,7 @@ dataflow.message.npe.array.access=Array access <code>#ref</code> #loc may produc
|
||||
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>
|
||||
dataflow.message.arraystore=Storing element of type <code>{0}</code> to array of <code>{1}</code> elements may produce <code>java.lang.ArrayStoreException</code>
|
||||
dataflow.message.redundant.instanceof=Condition <code>#ref</code> #loc is redundant and can be replaced with <code>!= null</code>
|
||||
dataflow.message.constant.condition=Condition <code>#ref</code> #loc is always <code>{0}</code>
|
||||
dataflow.message.constant.condition.when.reached=Condition <code>#ref</code> #loc is always <code>{0}</code> when reached
|
||||
|
||||
Reference in New Issue
Block a user