diff options
author | Matthias Andreas Benkard <code@mail.matthias.benkard.de> | 2022-03-01 13:43:50 +0100 |
---|---|---|
committer | Matthias Andreas Benkard <code@mail.matthias.benkard.de> | 2022-03-01 13:43:50 +0100 |
commit | 9006e7087bcefaecaf4c80489cb8c9e7a796d583 (patch) | |
tree | 8edf503f5b0d54c18c8c2ec24abb2769dc8685a2 | |
parent | 0ad6a55f3faa0ff8c42321edc6b27bbcc358b709 (diff) |
Ensure nullness correctness using Checker Framework.
Change-Id: Ie5a7749194313664a206e44597091a62afca9bdb
6 files changed, 91 insertions, 47 deletions
diff --git a/jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Decoder.java b/jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Decoder.java index 1dbd1fa..8a3741b 100644 --- a/jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Decoder.java +++ b/jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Decoder.java @@ -26,6 +26,7 @@ import java.util.function.Predicate; import java.util.function.UnaryOperator; import org.apiguardian.api.API; import org.apiguardian.api.API.Status; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** @@ -75,12 +76,11 @@ public abstract class Decoder<T> { * data. * @throws IllegalArgumentException if the serialized GVariant is ill-formed */ - public abstract T decode(ByteBuffer byteSlice); + public abstract @NotNull T decode(ByteBuffer byteSlice); abstract byte alignment(); - @Nullable - abstract Integer fixedSize(); + abstract @Nullable Integer fixedSize(); final boolean hasFixedSize() { return fixedSize() != null; @@ -103,7 +103,7 @@ public abstract class Decoder<T> { * @return a new, decorated {@link Decoder}. * @see java.util.stream.Stream#map */ - public final <U> Decoder<U> map(Function<T, U> function) { + public final <U> Decoder<U> map(Function<@NotNull T, @NotNull U> function) { return new MappingDecoder<>(function); } @@ -348,12 +348,12 @@ public abstract class Decoder<T> { @Override @Nullable - Integer fixedSize() { + public Integer fixedSize() { return null; } @Override - public List<U> decode(ByteBuffer byteSlice) { + public @NotNull List<U> decode(ByteBuffer byteSlice) { List<U> elements; var elementSize = elementDecoder.fixedSize(); @@ -406,12 +406,13 @@ public abstract class Decoder<T> { } @Override + @Nullable public Integer fixedSize() { return entryArrayDecoder.fixedSize(); } @Override - public Map<K, V> decode(ByteBuffer byteSlice) { + public @NotNull Map<K, V> decode(ByteBuffer byteSlice) { List<Map.Entry<K, V>> entries = entryArrayDecoder.decode(byteSlice); return entries.stream().collect(toMap(Entry::getKey, Entry::getValue)); } @@ -433,7 +434,7 @@ public abstract class Decoder<T> { } @Override - public byte[] decode(ByteBuffer byteSlice) { + public byte @NotNull [] decode(ByteBuffer byteSlice) { // A simple C-style array. byte[] elements = new byte[byteSlice.limit() / ELEMENT_SIZE]; byteSlice.get(elements); @@ -461,7 +462,7 @@ public abstract class Decoder<T> { } @Override - public Optional<U> decode(ByteBuffer byteSlice) { + public @NotNull Optional<U> decode(ByteBuffer byteSlice) { if (!byteSlice.hasRemaining()) { return Optional.empty(); } else { @@ -498,12 +499,12 @@ public abstract class Decoder<T> { } @Override - public Integer fixedSize() { + public @Nullable Integer fixedSize() { return tupleDecoder.fixedSize(); } @Override - public U decode(ByteBuffer byteSlice) { + public @NotNull U decode(ByteBuffer byteSlice) { Object[] recordConstructorArguments = tupleDecoder.decode(byteSlice); try { @@ -536,7 +537,7 @@ public abstract class Decoder<T> { } @Override - public Integer fixedSize() { + public @Nullable Integer fixedSize() { int position = 0; for (var componentDecoder : componentDecoders) { var fixedComponentSize = componentDecoder.fixedSize(); @@ -556,7 +557,7 @@ public abstract class Decoder<T> { } @Override - public Object[] decode(ByteBuffer byteSlice) { + public Object @NotNull [] decode(ByteBuffer byteSlice) { int framingOffsetSize = byteCount(byteSlice.limit()); var objects = new Object[componentDecoders.length]; @@ -616,13 +617,13 @@ public abstract class Decoder<T> { } @Override - public Integer fixedSize() { + public @Nullable Integer fixedSize() { return tupleDecoder.fixedSize(); } @Override @SuppressWarnings("unchecked") - public Map.Entry<K, V> decode(ByteBuffer byteSlice) { + public Map.@NotNull Entry<K, V> decode(ByteBuffer byteSlice) { Object[] components = tupleDecoder.decode(byteSlice); return new SimpleImmutableEntry<>((K) components[0], (V) components[1]); } @@ -642,7 +643,7 @@ public abstract class Decoder<T> { } @Override - public Variant decode(ByteBuffer byteSlice) { + public @NotNull Variant decode(ByteBuffer byteSlice) { for (int i = byteSlice.limit() - 1; i >= 0; --i) { if (byteSlice.get(i) != 0) { continue; @@ -678,7 +679,7 @@ public abstract class Decoder<T> { } @Override - public Boolean decode(ByteBuffer byteSlice) { + public @NotNull Boolean decode(ByteBuffer byteSlice) { return byteSlice.get() != 0; } } @@ -696,7 +697,7 @@ public abstract class Decoder<T> { } @Override - public Byte decode(ByteBuffer byteSlice) { + public @NotNull Byte decode(ByteBuffer byteSlice) { return byteSlice.get(); } } @@ -714,7 +715,7 @@ public abstract class Decoder<T> { } @Override - public Short decode(ByteBuffer byteSlice) { + public @NotNull Short decode(ByteBuffer byteSlice) { return byteSlice.getShort(); } } @@ -732,7 +733,7 @@ public abstract class Decoder<T> { } @Override - public Integer decode(ByteBuffer byteSlice) { + public @NotNull Integer decode(ByteBuffer byteSlice) { return byteSlice.getInt(); } } @@ -750,7 +751,7 @@ public abstract class Decoder<T> { } @Override - public Long decode(ByteBuffer byteSlice) { + public @NotNull Long decode(ByteBuffer byteSlice) { return byteSlice.getLong(); } } @@ -768,7 +769,7 @@ public abstract class Decoder<T> { } @Override - public Double decode(ByteBuffer byteSlice) { + public @NotNull Double decode(ByteBuffer byteSlice) { return byteSlice.getDouble(); } } @@ -793,7 +794,7 @@ public abstract class Decoder<T> { } @Override - public String decode(ByteBuffer byteSlice) { + public @NotNull String decode(ByteBuffer byteSlice) { byteSlice.limit(byteSlice.limit() - 1); return charset.decode(byteSlice).toString(); } @@ -801,9 +802,9 @@ public abstract class Decoder<T> { private class MappingDecoder<U> extends Decoder<U> { - private final Function<T, U> function; + private final Function<@NotNull T, @NotNull U> function; - MappingDecoder(Function<T, U> function) { + MappingDecoder(Function<@NotNull T, @NotNull U> function) { this.function = function; } @@ -818,7 +819,7 @@ public abstract class Decoder<T> { } @Override - public U decode(ByteBuffer byteSlice) { + public @NotNull U decode(ByteBuffer byteSlice) { return function.apply(Decoder.this.decode(byteSlice)); } } @@ -842,7 +843,7 @@ public abstract class Decoder<T> { } @Override - public T decode(ByteBuffer byteSlice) { + public @NotNull T decode(ByteBuffer byteSlice) { var transformedBuffer = function.apply(byteSlice.asReadOnlyBuffer().order(byteSlice.order())); return Decoder.this.decode(transformedBuffer); } @@ -867,7 +868,7 @@ public abstract class Decoder<T> { } @Override - public T decode(ByteBuffer byteSlice) { + public @NotNull T decode(ByteBuffer byteSlice) { var newByteSlice = byteSlice.duplicate(); newByteSlice.order(byteOrder); return Decoder.this.decode(newByteSlice); @@ -898,7 +899,9 @@ public abstract class Decoder<T> { if (!Objects.equals(thenDecoder.fixedSize(), elseDecoder.fixedSize())) { throw new IllegalArgumentException( "incompatible sizes in predicate branches: then=%s, else=%s" - .formatted(thenDecoder.fixedSize(), elseDecoder.fixedSize())); + .formatted( + Objects.requireNonNullElse(thenDecoder.fixedSize(), "(null)"), + Objects.requireNonNullElse(elseDecoder.fixedSize(), "(null)"))); } } @@ -913,7 +916,7 @@ public abstract class Decoder<T> { } @Override - public U decode(ByteBuffer byteSlice) { + public @NotNull U decode(ByteBuffer byteSlice) { var b = selector.test(byteSlice); byteSlice.rewind(); return b ? thenDecoder.decode(byteSlice) : elseDecoder.decode(byteSlice); diff --git a/jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Signature.java b/jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Signature.java index d61b9d1..cc9674d 100644 --- a/jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Signature.java +++ b/jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Signature.java @@ -12,6 +12,7 @@ import java.util.List; import java.util.Objects; import org.apiguardian.api.API; import org.apiguardian.api.API.Status; +import org.jetbrains.annotations.Nullable; /** * A GVariant signature string. @@ -73,7 +74,7 @@ public final class Signature { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { return (o instanceof Signature signature) && Objects.equals(signatureString, signature.signatureString); } diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/ByteString.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/ByteString.java index 3a8185c..cfe3635 100644 --- a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/ByteString.java +++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/ByteString.java @@ -10,6 +10,7 @@ import java.util.Base64; import java.util.HexFormat; import java.util.stream.IntStream; import java.util.stream.Stream; +import org.jetbrains.annotations.Nullable; /** * A wrapper for a {@code byte[]} that implements {@link #equals(Object)}, {@link #hashCode()}, and @@ -31,7 +32,7 @@ public record ByteString(byte[] bytes) { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { return (o instanceof ByteString byteString) && Arrays.equals(bytes, byteString.bytes); } diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaSuperblock.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaSuperblock.java index 52b970f..50da203 100644 --- a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaSuperblock.java +++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaSuperblock.java @@ -70,20 +70,20 @@ public record DeltaSuperblock( Decoder.ofByteArray().map(DeltaSuperblock::parseDeltaNameList), Decoder.ofArray(DeltaMetaEntry.decoder()).withByteOrder(ByteOrder.LITTLE_ENDIAN), Decoder.ofArray(DeltaFallback.decoder()).withByteOrder(ByteOrder.LITTLE_ENDIAN)) - .map( - deltaSuperblock -> { - // Fix up the endianness of the 'entries' and 'fallbacks' fields, which have - // unspecified byte order. - var endiannessMetadatum = - deltaSuperblock.metadata().fields().get("ostree.endianness"); - if (endiannessMetadatum != null - && endiannessMetadatum.value() instanceof Byte endiannessByte - && endiannessByte == (byte) 'B') { - return deltaSuperblock.byteSwapped(); - } else { - return deltaSuperblock; - } - }); + .map(DeltaSuperblock::byteSwappedIfBigEndian); + + private DeltaSuperblock byteSwappedIfBigEndian() { + // Fix up the endianness of the 'entries' and 'fallbacks' fields, which have + // unspecified byte order. + var endiannessMetadatum = metadata().fields().get("ostree.endianness"); + if (endiannessMetadatum != null + && endiannessMetadatum.value() instanceof Byte endiannessByte + && endiannessByte == (byte) 'B') { + return byteSwapped(); + } else { + return this; + } + } private DeltaSuperblock byteSwapped() { return new DeltaSuperblock( diff --git a/jgvariant-ostree/src/test/java/eu/mulk/jgvariant/ostree/OstreeDecoderTest.java b/jgvariant-ostree/src/test/java/eu/mulk/jgvariant/ostree/OstreeDecoderTest.java index 5c1dd79..d8ad271 100644 --- a/jgvariant-ostree/src/test/java/eu/mulk/jgvariant/ostree/OstreeDecoderTest.java +++ b/jgvariant-ostree/src/test/java/eu/mulk/jgvariant/ostree/OstreeDecoderTest.java @@ -17,6 +17,7 @@ import java.util.Map; import org.junit.jupiter.api.Test; @TestWithResources +@SuppressWarnings("initialization.field.uninitialized") class OstreeDecoderTest { @GivenBinaryResource("/ostree/summary") diff --git a/jgvariant-parent/pom.xml b/jgvariant-parent/pom.xml index fe0c1ed..92b7e07 100644 --- a/jgvariant-parent/pom.xml +++ b/jgvariant-parent/pom.xml @@ -74,6 +74,7 @@ SPDX-License-Identifier: LGPL-3.0-or-later <jetbrains-annotations.version>22.0.0</jetbrains-annotations.version> <junit-jupiter.version>5.8.2</junit-jupiter.version> <xz.version>1.9</xz.version> + <checker-framework.version>3.21.1</checker-framework.version> </properties> <distributionManagement> @@ -102,6 +103,18 @@ SPDX-License-Identifier: LGPL-3.0-or-later <version>${apiguardian.version}</version> </dependency> + <!-- Static analysis --> + <dependency> + <groupId>org.checkerframework</groupId> + <artifactId>checker</artifactId> + <version>${checker-framework.version}</version> + </dependency> + <dependency> + <groupId>org.checkerframework</groupId> + <artifactId>checker-qual</artifactId> + <version>${checker-framework.version}</version> + </dependency> + <!-- OSTree compression support --> <dependency> <groupId>org.tukaani</groupId> @@ -131,6 +144,22 @@ SPDX-License-Identifier: LGPL-3.0-or-later </dependencies> </dependencyManagement> + <dependencies> + <!-- Static analysis --> + <dependency> + <groupId>org.checkerframework</groupId> + <artifactId>checker</artifactId> + <scope>provided</scope> + <optional>true</optional> + </dependency> + <dependency> + <groupId>org.checkerframework</groupId> + <artifactId>checker-qual</artifactId> + <scope>provided</scope> + <optional>true</optional> + </dependency> + </dependencies> + <build> <pluginManagement> @@ -166,8 +195,9 @@ SPDX-License-Identifier: LGPL-3.0-or-later <fork>true</fork> <compilerArgs> <arg>-XDcompilePolicy=simple</arg> - <arg>-Xplugin:ErrorProne</arg> + <arg>-Xplugin:ErrorProne -Xep:InvalidParam:OFF</arg> <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</arg> + <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</arg> <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED</arg> <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED</arg> <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED</arg> @@ -184,7 +214,15 @@ SPDX-License-Identifier: LGPL-3.0-or-later <artifactId>error_prone_core</artifactId> <version>${errorprone.version}</version> </path> + <path> + <groupId>org.checkerframework</groupId> + <artifactId>checker</artifactId> + <version>${checker-framework.version}</version> + </path> </annotationProcessorPaths> + <annotationProcessors> + <annotationProcessor>org.checkerframework.checker.nullness.NullnessChecker</annotationProcessor> + </annotationProcessors> </configuration> </plugin> |