IDEA-241270 - support record serialization in existing inspections
authorIlyas Selimov <ilyas.selimov@jetbrains.com>
Thu, 13 Aug 2020 15:50:15 +0000 (22:50 +0700)
committerintellij-monorepo-bot <intellij-monorepo-bot-no-reply@jetbrains.com>
Thu, 13 Aug 2020 15:50:38 +0000 (15:50 +0000)
GitOrigin-RevId: 78a6a5f4fcde5c4706edd8efc01e6f36ec2b0f8e

23 files changed:
plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/ExternalizableWithSerializationMethodsInspection.java
plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/MissingSerialAnnotationInspection.java
plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/NonSerializableWithSerializationMethodsInspection.java
plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/ReadObjectAndWriteObjectPrivateInspection.java
plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/ReadObjectInitializationInspection.java
plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/SerialPersistentFieldsWithWrongSignatureInspection.java
plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/SerializableWithUnconstructableAncestorInspection.java
plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/TransientFieldInNonSerializableClassInspection.java
plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/TransientFieldNotInitializedInspection.java
plugins/InspectionGadgets/src/com/siyeh/ig/serialization/ExternalizableWithoutPublicNoArgConstructorInspection.java
plugins/InspectionGadgets/src/com/siyeh/ig/serialization/NonSerializableFieldInSerializableClassInspection.java
plugins/InspectionGadgets/src/com/siyeh/ig/serialization/SerializableHasSerialVersionUIDFieldInspection.java
plugins/InspectionGadgets/src/com/siyeh/ig/serialization/SerializableHasSerializationMethodsInspection.java
plugins/InspectionGadgets/src/com/siyeh/ig/serialization/SerializableInnerClassHasSerialVersionUIDFieldVisitor.java
plugins/InspectionGadgets/test/com/siyeh/igtest/serialization/externalizable_without_public_no_arg_constructor/ExternalizableWithoutPublicNoArgConstructor.java
plugins/InspectionGadgets/test/com/siyeh/igtest/serialization/missing_serial_annotation/ExternalizableClassInJava14Positive.java
plugins/InspectionGadgets/test/com/siyeh/igtest/serialization/missing_serial_annotation/SerializableClassInJava14Positive.java
plugins/InspectionGadgets/test/com/siyeh/igtest/serialization/non_serializable_with_serialization_methods/NonSerializableWithSerializationMethods.java
plugins/InspectionGadgets/test/com/siyeh/igtest/serialization/serializable_has_serial_version_uid_field/SerializableHasSerialVersionUIDField.java
plugins/InspectionGadgets/testsrc/com/siyeh/ig/serialization/ExternalizableWithoutPublicNoArgConstructorInspectionTest.java
plugins/InspectionGadgets/testsrc/com/siyeh/ig/serialization/NonSerializableWithSerializationMethodsInspectionTest.java
plugins/InspectionGadgets/testsrc/com/siyeh/ig/serialization/SerializableHasSerialVersionUIDFieldInspectionTest.java
plugins/InspectionGadgets/testsrc/com/siyeh/ig/serialization/SerializableInnerClassHasSerialVersionUIDFieldInspectionTest.java

