From ff1cfed0de5c93a4c8c43836a23853ede270199d Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Wed, 17 Apr 2024 11:53:26 +0200 Subject: [PATCH] [java-dfa] Do not assume that fields are non-initialized in readResolve() when readObject() is present Fixes IDEA-351947 False positive warning 'Nullability and data flow problems' in readResolve GitOrigin-RevId: 26c71c3e26c087334741a5a098812f91144bdcd4 --- .../jvm/descriptors/PlainDescriptor.java | 12 +++++-- .../dataFlow/fixture/ReadResolve2.java | 31 +++++++++++++++++++ .../DataFlowInspection21Test.java | 1 + 3 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 java/java-tests/testData/inspection/dataFlow/fixture/ReadResolve2.java diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/jvm/descriptors/PlainDescriptor.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/jvm/descriptors/PlainDescriptor.java index 22e982720eb0..c51b0ab73b8f 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/jvm/descriptors/PlainDescriptor.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/jvm/descriptors/PlainDescriptor.java @@ -18,6 +18,8 @@ import com.intellij.psi.*; import com.intellij.psi.util.*; import com.intellij.util.JavaPsiConstructorUtil; import com.intellij.util.ObjectUtils; +import com.intellij.util.containers.ContainerUtil; +import com.siyeh.ig.callMatcher.CallMatcher; import com.siyeh.ig.psiutils.ExpressionUtils; import com.siyeh.ig.psiutils.VariableAccessUtils; import org.jetbrains.annotations.NotNull; @@ -159,16 +161,20 @@ public final class PlainDescriptor extends PsiVarDescriptor { if (!placeMethod.hasModifierProperty(PsiModifier.STATIC) && target.hasModifierProperty(PsiModifier.STATIC)) return false; if (!target.hasModifierProperty(PsiModifier.STATIC) && !placeMethod.hasModifierProperty(PsiModifier.STATIC) && - (isReadResolveMethod(placeMethod, placeClass) || + (isReadResolveMethodWithoutReadObject(placeMethod, placeClass) || methodCanBeCalledFromConstructorBeforeFieldInitializing(target, placeMethod, placeClass))) { return true; } return getAccessOffset(placeMethod) < getWriteOffset(target); } - private static boolean isReadResolveMethod(@NotNull PsiMethod method, @NotNull PsiClass aClass) { + private static final CallMatcher READ_OBJECT = CallMatcher.instanceCall(CommonClassNames.JAVA_LANG_OBJECT, "readObject") + .parameterTypes("java.io.ObjectInputStream"); + + private static boolean isReadResolveMethodWithoutReadObject(@NotNull PsiMethod method, @NotNull PsiClass aClass) { return "readResolve".equals(method.getName()) && method.getParameterList().isEmpty() && - InheritanceUtil.isInheritor(aClass, CommonClassNames.JAVA_IO_SERIALIZABLE); + !aClass.isRecord() && InheritanceUtil.isInheritor(aClass, CommonClassNames.JAVA_IO_SERIALIZABLE) && + !ContainerUtil.exists(aClass.findMethodsByName("readObject", false), READ_OBJECT::methodMatches); } private record TargetCallInfo(@NotNull PsiMethod constructor, diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/ReadResolve2.java b/java/java-tests/testData/inspection/dataFlow/fixture/ReadResolve2.java new file mode 100644 index 000000000000..132a9e5a0bcb --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/ReadResolve2.java @@ -0,0 +1,31 @@ +import org.jetbrains.annotations.NotNull; + +import java.io.*; +import java.util.Base64; + +class Test implements Serializable { + @NotNull String string; + + Test(@NotNull String string) { + this.string = string; + } + + @Serial + private void readObject(ObjectInputStream in) throws IOException { + this.string = in.readUTF(); + } + + @Serial + protected Object readResolve() { + if (string == null) { + throw new IllegalStateException("Wrong object!"); + } + return this; + } + + public static void main(String[] args) throws IOException, ClassNotFoundException { + byte[] data = Base64.getDecoder().decode("rO0ABXNyAARUZXN0ST9d8XKvH/0CAAFMAAZzdHJpbmd0ABJMamF2YS9sYW5nL1N0cmluZzt4cHA="); + Test object = (Test) new ObjectInputStream(new ByteArrayInputStream(data)).readObject(); + System.out.println(object); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection21Test.java b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection21Test.java index 1d29ed6d532e..a5eace25c853 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection21Test.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection21Test.java @@ -114,6 +114,7 @@ public class DataFlowInspection21Test extends DataFlowInspectionTestCase { doTest(); } public void testReadResolve() { doTest(); } + public void testReadResolve2() { doTest(); } public void testDifferentTypesButNullable() { doTest(); } public void testForEachPattern() {