mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-21 05:51:25 +07:00
[java-dfa] Do not report array as immutable if it's passed to pure methods that return unmodifiable object
The object may be only shallowly unmodifiable GitOrigin-RevId: 41c0a0b46fc80169ff334d6337d9f8708e689448
This commit is contained in:
committed by
intellij-monorepo-bot
parent
a7a884760f
commit
a26d25a99f
@@ -7,7 +7,6 @@ import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightUtil;
|
||||
import com.intellij.codeInspection.dataFlow.ContractReturnValue;
|
||||
import com.intellij.codeInspection.dataFlow.JavaMethodContractUtil;
|
||||
import com.intellij.codeInspection.dataFlow.Mutability;
|
||||
import com.intellij.codeInspection.dataFlow.MutationSignature;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
@@ -1290,10 +1289,17 @@ public final class ExpressionUtils {
|
||||
if (array instanceof PsiField && !(array.hasModifierProperty(PsiModifier.PRIVATE) && array.hasModifierProperty(PsiModifier.STATIC))) {
|
||||
return null;
|
||||
}
|
||||
Boolean isConstantArray = CachedValuesManager.<Boolean>getCachedValue(array, () -> CachedValueProvider.Result
|
||||
.create(isConstantArray(array), PsiModificationTracker.MODIFICATION_COUNT));
|
||||
if (!isConstantContent(array)) return null;
|
||||
Arrays.asList(initializers).replaceAll(expr -> isIllegalReference(array, expr) ? null : expr);
|
||||
return Boolean.TRUE.equals(isConstantArray) ? initializers : null;
|
||||
return initializers;
|
||||
}
|
||||
|
||||
private static boolean isConstantContent(PsiVariable array) {
|
||||
Boolean isConstantArray = CachedValuesManager.<Boolean>getCachedValue(array, () -> CachedValueProvider.Result
|
||||
.create(!ContainerUtil.exists(VariableAccessUtils.getVariableReferences(array),
|
||||
ExpressionUtils::canBeMutated),
|
||||
PsiModificationTracker.MODIFICATION_COUNT));
|
||||
return Boolean.TRUE.equals(isConstantArray);
|
||||
}
|
||||
|
||||
private static boolean isIllegalReference(PsiVariable array, PsiExpression expr) {
|
||||
@@ -1305,13 +1311,6 @@ public final class ExpressionUtils {
|
||||
}) != null;
|
||||
}
|
||||
|
||||
private static boolean isConstantArray(PsiVariable array) {
|
||||
PsiElement scope = PsiTreeUtil.getParentOfType(array, array instanceof PsiField ? PsiClass.class : PsiCodeBlock.class);
|
||||
if (scope == null) return false;
|
||||
return PsiTreeUtil.processElements(
|
||||
scope, e -> !(e instanceof PsiReferenceExpression ref && ref.isReferenceTo(array) && canBeMutated(ref)));
|
||||
}
|
||||
|
||||
private static boolean canBeMutated(@NotNull PsiExpression expr) {
|
||||
while (expr.getParent() instanceof PsiParenthesizedExpression parent) {
|
||||
expr = parent;
|
||||
@@ -1334,17 +1333,13 @@ public final class ExpressionUtils {
|
||||
PsiMethod method = call.resolveMethod();
|
||||
if (method == null) return true;
|
||||
MutationSignature signature = MutationSignature.fromCall(call);
|
||||
if (signature == MutationSignature.unknown()) return true;
|
||||
if (!signature.isPure()) {
|
||||
PsiParameter parameter = MethodCallUtils.getParameterForArgument(expr);
|
||||
if (parameter == null || !(parameter.getParent() instanceof PsiParameterList paramList)) return true;
|
||||
int index = paramList.getParameterIndex(parameter);
|
||||
if (signature.mutatesArg(index)) return true;
|
||||
}
|
||||
if (!signature.isPure()) return true;
|
||||
PsiType type = method.getReturnType();
|
||||
boolean okReturnType = type instanceof PsiPrimitiveType || TypeUtils.isJavaLangString(type)
|
||||
|| Mutability.getMutability(method).isUnmodifiable();
|
||||
return !okReturnType && canBeMutated(call);
|
||||
if (type instanceof PsiPrimitiveType || TypeUtils.isJavaLangString(type)) return false;
|
||||
return canBeMutated(call);
|
||||
}
|
||||
if (parent instanceof PsiLocalVariable variable) {
|
||||
return !isConstantContent(variable);
|
||||
}
|
||||
if (parent instanceof PsiArrayAccessExpression arrayAccess) {
|
||||
return PsiUtil.isAccessedForWriting(arrayAccess);
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import java.util.*;
|
||||
|
||||
public class ArrayAddedIntoCollection {
|
||||
public static void main(String[] args) {
|
||||
int[] data = {-1};
|
||||
List<int[]> wrapped = new ArrayList<>();
|
||||
wrapped.add(wrap(data));
|
||||
wrapped.get(0)[0] = 0;
|
||||
if (data[0] == 0) {
|
||||
System.out.println("oops");
|
||||
}
|
||||
}
|
||||
|
||||
static int[] wrap(int[] data) {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import java.util.*;
|
||||
|
||||
public class ArrayElementWrappedInPureMethod {
|
||||
public static void main(String[] args) {
|
||||
int[] data = {-1};
|
||||
List<int[]> wrapped = wrap(data);
|
||||
wrapped.get(0)[0] = 0;
|
||||
if (data[0] == 0) {
|
||||
System.out.println("oops");
|
||||
}
|
||||
}
|
||||
|
||||
static List<int[]> wrap(int[] data) {
|
||||
return List.of(data);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,8 @@ class Test {
|
||||
int[] array = {1, 2, 3, 4, 5};
|
||||
System.out.println(Arrays.toString(array));
|
||||
System.arraycopy(array, 0, dest, 0, 5);
|
||||
if (<warning descr="Condition 'array[3] == 4' is always 'true'">array[3] == 4</warning>) {
|
||||
// Analysis cannot see this, unfortunately
|
||||
if (array[3] == 4) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -127,4 +127,7 @@ public class DataFlowInspection21Test extends DataFlowInspectionTestCase {
|
||||
}""");
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testArrayElementWrappedInPureMethod() { doTest(); }
|
||||
public void testArrayAddedIntoCollection() { doTest(); }
|
||||
}
|
||||
Reference in New Issue
Block a user