diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/inst/ArrayAccessInstruction.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/inst/ArrayAccessInstruction.java index 14a0202b1cae..6a291711490e 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/inst/ArrayAccessInstruction.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/inst/ArrayAccessInstruction.java @@ -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) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/jvm/descriptors/ArrayElementDescriptor.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/jvm/descriptors/ArrayElementDescriptor.java index da60dd99e5c3..bc27d2ab7164 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/jvm/descriptors/ArrayElementDescriptor.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/jvm/descriptors/ArrayElementDescriptor.java @@ -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; + } } diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/NullableArrayComponent.java b/java/java-tests/testData/inspection/dataFlow/fixture/NullableArrayComponent.java index 36f3af3e6d5d..d7ef4b1c1ae7 100644 --- a/java/java-tests/testData/inspection/dataFlow/fixture/NullableArrayComponent.java +++ b/java/java-tests/testData/inspection/dataFlow/fixture/NullableArrayComponent.java @@ -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].length()); + System.out.println(a2[0][0].length()); + System.out.println(a3[0][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(a2[i][i].length()); + System.out.println(a3[i][i].length()); + } } \ No newline at end of file