index 492bc1bb0ce5207148181f2738264bec02437e1c..74e6483415a6a170febd714b2bdcca838acb6dec 100644 (file)
@@ -61,7 +61,7 @@ public class ExternalizableWithSerializationMethodsInspection
     @Override
     public void visitClass(@NotNull PsiClass aClass) {
       // no call to super, so it doesn't drill down
-      if (aClass.isInterface() || aClass.isAnnotationType()) {
+      if (aClass.isInterface() || aClass.isAnnotationType() || aClass.isRecord()) {
         return;
       }
       if (!SerializationUtils.isExternalizable(aClass)) {
index a0b09641c7e8c147f49cd3a774fd70291511e56c..30bf2d1efb7372e977a6b75cc366c9985122573b 100644 (file)
@@ -48,8 +48,14 @@ public class MissingSerialAnnotationInspection extends BaseInspection {
       Optional<PsiClass> pClass = getSerializablePsiClass(field);
       if (pClass.isEmpty()) return;
 
-      boolean candidateToBeAnnotated =
-        SerializationUtils.isExternalizable(pClass.get()) ? isSerialFieldInExternalizable(field) : isSerialFieldInSerializable(field);
+      boolean candidateToBeAnnotated;
+      if (pClass.get().isRecord()) {
+        candidateToBeAnnotated = isSerialFieldInExternalizable(field);
+      }
+      else {
+        candidateToBeAnnotated = SerializationUtils.isExternalizable(pClass.get()) ? isSerialFieldInExternalizable(field)
+                                                                                   : isSerialFieldInSerializable(field);
+      }
       if (candidateToBeAnnotated) {
         registerError(field.getNameIdentifier(), field);
       }
@@ -63,8 +69,14 @@ public class MissingSerialAnnotationInspection extends BaseInspection {
       Optional<PsiClass> pClass = getSerializablePsiClass(method);
       if (pClass.isEmpty()) return;
 
-      boolean candidateToBeAnnotated =
-        SerializationUtils.isExternalizable(pClass.get()) ? isSerialMethodInExternalizable(method) : isSerialMethodInSerializable(method);
+      boolean candidateToBeAnnotated;
+      if (pClass.get().isRecord()) {
+        candidateToBeAnnotated = isSerialMethodInExternalizable(method);
+      }
+      else {
+        candidateToBeAnnotated = SerializationUtils.isExternalizable(pClass.get()) ? isSerialMethodInExternalizable(method)
+                                                                                   : isSerialMethodInSerializable(method);
+      }
       if (candidateToBeAnnotated) {
         PsiIdentifier methodIdentifier = method.getNameIdentifier();
         if (methodIdentifier == null) return;
index 0b4950877fa1be39f6086c62fe0bd6dc9b9b81d4..d699d5f090128fe2a16f52cd2efe50d9f418afc9 100644 (file)
@@ -84,7 +84,7 @@ public class NonSerializableWithSerializationMethodsInspection extends BaseInspe
 
     @Override
     public void visitClass(@NotNull PsiClass aClass) {
-      if (aClass.isInterface() || aClass.isAnnotationType()) {
+      if (aClass.isInterface() || aClass.isAnnotationType() || aClass.isRecord()) {
         return;
       }
       final boolean hasReadObject = SerializationUtils.hasReadObject(aClass);
index 542cc07127a17f068f46a8d5e6a649cb4ba13eed..958c8814bfaefdfb4fe1f02584adc638607348ba 100644 (file)
@@ -62,7 +62,7 @@ public class ReadObjectAndWriteObjectPrivateInspection
       if (aClass == null) {
         return;
       }
-      if (aClass.isInterface() || aClass.isAnnotationType()) {
+      if (aClass.isInterface() || aClass.isAnnotationType() || aClass.isRecord()) {
         return;
       }
       if (method.hasModifierProperty(PsiModifier.PRIVATE)) {
index 3adcb028188e7e80f1377567b710b3cb3e50c8c3..dfdac52c5138b0ed600da91d2b87cef650ddfe7f 100644 (file)
@@ -54,7 +54,7 @@ public class ReadObjectInitializationInspection extends BaseInspection {
       if (aClass == null) {
         return;
       }
-      if (aClass.isInterface() || aClass.isAnnotationType()) {
+      if (aClass.isInterface() || aClass.isAnnotationType() || aClass.isRecord()) {
         return;
       }
       if (!SerializationUtils.isSerializable(aClass)) {
index 3295d84e0344c3f3a3ad2b5369d4dea9e0f1a27d..355a78f398f2e5b694ea090090c3c7c04e6e654a 100644 (file)
@@ -47,7 +47,7 @@ public class SerialPersistentFieldsWithWrongSignatureInspection
     @Override
     public void visitClass(@NotNull PsiClass aClass) {
       // no call to super, so it doesn't drill down
-      if (aClass.isInterface() || aClass.isAnnotationType()) {
+      if (aClass.isInterface() || aClass.isAnnotationType() || aClass.isRecord()) {
         return;
       }
       PsiField badSerialPersistentFields = null;
index 3fc5c28b9ca987510303962f05ab1e458e932a46..48f5211c004e0247642cd90abab545f8ec1a27fd 100644 (file)
@@ -52,7 +52,7 @@ public class SerializableWithUnconstructableAncestorInspection extends BaseInspe
 
     @Override
     public void visitClass(@NotNull PsiClass aClass) {
-      if (aClass.isInterface() || aClass.isAnnotationType()) {
+      if (aClass.isInterface() || aClass.isAnnotationType() || aClass.isRecord()) {
         return;
       }
       if (!SerializationUtils.isSerializable(aClass) || SerializationUtils.hasWriteReplace(aClass)) {
index cc9d5165ce33b4e80fef9318b01831c9d12218d9..3162f3377430fc98863b34f0f07f3fd1a555eaff 100644 (file)
@@ -55,7 +55,7 @@ public class TransientFieldInNonSerializableClassInspection extends BaseInspecti
         return;
       }
       final PsiClass aClass = field.getContainingClass();
-      if (SerializationUtils.isSerializable(aClass)) {
+      if (aClass != null && aClass.isRecord() || SerializationUtils.isSerializable(aClass)) {
         return;
       }
       registerModifierError(PsiModifier.TRANSIENT, field, field);
index 1d257713c2f0adc10a20e1fc02f72c6e3debc7cd..b968fa31cac1a2d62256f3534a8d88eddee190ef 100644 (file)
@@ -47,7 +47,7 @@ public class TransientFieldNotInitializedInspection extends BaseInspection {
         return;
       }
       final PsiClass containingClass = field.getContainingClass();
-      if (!SerializationUtils.isSerializable(containingClass)) {
+      if (containingClass != null && containingClass.isRecord() || !SerializationUtils.isSerializable(containingClass)) {
         return;
       }
       final PsiExpression initializer = field.getInitializer();
index 5a3791465e4bfd087871ed78e505a5e6693841b3..1562753ac122b8551c90b4268d1347b502a4acd7 100644 (file)
@@ -85,7 +85,7 @@ public class ExternalizableWithoutPublicNoArgConstructorInspection extends BaseI
 
     @Override
     public void visitClass(@NotNull PsiClass aClass) {
-      if (aClass.isInterface() || aClass.isEnum() || aClass.isAnnotationType() || aClass instanceof PsiTypeParameter) {
+      if (aClass.isInterface() || aClass.isEnum() || aClass.isAnnotationType() || aClass.isRecord() || aClass instanceof PsiTypeParameter) {
         return;
       }
       if (aClass.hasModifierProperty(PsiModifier.ABSTRACT)) {
index a333a8a25a35e822a31ee180c7d79c16bebf6bdb..047229f0a39dd70a9a0edd4ad0d148161b97f09e 100644 (file)
@@ -71,7 +71,7 @@ public class NonSerializableFieldInSerializableClassInspection extends Serializa
       if (ignoreAnonymousInnerClasses && aClass instanceof PsiAnonymousClass) {
         return;
       }
-      if (!SerializationUtils.isSerializable(aClass)) {
+      if (aClass.isRecord() || !SerializationUtils.isSerializable(aClass)) {
         return;
       }
       PsiType fieldType = field.getType();
index 06f0593e7a22d21ebdfd73227ee4e85ad13349ac..9de08791322c73bd8a65be063b22813b2130de47 100644 (file)
@@ -55,7 +55,7 @@ public class SerializableHasSerialVersionUIDFieldInspection extends Serializable
 
     @Override
     public void visitClass(@NotNull PsiClass aClass) {
-      if (aClass.isInterface() || aClass.isAnnotationType() || aClass.isEnum()) {
+      if (aClass.isInterface() || aClass.isAnnotationType() || aClass.isEnum() || aClass.isRecord()) {
         return;
       }
       if (aClass instanceof PsiTypeParameter || aClass instanceof PsiEnumConstantInitializer) {
index 9cca5ecf9d7514ff25390fb8657fb91a462478aa..3a145cbfca5e8146c2e1e8951fb61751f9884893 100644 (file)
@@ -72,7 +72,7 @@ public class SerializableHasSerializationMethodsInspection extends SerializableI
     @Override
     public void visitClass(@NotNull PsiClass aClass) {
       // no call to super, so it doesn't drill down
-      if (aClass.isInterface() || aClass.isAnnotationType() || aClass.isEnum()) {
+      if (aClass.isInterface() || aClass.isAnnotationType() || aClass.isEnum() || aClass.isRecord()) {
         return;
       }
       if (aClass instanceof PsiTypeParameter || aClass instanceof PsiEnumConstantInitializer) {
index d0dd4dd5deda2509b2e81e17489acafb657385ce..e99380eff87d7261ae838bcbaddb6e23e6176e39 100644 (file)
@@ -36,7 +36,7 @@ class SerializableInnerClassHasSerialVersionUIDFieldVisitor
   @Override
   public void visitClass(@NotNull PsiClass aClass) {
     // no call to super, so it doesn't drill down
-    if (aClass.isInterface() || aClass.isAnnotationType() || aClass.isEnum()) {
+    if (aClass.isInterface() || aClass.isAnnotationType() || aClass.isEnum() || aClass.isRecord()) {
       return;
     }
     if (aClass instanceof PsiTypeParameter) {
index 18684d9788263d98ce7e70d981bba114131f0deb..4a7ff72a52bf17747cf4978c90ba770603f4a18a 100644 (file)
@@ -74,3 +74,12 @@ public class ExternalizableWithoutPublicNoArgConstructor implements Externalizab
   }
 }
 
+record R(int a, int b) implements Externalizable {
+  @Override
+  public void writeExternal(ObjectOutput out) throws IOException {
+  }
+
+  @Override
+  public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+  }
+}
index 4e536d1c7fe3539b0a0611b16cef5e96ba6f3c1f..12243248c30a4e22582379c1a68a5096031c22a9 100644 (file)
@@ -19,4 +19,24 @@ class Test implements Externalizable {
   @Override
   public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
   }
+}
+
+record R() implements Externalizable {
+  private static final long <warning descr="The 'serialVersionUID' can be annotated with @Serial annotation">serialVersionUID</warning> = 7874493593505141603L;
+
+  public Object <warning descr="The 'writeReplace' can be annotated with @Serial annotation">writeReplace</warning>() throws ObjectStreamException {
+    return 1;
+  }
+
+  protected Object <warning descr="The 'readResolve' can be annotated with @Serial annotation">readResolve</warning>() throws ObjectStreamException {
+    return 1;
+  }
+
+  @Override
+  public void writeExternal(ObjectOutput out) throws IOException {
+  }
+
+  @Override
+  public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+  }
 }
\ No newline at end of file
index e0c6e0ee99c861f96318adcc49bf77262f025939..7ffd6eea432f5a8af284d20cc874f4630c01ad32 100644 (file)
@@ -21,4 +21,26 @@ class Test implements Serializable {
   protected Object <warning descr="The 'readResolve' can be annotated with @Serial annotation">readResolve</warning>() throws ObjectStreamException {
     return 1;
   }
+}
+
+record R() implements Serializable {
+  private static final long <warning descr="The 'serialVersionUID' can be annotated with @Serial annotation">serialVersionUID</warning> = 7874493593505141603L;
+  private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0];
+
+  public Object <warning descr="The 'writeReplace' can be annotated with @Serial annotation">writeReplace</warning>() throws ObjectStreamException {
+    return 1;
+  }
+
+  protected Object <warning descr="The 'readResolve' can be annotated with @Serial annotation">readResolve</warning>() throws ObjectStreamException {
+    return 1;
+  }
+
+  private void writeObject(ObjectOutputStream out) throws IOException {
+  }
+
+  private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+  }
+
+  private void readObjectNoData() throws ObjectStreamException {
+  }
 }
\ No newline at end of file
index c25d6f2be8a54cd68dfe8b79b4da56be8c5e3c6f..2d97ba6fc7732767157214986a2db0acb1c2ef53 100644 (file)
@@ -18,3 +18,12 @@ public class <warning descr="Non-serializable class 'NonSerializableWithSerializ
         };
     }
 }
+
+`record R() {
+
+    private void readObject(java.io.ObjectInputStream str) {
+    }
+
+    private void writeObject(java.io.ObjectOutputStream str) {
+    }
+}
\ No newline at end of file
index 7a3373a3d0b7783118dd53c87dbc93ef642383b9..10a0e08a87c720c9ebc10826eec691912c133221 100644 (file)
@@ -8,4 +8,7 @@ public class <warning descr="'SerializableHasSerialVersionUIDField' does not def
     public abstract class <warning descr="'DoWarnOnMe' does not define a 'serialVersionUID' field">DoWarnOnMe</warning> implements Serializable { }
 
     public interface X extends Serializable {}
+}
+
+record R() implements Serializable {
 }
\ No newline at end of file
index 74263ddba79e776058f809a967cfd323afe377db..91166df6fee5968923cfedfe34014ac93d942f63 100644 (file)
@@ -15,7 +15,7 @@ public class ExternalizableWithoutPublicNoArgConstructorInspectionTest extends L
   @NotNull
   @Override
   protected LightProjectDescriptor getProjectDescriptor() {
-    return JAVA_8;
+    return JAVA_14;
   }
 
   private void doTest() {
index 76110b80f5da8f4337cbb09605712d2cbf3aed21..3261ffd787ba767398724733248e46ade36e38dc 100644 (file)
@@ -15,7 +15,7 @@ public class NonSerializableWithSerializationMethodsInspectionTest extends Light
   @NotNull
   @Override
   protected LightProjectDescriptor getProjectDescriptor() {
-    return JAVA_8;
+    return JAVA_14;
   }
 
   private void doTest() {
index 374b0f555cacf86b8488032586ec4f41a19f6873..e0710f546ceb2b57654bb35663158d13e7b431c3 100644 (file)
@@ -15,7 +15,7 @@ public class SerializableHasSerialVersionUIDFieldInspectionTest extends LightJav
   @NotNull
   @Override
   protected LightProjectDescriptor getProjectDescriptor() {
-    return JAVA_8;
+    return JAVA_15;
   }
 
   private void doTest() {
index 56f980574b1dcc83675e12face45279a80d539d9..23fbc22b388bc60f313938b58c5b064453aee942 100644 (file)
 package com.siyeh.ig.serialization;
 
 import com.intellij.codeInspection.InspectionProfileEntry;
+import com.intellij.testFramework.LightProjectDescriptor;
+import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
 import com.siyeh.ig.LightJavaInspectionTestCase;
+import org.jetbrains.annotations.NotNull;
 
 /**
  * @author Bas Leijdekkers
@@ -66,7 +69,27 @@ public class SerializableInnerClassHasSerialVersionUIDFieldInspectionTest extend
            "}");
   }
 
+  public void testRecord() {
+    doTest("class A {" +
+           "  record R() implements java.io.Serializable {" +
+           "  }" +
+           "}");
+  }
+
   public void testTypeParameter() {
     doTest("class A<TypeParameter extends java.awt.Component> {}");
   }
+
+  @Override
+  protected String[] getEnvironmentClasses() {
+    return new String[] {
+      "package java.awt;" +
+      "public abstract class Component {}"
+    };
+  }
+
+  @Override
+  protected @NotNull LightProjectDescriptor getProjectDescriptor() {
+    return LightJavaCodeInsightFixtureTestCase.JAVA_15;
+  }
 }