From e0cb1792bdff8278f7ab3464ebadf62dff05c8b9 Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Wed, 2 Jul 2025 11:18:30 +0200 Subject: [PATCH 1/7] Java: Add 'Useless serialization member in record class' query --- .../UselessMembersOfTheRecordsClass.md | 54 +++++++++++++++++++ .../UselessMembersOfTheRecordsClass.ql | 23 ++++++++ .../UselessMembersOfTheRecordsClass/Test.java | 42 +++++++++++++++ .../UselessMembersOfTheRecordsClass.expected | 6 +++ .../UselessMembersOfTheRecordsClass.qlref | 1 + .../UselessMembersOfTheRecordsClass/options | 1 + 6 files changed, 127 insertions(+) create mode 100644 java/ql/src/Violations of Best Practice/Records/UselessMembersOfTheRecordsClass.md create mode 100644 java/ql/src/Violations of Best Practice/Records/UselessMembersOfTheRecordsClass.ql create mode 100644 java/ql/test/query-tests/UselessMembersOfTheRecordsClass/Test.java create mode 100644 java/ql/test/query-tests/UselessMembersOfTheRecordsClass/UselessMembersOfTheRecordsClass.expected create mode 100644 java/ql/test/query-tests/UselessMembersOfTheRecordsClass/UselessMembersOfTheRecordsClass.qlref create mode 100644 java/ql/test/query-tests/UselessMembersOfTheRecordsClass/options diff --git a/java/ql/src/Violations of Best Practice/Records/UselessMembersOfTheRecordsClass.md b/java/ql/src/Violations of Best Practice/Records/UselessMembersOfTheRecordsClass.md new file mode 100644 index 000000000000..27a1d25cecc9 --- /dev/null +++ b/java/ql/src/Violations of Best Practice/Records/UselessMembersOfTheRecordsClass.md @@ -0,0 +1,54 @@ +## Overview + +Record types were introduced in Java 16 as a mechanism to provide simpler data handling that is an alternative to regular classes. Record classes behave slightly differently during serialization however, namely any `writeObject`, `readObject`, `readObjectNoData`, `writeExternal`, and `readExternal` methods and `serialPersistentFields` fields declared in these classes cannot be used to affect the serialization process of any `Record` data type. + +## Recommendation + +Some level of serialization customization is offered by the Java 16 Record feature; the `writeReplace` and `readResolve` methods in a record that implements `java.io.Serializable` can be used to replace the object to be serialized. Otherwise no further customization of serialization of records is possible, and it is better to consider using a regular class implementing `java.io.Serializable` or `java.io.Externalizable` when customization is needed. + +## Example + +```java +record T1() implements Serializable { + + @Serial + private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0]; // NON_COMPLIANT + + @Serial + private void writeObject(ObjectOutputStream out) throws IOException {} // NON_COMPLIANT + + @Serial + private void readObject(ObjectOutputStream out) throws IOException {}// NON_COMPLIANT + + @Serial + private void readObjectNoData(ObjectOutputStream out) throws IOException { // NON_COMPLIANT + } +} + +record T2() implements Externalizable { + + @Override + public void writeExternal(ObjectOutput out) throws IOException { // NON_COMPLIANT + } + + @Override + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { // NON_COMPLIANT + } +} + +record T3() implements Serializable { + + public Object writeReplace(ObjectOutput out) throws ObjectStreamException { // COMPLIANT + return new Object(); + } + + public Object readResolve(ObjectInput in) throws ObjectStreamException { // COMPLIANT + return new Object(); + } +} +``` + +## References + +- Oracle Serialization Documentation: [Serialization of Records](https://docs.oracle.com/en/java/javase/16/docs/specs/serialization/serial-arch.html#serialization-of-records) +- Java Record: [Feature Specification](https://openjdk.org/jeps/395) diff --git a/java/ql/src/Violations of Best Practice/Records/UselessMembersOfTheRecordsClass.ql b/java/ql/src/Violations of Best Practice/Records/UselessMembersOfTheRecordsClass.ql new file mode 100644 index 000000000000..1e773d0fec0b --- /dev/null +++ b/java/ql/src/Violations of Best Practice/Records/UselessMembersOfTheRecordsClass.ql @@ -0,0 +1,23 @@ +/** + * @id java/useless-members-of-the-records-class + * @name Useless serialization members of `Records` + * @description Using certain members of the `Records` class during serialization will result in + * those members being ignored. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags quality + * reliability + * correctness + */ + +import java + +from Record record, Member m +where + record.getAMember() = m and + m.hasName([ + "writeObject", "readObject", "readObjectNoData", "writeExternal", "readExternal", + "serialPersistentFields" + ]) +select record, "Declaration of useless member $@ found.", m, m.getName() diff --git a/java/ql/test/query-tests/UselessMembersOfTheRecordsClass/Test.java b/java/ql/test/query-tests/UselessMembersOfTheRecordsClass/Test.java new file mode 100644 index 000000000000..aaf04a1baf88 --- /dev/null +++ b/java/ql/test/query-tests/UselessMembersOfTheRecordsClass/Test.java @@ -0,0 +1,42 @@ +import java.io.*; + +public class Test { + record T1() implements Serializable { + + @Serial + private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0]; // NON_COMPLIANT + + @Serial + private void writeObject(ObjectOutputStream out) throws IOException {} // NON_COMPLIANT + + @Serial + private void readObject(ObjectOutputStream out) throws IOException {}// NON_COMPLIANT + + @Serial + private void readObjectNoData(ObjectOutputStream out) throws IOException { // NON_COMPLIANT + } + +} + + record T2() implements Externalizable { + + @Override + public void writeExternal(ObjectOutput out) throws IOException { // NON_COMPLIANT + } + + @Override + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { // NON_COMPLIANT + } + + } + + record T3() implements Serializable { + + public Object writeReplace(ObjectOutput out) throws ObjectStreamException { // COMPLIANT + return new Object(); + } + + public Object readResolve(ObjectInput in) throws ObjectStreamException { // COMPLIANT + return new Object(); + } +}} diff --git a/java/ql/test/query-tests/UselessMembersOfTheRecordsClass/UselessMembersOfTheRecordsClass.expected b/java/ql/test/query-tests/UselessMembersOfTheRecordsClass/UselessMembersOfTheRecordsClass.expected new file mode 100644 index 000000000000..ed0d429437fc --- /dev/null +++ b/java/ql/test/query-tests/UselessMembersOfTheRecordsClass/UselessMembersOfTheRecordsClass.expected @@ -0,0 +1,6 @@ +| Test.java:4:12:4:13 | T1 | Declaration of useless member $@ found. | Test.java:7:46:7:67 | serialPersistentFields | serialPersistentFields | +| Test.java:4:12:4:13 | T1 | Declaration of useless member $@ found. | Test.java:10:18:10:28 | writeObject | writeObject | +| Test.java:4:12:4:13 | T1 | Declaration of useless member $@ found. | Test.java:13:18:13:27 | readObject | readObject | +| Test.java:4:12:4:13 | T1 | Declaration of useless member $@ found. | Test.java:16:18:16:33 | readObjectNoData | readObjectNoData | +| Test.java:21:12:21:13 | T2 | Declaration of useless member $@ found. | Test.java:24:17:24:29 | writeExternal | writeExternal | +| Test.java:21:12:21:13 | T2 | Declaration of useless member $@ found. | Test.java:28:17:28:28 | readExternal | readExternal | diff --git a/java/ql/test/query-tests/UselessMembersOfTheRecordsClass/UselessMembersOfTheRecordsClass.qlref b/java/ql/test/query-tests/UselessMembersOfTheRecordsClass/UselessMembersOfTheRecordsClass.qlref new file mode 100644 index 000000000000..0a005e56a89c --- /dev/null +++ b/java/ql/test/query-tests/UselessMembersOfTheRecordsClass/UselessMembersOfTheRecordsClass.qlref @@ -0,0 +1 @@ +Violations of Best Practice/Records/UselessMembersOfTheRecordsClass.ql diff --git a/java/ql/test/query-tests/UselessMembersOfTheRecordsClass/options b/java/ql/test/query-tests/UselessMembersOfTheRecordsClass/options new file mode 100644 index 000000000000..5465e6337b0e --- /dev/null +++ b/java/ql/test/query-tests/UselessMembersOfTheRecordsClass/options @@ -0,0 +1 @@ +//semmle-extractor-options: --javac-args -source 16 -target 16 \ No newline at end of file From 2cd0c64e418f5434932eadf33600a04367ceb79d Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Wed, 2 Jul 2025 11:19:10 +0200 Subject: [PATCH 2/7] Improve query quality --- .../Records/UselessMembersOfTheRecordsClass.ql | 10 ++++++---- .../UselessMembersOfTheRecordsClass.expected | 12 ++++++------ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/java/ql/src/Violations of Best Practice/Records/UselessMembersOfTheRecordsClass.ql b/java/ql/src/Violations of Best Practice/Records/UselessMembersOfTheRecordsClass.ql index 1e773d0fec0b..9b5cb7662937 100644 --- a/java/ql/src/Violations of Best Practice/Records/UselessMembersOfTheRecordsClass.ql +++ b/java/ql/src/Violations of Best Practice/Records/UselessMembersOfTheRecordsClass.ql @@ -1,8 +1,9 @@ /** - * @id java/useless-members-of-the-records-class - * @name Useless serialization members of `Records` - * @description Using certain members of the `Records` class during serialization will result in + * @id java/useless-member-of-the-record-class + * @name Useless serialization member of record class + * @description Using certain members of a record class during serialization will result in * those members being ignored. + * @previous-id java/useless-members-of-the-records-class * @kind problem * @precision very-high * @problem.severity warning @@ -20,4 +21,5 @@ where "writeObject", "readObject", "readObjectNoData", "writeExternal", "readExternal", "serialPersistentFields" ]) -select record, "Declaration of useless member $@ found.", m, m.getName() +select m, "Useless serialization member $@ found in record class $@.", m, m.getName(), record, + record.getName() diff --git a/java/ql/test/query-tests/UselessMembersOfTheRecordsClass/UselessMembersOfTheRecordsClass.expected b/java/ql/test/query-tests/UselessMembersOfTheRecordsClass/UselessMembersOfTheRecordsClass.expected index ed0d429437fc..ba8cee2d9615 100644 --- a/java/ql/test/query-tests/UselessMembersOfTheRecordsClass/UselessMembersOfTheRecordsClass.expected +++ b/java/ql/test/query-tests/UselessMembersOfTheRecordsClass/UselessMembersOfTheRecordsClass.expected @@ -1,6 +1,6 @@ -| Test.java:4:12:4:13 | T1 | Declaration of useless member $@ found. | Test.java:7:46:7:67 | serialPersistentFields | serialPersistentFields | -| Test.java:4:12:4:13 | T1 | Declaration of useless member $@ found. | Test.java:10:18:10:28 | writeObject | writeObject | -| Test.java:4:12:4:13 | T1 | Declaration of useless member $@ found. | Test.java:13:18:13:27 | readObject | readObject | -| Test.java:4:12:4:13 | T1 | Declaration of useless member $@ found. | Test.java:16:18:16:33 | readObjectNoData | readObjectNoData | -| Test.java:21:12:21:13 | T2 | Declaration of useless member $@ found. | Test.java:24:17:24:29 | writeExternal | writeExternal | -| Test.java:21:12:21:13 | T2 | Declaration of useless member $@ found. | Test.java:28:17:28:28 | readExternal | readExternal | +| Test.java:7:46:7:67 | serialPersistentFields | Useless serialization member $@ found in record class $@. | Test.java:7:46:7:67 | serialPersistentFields | serialPersistentFields | Test.java:4:12:4:13 | T1 | T1 | +| Test.java:10:18:10:28 | writeObject | Useless serialization member $@ found in record class $@. | Test.java:10:18:10:28 | writeObject | writeObject | Test.java:4:12:4:13 | T1 | T1 | +| Test.java:13:18:13:27 | readObject | Useless serialization member $@ found in record class $@. | Test.java:13:18:13:27 | readObject | readObject | Test.java:4:12:4:13 | T1 | T1 | +| Test.java:16:18:16:33 | readObjectNoData | Useless serialization member $@ found in record class $@. | Test.java:16:18:16:33 | readObjectNoData | readObjectNoData | Test.java:4:12:4:13 | T1 | T1 | +| Test.java:24:17:24:29 | writeExternal | Useless serialization member $@ found in record class $@. | Test.java:24:17:24:29 | writeExternal | writeExternal | Test.java:21:12:21:13 | T2 | T2 | +| Test.java:28:17:28:28 | readExternal | Useless serialization member $@ found in record class $@. | Test.java:28:17:28:28 | readExternal | readExternal | Test.java:21:12:21:13 | T2 | T2 | From a2d4f58af763e835a57beac7ddec3978b91b9a99 Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Wed, 2 Jul 2025 11:27:32 +0200 Subject: [PATCH 3/7] Use inline test expectations --- .../UselessMembersOfTheRecordsClass/Test.java | 12 ++++++------ .../UselessMembersOfTheRecordsClass.qlref | 3 ++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/java/ql/test/query-tests/UselessMembersOfTheRecordsClass/Test.java b/java/ql/test/query-tests/UselessMembersOfTheRecordsClass/Test.java index aaf04a1baf88..bab1780de0ec 100644 --- a/java/ql/test/query-tests/UselessMembersOfTheRecordsClass/Test.java +++ b/java/ql/test/query-tests/UselessMembersOfTheRecordsClass/Test.java @@ -4,16 +4,16 @@ public class Test { record T1() implements Serializable { @Serial - private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0]; // NON_COMPLIANT + private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0]; // $ Alert @Serial - private void writeObject(ObjectOutputStream out) throws IOException {} // NON_COMPLIANT + private void writeObject(ObjectOutputStream out) throws IOException {} // $ Alert @Serial - private void readObject(ObjectOutputStream out) throws IOException {}// NON_COMPLIANT + private void readObject(ObjectOutputStream out) throws IOException {} // $ Alert @Serial - private void readObjectNoData(ObjectOutputStream out) throws IOException { // NON_COMPLIANT + private void readObjectNoData(ObjectOutputStream out) throws IOException { // $ Alert } } @@ -21,11 +21,11 @@ private void readObjectNoData(ObjectOutputStream out) throws IOException { // NO record T2() implements Externalizable { @Override - public void writeExternal(ObjectOutput out) throws IOException { // NON_COMPLIANT + public void writeExternal(ObjectOutput out) throws IOException { // $ Alert } @Override - public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { // NON_COMPLIANT + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { // $ Alert } } diff --git a/java/ql/test/query-tests/UselessMembersOfTheRecordsClass/UselessMembersOfTheRecordsClass.qlref b/java/ql/test/query-tests/UselessMembersOfTheRecordsClass/UselessMembersOfTheRecordsClass.qlref index 0a005e56a89c..5b4ab63ba4bd 100644 --- a/java/ql/test/query-tests/UselessMembersOfTheRecordsClass/UselessMembersOfTheRecordsClass.qlref +++ b/java/ql/test/query-tests/UselessMembersOfTheRecordsClass/UselessMembersOfTheRecordsClass.qlref @@ -1 +1,2 @@ -Violations of Best Practice/Records/UselessMembersOfTheRecordsClass.ql +query: Violations of Best Practice/Records/UselessMembersOfTheRecordsClass.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql From 528389af38f741546dbb24e6b766cf0de180e1be Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Wed, 2 Jul 2025 11:46:46 +0200 Subject: [PATCH 4/7] Adjust expected file for query suite integration test --- .../java/query-suite/java-code-quality-extended.qls.expected | 1 + .../java/query-suite/java-code-quality.qls.expected | 1 + 2 files changed, 2 insertions(+) diff --git a/java/ql/integration-tests/java/query-suite/java-code-quality-extended.qls.expected b/java/ql/integration-tests/java/query-suite/java-code-quality-extended.qls.expected index 5c82bd5e3498..00b8a91fb576 100644 --- a/java/ql/integration-tests/java/query-suite/java-code-quality-extended.qls.expected +++ b/java/ql/integration-tests/java/query-suite/java-code-quality-extended.qls.expected @@ -77,6 +77,7 @@ ql/java/ql/src/Violations of Best Practice/Naming Conventions/ConfusingMethodNam ql/java/ql/src/Violations of Best Practice/Naming Conventions/ConfusingOverloading.ql ql/java/ql/src/Violations of Best Practice/Naming Conventions/LocalShadowsFieldConfusing.ql ql/java/ql/src/Violations of Best Practice/Naming Conventions/SameNameAsSuper.ql +ql/java/ql/src/Violations of Best Practice/Records/UselessMembersOfTheRecordsClass.ql ql/java/ql/src/Violations of Best Practice/Undesirable Calls/CallsToStringToString.ql ql/java/ql/src/Violations of Best Practice/Undesirable Calls/DefaultToString.ql ql/java/ql/src/Violations of Best Practice/Undesirable Calls/DoNotCallFinalize.ql diff --git a/java/ql/integration-tests/java/query-suite/java-code-quality.qls.expected b/java/ql/integration-tests/java/query-suite/java-code-quality.qls.expected index e558cf3ffc48..df6ec8fab813 100644 --- a/java/ql/integration-tests/java/query-suite/java-code-quality.qls.expected +++ b/java/ql/integration-tests/java/query-suite/java-code-quality.qls.expected @@ -75,6 +75,7 @@ ql/java/ql/src/Violations of Best Practice/Naming Conventions/ConfusingMethodNam ql/java/ql/src/Violations of Best Practice/Naming Conventions/ConfusingOverloading.ql ql/java/ql/src/Violations of Best Practice/Naming Conventions/LocalShadowsFieldConfusing.ql ql/java/ql/src/Violations of Best Practice/Naming Conventions/SameNameAsSuper.ql +ql/java/ql/src/Violations of Best Practice/Records/UselessMembersOfTheRecordsClass.ql ql/java/ql/src/Violations of Best Practice/Undesirable Calls/CallsToStringToString.ql ql/java/ql/src/Violations of Best Practice/Undesirable Calls/DefaultToString.ql ql/java/ql/src/Violations of Best Practice/Undesirable Calls/DoNotCallFinalize.ql From 82fe647a40a6ef2beec01b9cca6e88e808a0b7da Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Thu, 3 Jul 2025 16:03:05 +0200 Subject: [PATCH 5/7] Improve alert message --- .../Records/UselessMembersOfTheRecordsClass.ql | 3 +-- .../UselessMembersOfTheRecordsClass.expected | 12 ++++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/java/ql/src/Violations of Best Practice/Records/UselessMembersOfTheRecordsClass.ql b/java/ql/src/Violations of Best Practice/Records/UselessMembersOfTheRecordsClass.ql index 9b5cb7662937..a55b283d6e70 100644 --- a/java/ql/src/Violations of Best Practice/Records/UselessMembersOfTheRecordsClass.ql +++ b/java/ql/src/Violations of Best Practice/Records/UselessMembersOfTheRecordsClass.ql @@ -21,5 +21,4 @@ where "writeObject", "readObject", "readObjectNoData", "writeExternal", "readExternal", "serialPersistentFields" ]) -select m, "Useless serialization member $@ found in record class $@.", m, m.getName(), record, - record.getName() +select m, "Useless serialization member found in record class $@.", record, record.getName() diff --git a/java/ql/test/query-tests/UselessMembersOfTheRecordsClass/UselessMembersOfTheRecordsClass.expected b/java/ql/test/query-tests/UselessMembersOfTheRecordsClass/UselessMembersOfTheRecordsClass.expected index ba8cee2d9615..27195b3f5080 100644 --- a/java/ql/test/query-tests/UselessMembersOfTheRecordsClass/UselessMembersOfTheRecordsClass.expected +++ b/java/ql/test/query-tests/UselessMembersOfTheRecordsClass/UselessMembersOfTheRecordsClass.expected @@ -1,6 +1,6 @@ -| Test.java:7:46:7:67 | serialPersistentFields | Useless serialization member $@ found in record class $@. | Test.java:7:46:7:67 | serialPersistentFields | serialPersistentFields | Test.java:4:12:4:13 | T1 | T1 | -| Test.java:10:18:10:28 | writeObject | Useless serialization member $@ found in record class $@. | Test.java:10:18:10:28 | writeObject | writeObject | Test.java:4:12:4:13 | T1 | T1 | -| Test.java:13:18:13:27 | readObject | Useless serialization member $@ found in record class $@. | Test.java:13:18:13:27 | readObject | readObject | Test.java:4:12:4:13 | T1 | T1 | -| Test.java:16:18:16:33 | readObjectNoData | Useless serialization member $@ found in record class $@. | Test.java:16:18:16:33 | readObjectNoData | readObjectNoData | Test.java:4:12:4:13 | T1 | T1 | -| Test.java:24:17:24:29 | writeExternal | Useless serialization member $@ found in record class $@. | Test.java:24:17:24:29 | writeExternal | writeExternal | Test.java:21:12:21:13 | T2 | T2 | -| Test.java:28:17:28:28 | readExternal | Useless serialization member $@ found in record class $@. | Test.java:28:17:28:28 | readExternal | readExternal | Test.java:21:12:21:13 | T2 | T2 | +| Test.java:7:46:7:67 | serialPersistentFields | Useless serialization member found in record class $@. | Test.java:4:12:4:13 | T1 | T1 | +| Test.java:10:18:10:28 | writeObject | Useless serialization member found in record class $@. | Test.java:4:12:4:13 | T1 | T1 | +| Test.java:13:18:13:27 | readObject | Useless serialization member found in record class $@. | Test.java:4:12:4:13 | T1 | T1 | +| Test.java:16:18:16:33 | readObjectNoData | Useless serialization member found in record class $@. | Test.java:4:12:4:13 | T1 | T1 | +| Test.java:24:17:24:29 | writeExternal | Useless serialization member found in record class $@. | Test.java:21:12:21:13 | T2 | T2 | +| Test.java:28:17:28:28 | readExternal | Useless serialization member found in record class $@. | Test.java:21:12:21:13 | T2 | T2 | From f2805ba80c4cddc4b76607247044ad81c39a86ef Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Mon, 7 Jul 2025 09:40:22 +0200 Subject: [PATCH 6/7] Improve query help --- .../Records/UselessMembersOfTheRecordsClass.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/ql/src/Violations of Best Practice/Records/UselessMembersOfTheRecordsClass.md b/java/ql/src/Violations of Best Practice/Records/UselessMembersOfTheRecordsClass.md index 27a1d25cecc9..5666971b331d 100644 --- a/java/ql/src/Violations of Best Practice/Records/UselessMembersOfTheRecordsClass.md +++ b/java/ql/src/Violations of Best Practice/Records/UselessMembersOfTheRecordsClass.md @@ -1,10 +1,10 @@ ## Overview -Record types were introduced in Java 16 as a mechanism to provide simpler data handling that is an alternative to regular classes. Record classes behave slightly differently during serialization however, namely any `writeObject`, `readObject`, `readObjectNoData`, `writeExternal`, and `readExternal` methods and `serialPersistentFields` fields declared in these classes cannot be used to affect the serialization process of any `Record` data type. +Record types were introduced in Java 16 as a mechanism to provide simpler data handling as an alternative to regular classes. However, record classes behave slightly differently during serialization. Namely any `writeObject`, `readObject`, `readObjectNoData`, `writeExternal`, and `readExternal` methods and `serialPersistentFields` fields declared in these classes cannot be used to affect the serialization process of any `Record` data type. ## Recommendation -Some level of serialization customization is offered by the Java 16 Record feature; the `writeReplace` and `readResolve` methods in a record that implements `java.io.Serializable` can be used to replace the object to be serialized. Otherwise no further customization of serialization of records is possible, and it is better to consider using a regular class implementing `java.io.Serializable` or `java.io.Externalizable` when customization is needed. +Some level of serialization customization is offered by the Java 16 Record feature. The `writeReplace` and `readResolve` methods in a record that implements `java.io.Serializable` can be used to replace the object to be serialized. Otherwise, no further customization of serialization of records is possible, and it is better to consider using a regular class implementing `java.io.Serializable` or `java.io.Externalizable` when customization is needed. ## Example From 813ce7d3f880b48a4dfbd942fa681f42e3f456fe Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Tue, 8 Jul 2025 11:03:32 +0200 Subject: [PATCH 7/7] Rename query --- .../java-code-quality-extended.qls.expected | 2 +- .../java/query-suite/java-code-quality.qls.expected | 2 +- ...d => IgnoredSerializationMembersOfRecordClass.md} | 0 ...l => IgnoredSerializationMembersOfRecordClass.ql} | 6 +++--- ...gnoredSerializationMembersOfRecordClass.expected} | 12 ++++++------ .../IgnoredSerializationMembersOfRecordClass.qlref | 2 ++ .../Test.java | 0 .../options | 0 .../UselessMembersOfTheRecordsClass.qlref | 2 -- 9 files changed, 13 insertions(+), 13 deletions(-) rename java/ql/src/Violations of Best Practice/Records/{UselessMembersOfTheRecordsClass.md => IgnoredSerializationMembersOfRecordClass.md} (100%) rename java/ql/src/Violations of Best Practice/Records/{UselessMembersOfTheRecordsClass.ql => IgnoredSerializationMembersOfRecordClass.ql} (76%) rename java/ql/test/query-tests/{UselessMembersOfTheRecordsClass/UselessMembersOfTheRecordsClass.expected => IgnoredSerializationMembersOfRecordClass/IgnoredSerializationMembersOfRecordClass.expected} (51%) create mode 100644 java/ql/test/query-tests/IgnoredSerializationMembersOfRecordClass/IgnoredSerializationMembersOfRecordClass.qlref rename java/ql/test/query-tests/{UselessMembersOfTheRecordsClass => IgnoredSerializationMembersOfRecordClass}/Test.java (100%) rename java/ql/test/query-tests/{UselessMembersOfTheRecordsClass => IgnoredSerializationMembersOfRecordClass}/options (100%) delete mode 100644 java/ql/test/query-tests/UselessMembersOfTheRecordsClass/UselessMembersOfTheRecordsClass.qlref diff --git a/java/ql/integration-tests/java/query-suite/java-code-quality-extended.qls.expected b/java/ql/integration-tests/java/query-suite/java-code-quality-extended.qls.expected index 00b8a91fb576..7a76c4ad9117 100644 --- a/java/ql/integration-tests/java/query-suite/java-code-quality-extended.qls.expected +++ b/java/ql/integration-tests/java/query-suite/java-code-quality-extended.qls.expected @@ -77,7 +77,7 @@ ql/java/ql/src/Violations of Best Practice/Naming Conventions/ConfusingMethodNam ql/java/ql/src/Violations of Best Practice/Naming Conventions/ConfusingOverloading.ql ql/java/ql/src/Violations of Best Practice/Naming Conventions/LocalShadowsFieldConfusing.ql ql/java/ql/src/Violations of Best Practice/Naming Conventions/SameNameAsSuper.ql -ql/java/ql/src/Violations of Best Practice/Records/UselessMembersOfTheRecordsClass.ql +ql/java/ql/src/Violations of Best Practice/Records/IgnoredSerializationMembersOfRecordClass.ql ql/java/ql/src/Violations of Best Practice/Undesirable Calls/CallsToStringToString.ql ql/java/ql/src/Violations of Best Practice/Undesirable Calls/DefaultToString.ql ql/java/ql/src/Violations of Best Practice/Undesirable Calls/DoNotCallFinalize.ql diff --git a/java/ql/integration-tests/java/query-suite/java-code-quality.qls.expected b/java/ql/integration-tests/java/query-suite/java-code-quality.qls.expected index df6ec8fab813..c9a36f8fe134 100644 --- a/java/ql/integration-tests/java/query-suite/java-code-quality.qls.expected +++ b/java/ql/integration-tests/java/query-suite/java-code-quality.qls.expected @@ -75,7 +75,7 @@ ql/java/ql/src/Violations of Best Practice/Naming Conventions/ConfusingMethodNam ql/java/ql/src/Violations of Best Practice/Naming Conventions/ConfusingOverloading.ql ql/java/ql/src/Violations of Best Practice/Naming Conventions/LocalShadowsFieldConfusing.ql ql/java/ql/src/Violations of Best Practice/Naming Conventions/SameNameAsSuper.ql -ql/java/ql/src/Violations of Best Practice/Records/UselessMembersOfTheRecordsClass.ql +ql/java/ql/src/Violations of Best Practice/Records/IgnoredSerializationMembersOfRecordClass.ql ql/java/ql/src/Violations of Best Practice/Undesirable Calls/CallsToStringToString.ql ql/java/ql/src/Violations of Best Practice/Undesirable Calls/DefaultToString.ql ql/java/ql/src/Violations of Best Practice/Undesirable Calls/DoNotCallFinalize.ql diff --git a/java/ql/src/Violations of Best Practice/Records/UselessMembersOfTheRecordsClass.md b/java/ql/src/Violations of Best Practice/Records/IgnoredSerializationMembersOfRecordClass.md similarity index 100% rename from java/ql/src/Violations of Best Practice/Records/UselessMembersOfTheRecordsClass.md rename to java/ql/src/Violations of Best Practice/Records/IgnoredSerializationMembersOfRecordClass.md diff --git a/java/ql/src/Violations of Best Practice/Records/UselessMembersOfTheRecordsClass.ql b/java/ql/src/Violations of Best Practice/Records/IgnoredSerializationMembersOfRecordClass.ql similarity index 76% rename from java/ql/src/Violations of Best Practice/Records/UselessMembersOfTheRecordsClass.ql rename to java/ql/src/Violations of Best Practice/Records/IgnoredSerializationMembersOfRecordClass.ql index a55b283d6e70..627f4cc669bb 100644 --- a/java/ql/src/Violations of Best Practice/Records/UselessMembersOfTheRecordsClass.ql +++ b/java/ql/src/Violations of Best Practice/Records/IgnoredSerializationMembersOfRecordClass.ql @@ -1,6 +1,6 @@ /** - * @id java/useless-member-of-the-record-class - * @name Useless serialization member of record class + * @id java/ignored-serialization-member-of-record-class + * @name Ignored serialization member of record class * @description Using certain members of a record class during serialization will result in * those members being ignored. * @previous-id java/useless-members-of-the-records-class @@ -21,4 +21,4 @@ where "writeObject", "readObject", "readObjectNoData", "writeExternal", "readExternal", "serialPersistentFields" ]) -select m, "Useless serialization member found in record class $@.", record, record.getName() +select m, "Ignored serialization member found in record class $@.", record, record.getName() diff --git a/java/ql/test/query-tests/UselessMembersOfTheRecordsClass/UselessMembersOfTheRecordsClass.expected b/java/ql/test/query-tests/IgnoredSerializationMembersOfRecordClass/IgnoredSerializationMembersOfRecordClass.expected similarity index 51% rename from java/ql/test/query-tests/UselessMembersOfTheRecordsClass/UselessMembersOfTheRecordsClass.expected rename to java/ql/test/query-tests/IgnoredSerializationMembersOfRecordClass/IgnoredSerializationMembersOfRecordClass.expected index 27195b3f5080..6bee23827743 100644 --- a/java/ql/test/query-tests/UselessMembersOfTheRecordsClass/UselessMembersOfTheRecordsClass.expected +++ b/java/ql/test/query-tests/IgnoredSerializationMembersOfRecordClass/IgnoredSerializationMembersOfRecordClass.expected @@ -1,6 +1,6 @@ -| Test.java:7:46:7:67 | serialPersistentFields | Useless serialization member found in record class $@. | Test.java:4:12:4:13 | T1 | T1 | -| Test.java:10:18:10:28 | writeObject | Useless serialization member found in record class $@. | Test.java:4:12:4:13 | T1 | T1 | -| Test.java:13:18:13:27 | readObject | Useless serialization member found in record class $@. | Test.java:4:12:4:13 | T1 | T1 | -| Test.java:16:18:16:33 | readObjectNoData | Useless serialization member found in record class $@. | Test.java:4:12:4:13 | T1 | T1 | -| Test.java:24:17:24:29 | writeExternal | Useless serialization member found in record class $@. | Test.java:21:12:21:13 | T2 | T2 | -| Test.java:28:17:28:28 | readExternal | Useless serialization member found in record class $@. | Test.java:21:12:21:13 | T2 | T2 | +| Test.java:7:46:7:67 | serialPersistentFields | Ignored serialization member found in record class $@. | Test.java:4:12:4:13 | T1 | T1 | +| Test.java:10:18:10:28 | writeObject | Ignored serialization member found in record class $@. | Test.java:4:12:4:13 | T1 | T1 | +| Test.java:13:18:13:27 | readObject | Ignored serialization member found in record class $@. | Test.java:4:12:4:13 | T1 | T1 | +| Test.java:16:18:16:33 | readObjectNoData | Ignored serialization member found in record class $@. | Test.java:4:12:4:13 | T1 | T1 | +| Test.java:24:17:24:29 | writeExternal | Ignored serialization member found in record class $@. | Test.java:21:12:21:13 | T2 | T2 | +| Test.java:28:17:28:28 | readExternal | Ignored serialization member found in record class $@. | Test.java:21:12:21:13 | T2 | T2 | diff --git a/java/ql/test/query-tests/IgnoredSerializationMembersOfRecordClass/IgnoredSerializationMembersOfRecordClass.qlref b/java/ql/test/query-tests/IgnoredSerializationMembersOfRecordClass/IgnoredSerializationMembersOfRecordClass.qlref new file mode 100644 index 000000000000..fa7814867df2 --- /dev/null +++ b/java/ql/test/query-tests/IgnoredSerializationMembersOfRecordClass/IgnoredSerializationMembersOfRecordClass.qlref @@ -0,0 +1,2 @@ +query: Violations of Best Practice/Records/IgnoredSerializationMembersOfRecordClass.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/java/ql/test/query-tests/UselessMembersOfTheRecordsClass/Test.java b/java/ql/test/query-tests/IgnoredSerializationMembersOfRecordClass/Test.java similarity index 100% rename from java/ql/test/query-tests/UselessMembersOfTheRecordsClass/Test.java rename to java/ql/test/query-tests/IgnoredSerializationMembersOfRecordClass/Test.java diff --git a/java/ql/test/query-tests/UselessMembersOfTheRecordsClass/options b/java/ql/test/query-tests/IgnoredSerializationMembersOfRecordClass/options similarity index 100% rename from java/ql/test/query-tests/UselessMembersOfTheRecordsClass/options rename to java/ql/test/query-tests/IgnoredSerializationMembersOfRecordClass/options diff --git a/java/ql/test/query-tests/UselessMembersOfTheRecordsClass/UselessMembersOfTheRecordsClass.qlref b/java/ql/test/query-tests/UselessMembersOfTheRecordsClass/UselessMembersOfTheRecordsClass.qlref deleted file mode 100644 index 5b4ab63ba4bd..000000000000 --- a/java/ql/test/query-tests/UselessMembersOfTheRecordsClass/UselessMembersOfTheRecordsClass.qlref +++ /dev/null @@ -1,2 +0,0 @@ -query: Violations of Best Practice/Records/UselessMembersOfTheRecordsClass.ql -postprocess: utils/test/InlineExpectationsTestQuery.ql