[java-dfa] Better support for array component nullability

GitOrigin-RevId: 6536838ee1349aae389da59b7b4c878616309b20
This commit is contained in:
Tagir Valeev
2023-04-27 09:56:46 +00:00
committed by intellij-monorepo-bot
parent fa62fec763
commit e85a88956e
3 changed files with 46 additions and 9 deletions
@@ -1,4 +1,4 @@
// Copyright 2000-2021 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.
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInspection.dataFlow.java.inst;
import com.intellij.codeInspection.dataFlow.TypeConstraint;
@@ -57,6 +57,7 @@ public class ArrayAccessInstruction extends ExpressionPushingInstruction {
}
else {
DfType type = TypeConstraint.fromDfType(stateBefore.getDfType(array)).getArrayComponentType();
type = type.meet(ArrayElementDescriptor.getArrayComponentType(array));
result = type == DfType.BOTTOM ? interpreter.getFactory().getUnknown() : interpreter.getFactory().fromDfType(type);
}
if (!(result instanceof DfaVariableValue) && array instanceof DfaVariableValue) {
@@ -7,10 +7,7 @@ import com.intellij.codeInspection.dataFlow.java.JavaDfaValueFactory;
import com.intellij.codeInspection.dataFlow.jvm.SpecialField;
import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet;
import com.intellij.codeInspection.dataFlow.types.*;
import com.intellij.codeInspection.dataFlow.value.DfaTypeValue;
import com.intellij.codeInspection.dataFlow.value.DfaValue;
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
import com.intellij.codeInspection.dataFlow.value.*;
import com.intellij.psi.*;
import com.intellij.util.ObjectUtils;
import com.siyeh.ig.psiutils.ExpressionUtils;
@@ -46,13 +43,22 @@ public final class ArrayElementDescriptor extends JvmVariableDescriptor {
DfaVariableValue qualifier = thisValue.getQualifier();
DfType dfType = getDfType(qualifier);
if (qualifier != null && dfType instanceof DfReferenceType) {
PsiVarDescriptor descriptor = ObjectUtils.tryCast(qualifier.getDescriptor(), PsiVarDescriptor.class);
VariableDescriptor qualDescriptor = qualifier.getDescriptor();
int depth = 1;
while (qualDescriptor instanceof ArrayElementDescriptor) {
depth++;
qualifier = qualifier.getQualifier();
if (qualifier == null) break;
qualDescriptor = qualifier.getDescriptor();
}
PsiVarDescriptor descriptor = ObjectUtils.tryCast(qualDescriptor, PsiVarDescriptor.class);
if (descriptor != null) {
PsiType psiType = descriptor.getType(qualifier);
if (psiType instanceof PsiArrayType) {
PsiType componentType = ((PsiArrayType)psiType).getComponentType();
return dfType.meet(DfaNullability.fromNullability(DfaPsiUtil.getTypeNullability(componentType)).asDfType());
for (int i = 0; i < depth; i++) {
if (!(psiType instanceof PsiArrayType arrayType)) return dfType;
psiType = arrayType.getComponentType();
}
return dfType.meet(DfaNullability.fromNullability(DfaPsiUtil.getTypeNullability(psiType)).asDfType());
}
}
return dfType;
@@ -179,4 +185,18 @@ public final class ArrayElementDescriptor extends JvmVariableDescriptor {
}
return DfaUtil.boxUnbox(factory.fromDfType(dfType), targetType);
}
/**
* @param array array value
* @return inherent type of array component
*/
public static @NotNull DfType getArrayComponentType(@NotNull DfaValue array) {
DfType componentType = TypeConstraint.fromDfType(array.getDfType()).getArrayComponentType();
if (componentType instanceof DfReferenceType &&
array instanceof DfaVariableValue var && var.getDescriptor() instanceof PsiVarDescriptor varDescriptor &&
varDescriptor.getType(var.getQualifier()) instanceof PsiArrayType arrayType) {
return componentType.meet(DfaNullability.fromNullability(DfaPsiUtil.getTypeNullability(arrayType.getComponentType())).asDfType());
}
return componentType;
}
}
@@ -39,4 +39,20 @@ class TestCompilerWarnings {
}
}
void multidim(@Nullable String @NotNull[] @NotNull[] a1,
@NotNull String @NotNull[] @Nullable[] a2,
@NotNull String @Nullable [] @NotNull[] a3) {
System.out.println(a1[0][0].<warning descr="Method invocation 'length' may produce 'NullPointerException'">length</warning>());
System.out.println(<warning descr="Array access 'a2[0][0]' may produce 'NullPointerException'">a2[0][0]</warning>.length());
System.out.println(<warning descr="Array access 'a3[0]' may produce 'NullPointerException'">a3[0]</warning>[0].length());
}
void multidimParam(@Nullable String @NotNull[] @NotNull[] a1,
@NotNull String @NotNull[] @Nullable[] a2,
@NotNull String @Nullable [] @NotNull[] a3,
int i) {
System.out.println(a1[i][i].length()); // Should warn here as well but not supported yet
System.out.println(<warning descr="Array access 'a2[i][i]' may produce 'NullPointerException'">a2[i][i]</warning>.length());
System.out.println(<warning descr="Array access 'a3[i]' may produce 'NullPointerException'">a3[i]</warning>[i].length());
}
}