Skip to content

Commit

Permalink
Use JSpecify annotations in the public release.
Browse files Browse the repository at this point in the history
We've been using these internally for over a year. With JSpecify 1.0 not far off and wider adoption to gradually follow, now seems like the time to expand our public usage of the annotations. We've already been using them in a few projects, but Truth may become the mostly widely used project that users actually link against to use them. At the same time, it's a library that's used only in testing code, so the stakes remain relatively low.

Most users will see no effect from this change, since most users don't use nullness checking and since we already used some nullness annotations in our public release. The main effect users are likely to see is if they pass nullable values for parameters that are now recognized as non-nullable. Under Kotlin, the effect should normally be a warning, not an error, at least until [Kotlin 2.1 or so](https://youtrack.jetbrains.com/issue/KT-55586/Handle-nullability-from-jspecify-annotations-properly#focus=Comments-27-8368666.0-0). Please still [report any problems](https://github.com/google/truth/issues/new).

(progress toward JSpecify adoption in our projects in general, including google/guava#2960)

RELNOTES=Added more nullness information to our APIs (in the form of [JSpecify](https://jspecify.dev/) annotations). This could lead to additional warnings (or even errors) for users of Kotlin and other nullness checkers. Please [report any problems](https://github.com/google/truth/issues/new).
PiperOrigin-RevId: 647012817
cpovirk authored and Google Java Core Libraries committed Jun 26, 2024
1 parent d39e722 commit ee680cb
Showing 92 changed files with 198 additions and 88 deletions.
4 changes: 2 additions & 2 deletions core/pom.xml
Original file line number Diff line number Diff line change
@@ -15,8 +15,8 @@
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>org.checkerframework</groupId>
<artifactId>checker-qual</artifactId>
<groupId>org.jspecify</groupId>
<artifactId>jspecify</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
Original file line number Diff line number Diff line change
@@ -20,13 +20,15 @@
import static com.google.common.truth.Fact.simpleFact;

import java.lang.reflect.Array;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

/**
* A common supertype for Array subjects, abstracting some common display and error infrastructure.
*
* @author Christian Gruber (cgruber@israfil.net)
*/
@NullMarked
abstract class AbstractArraySubject extends Subject {
private final @Nullable Object actual;

Original file line number Diff line number Diff line change
@@ -33,7 +33,8 @@
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Map.Entry;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Handle;
@@ -69,6 +70,7 @@
*/
@GwtIncompatible
@J2ktIncompatible
@NullMarked
final class ActualValueInference {
/** <b>Call {@link Platform#inferDescription} rather than calling this directly.</b> */
static @Nullable String describeActualValue(String className, String methodName, int lineNumber) {
Original file line number Diff line number Diff line change
@@ -19,13 +19,15 @@
import static com.google.common.truth.Fact.makeMessage;

import com.google.common.collect.ImmutableList;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

/**
* An {@link AssertionError} composed of structured {@link Fact} instances and other string
* messages.
*/
@SuppressWarnings("OverrideThrowableToString") // We intentionally hide the class name.
@NullMarked
final class AssertionErrorWithFacts extends AssertionError implements ErrorWithFacts {
private final ImmutableList<Fact> facts;

Original file line number Diff line number Diff line change
@@ -20,13 +20,15 @@
import static com.google.common.truth.Fact.simpleFact;

import java.math.BigDecimal;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

/**
* Propositions for {@link BigDecimal} typed subjects.
*
* @author Kurt Alfred Kluever
*/
@NullMarked
public final class BigDecimalSubject extends ComparableSubject<BigDecimal> {
private final @Nullable BigDecimal actual;

Original file line number Diff line number Diff line change
@@ -17,13 +17,15 @@

import static com.google.common.truth.Fact.simpleFact;

import org.checkerframework.checker.nullness.qual.Nullable;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

/**
* Propositions for boolean subjects.
*
* @author Christian Gruber (cgruber@israfil.net)
*/
@NullMarked
public final class BooleanSubject extends Subject {
private final @Nullable Boolean actual;

4 changes: 3 additions & 1 deletion core/src/main/java/com/google/common/truth/ClassSubject.java
Original file line number Diff line number Diff line change
@@ -18,13 +18,15 @@
import static com.google.common.base.Preconditions.checkNotNull;

import com.google.common.annotations.GwtIncompatible;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

/**
* Propositions for {@link Class} subjects.
*
* @author Kurt Alfred Kluever
*/
@NullMarked
@GwtIncompatible("reflection")
@J2ktIncompatible
public final class ClassSubject extends Subject {
Original file line number Diff line number Diff line change
@@ -18,14 +18,16 @@
import static com.google.common.base.Preconditions.checkNotNull;

import com.google.common.collect.Range;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

/**
* Propositions for {@link Comparable} typed subjects.
*
* @author Kurt Alfred Kluever
* @param <T> the type of the object being tested by this {@code ComparableSubject}
*/
@NullMarked
// TODO(b/136040841): Consider further tightening this to the proper `extends Comparable<? super T>`
public abstract class ComparableSubject<T extends Comparable<?>> extends Subject {
/**
Original file line number Diff line number Diff line change
@@ -21,12 +21,14 @@

import com.google.common.collect.ImmutableList;
import com.google.common.truth.Platform.PlatformComparisonFailure;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

/**
* An {@link AssertionError} (usually a JUnit {@code ComparisonFailure}, but not under GWT) composed
* of structured {@link Fact} instances and other string messages.
*/
@NullMarked
final class ComparisonFailureWithFacts extends PlatformComparisonFailure implements ErrorWithFacts {
private final ImmutableList<Fact> facts;

Original file line number Diff line number Diff line change
@@ -26,7 +26,8 @@

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

/**
* Contains part of the code responsible for creating a JUnit {@code ComparisonFailure} (if
@@ -42,6 +43,7 @@
* different implementation under GWT/j2cl, where {@code ComparisonFailure} is also unavailable but
* we can't just recover from that at runtime.
*/
@NullMarked
final class ComparisonFailures {
static ImmutableList<Fact> makeComparisonFailureFacts(
ImmutableList<Fact> headFacts,
Original file line number Diff line number Diff line change
@@ -31,7 +31,8 @@
import com.google.common.collect.ImmutableList;
import java.util.Arrays;
import java.util.List;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

/**
* Determines whether an instance of type {@code A} corresponds in some way to an instance of type
@@ -65,6 +66,7 @@
*
* @author Pete Gillin
*/
@NullMarked
public abstract class Correspondence<A extends @Nullable Object, E extends @Nullable Object> {

/**
Original file line number Diff line number Diff line change
@@ -18,6 +18,7 @@

import static com.google.common.base.Preconditions.checkNotNull;

import org.jspecify.annotations.NullMarked;

/**
* In a fluent assertion chain, exposes one or more "custom" {@code that} methods, which accept a
@@ -35,6 +36,7 @@
* extensions</a>. It explains the cases in which {@code CustomSubjectBuilder} is necessary, and it
* links to further instructions.
*/
@NullMarked
public abstract class CustomSubjectBuilder {
/**
* In a fluent assertion chain, the argument to the "custom" overload of {@link
2 changes: 2 additions & 0 deletions core/src/main/java/com/google/common/truth/DiffUtils.java
Original file line number Diff line number Diff line change
@@ -23,13 +23,15 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jspecify.annotations.NullMarked;

/**
* A custom implementation of the diff algorithm based on the solution described at
* https://en.wikipedia.org/wiki/Longest_common_subsequence_problem
*
* @author Yun Peng (pcloudy@google.com)
*/
@NullMarked
final class DiffUtils {
// A list of unique strings appeared in compared texts.
// The index of each string is its incremental Id.
Original file line number Diff line number Diff line change
@@ -26,13 +26,15 @@
import static java.lang.Double.NaN;
import static java.lang.Double.doubleToLongBits;

import org.checkerframework.checker.nullness.qual.Nullable;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

/**
* Propositions for {@link Double} subjects.
*
* @author Kurt Alfred Kluever
*/
@NullMarked
public final class DoubleSubject extends ComparableSubject<Double> {
private static final long NEG_ZERO_BITS = doubleToLongBits(-0.0);

Original file line number Diff line number Diff line change
@@ -17,11 +17,13 @@
package com.google.common.truth;

import com.google.common.collect.ImmutableList;
import org.jspecify.annotations.NullMarked;

/**
* Supertype of Truth's {@link AssertionError} subclasses that are created from a list of {@link
* Fact} instances.
*/
@NullMarked
interface ErrorWithFacts {
ImmutableList<Fact> facts();
}
4 changes: 3 additions & 1 deletion core/src/main/java/com/google/common/truth/Expect.java
Original file line number Diff line number Diff line change
@@ -30,7 +30,8 @@
import com.google.errorprone.annotations.concurrent.GuardedBy;
import java.util.ArrayList;
import java.util.List;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import org.junit.internal.AssumptionViolatedException;
import org.junit.rules.ErrorCollector;
import org.junit.rules.TestRule;
@@ -83,6 +84,7 @@
*/
@GwtIncompatible("JUnit4")
@J2ktIncompatible
@NullMarked
public final class Expect extends StandardSubjectBuilder implements TestRule {

private static final class ExpectationGatherer implements FailureStrategy {
Original file line number Diff line number Diff line change
@@ -24,7 +24,8 @@
import com.google.common.annotations.GwtIncompatible;
import com.google.common.truth.Truth.SimpleAssertionError;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;

@@ -66,6 +67,7 @@
* also checks that the assertion you're testing uses the supplied {@link FailureStrategy} and calls
* {@link FailureStrategy#fail} only once.
*/
@NullMarked
public final class ExpectFailure implements Platform.JUnitTestRule {
private boolean inRuleContext = false;
private boolean failureExpected = false;
4 changes: 3 additions & 1 deletion core/src/main/java/com/google/common/truth/Fact.java
Original file line number Diff line number Diff line change
@@ -22,7 +22,8 @@

import com.google.common.collect.ImmutableList;
import java.io.Serializable;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

/**
* A string key-value pair in a failure message, such as "expected: abc" or "but was: xyz."
@@ -34,6 +35,7 @@
* <p>If you are writing a custom {@code Subject}, see <a
* href="https://truth.dev/failure_messages">our tips on writing failure messages</a>.
*/
@NullMarked
public final class Fact implements Serializable {
/**
* Creates a fact with the given key and value, which will be printed in a format like "key:
Original file line number Diff line number Diff line change
@@ -29,7 +29,8 @@

import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

/**
* An opaque, immutable object containing state from the previous calls in the fluent assertion
@@ -51,6 +52,7 @@
* using their {@link CustomSubjectBuilder#metadata()} method to get an instance to pass to the
* constructor.)
*/
@NullMarked
public final class FailureMetadata {
static FailureMetadata forFailureStrategy(FailureStrategy failureStrategy) {
return new FailureMetadata(
Original file line number Diff line number Diff line change
@@ -15,6 +15,7 @@
*/
package com.google.common.truth;

import org.jspecify.annotations.NullMarked;

/**
* Defines what to do when a check fails.
@@ -53,6 +54,7 @@
* StandardSubjectBuilder#forCustomFailureStrategy
* StandardSubjectBuilder.forCustomFailureStrategy(STRATEGY)}.
*/
@NullMarked
public interface FailureStrategy {
/**
* Handles a failure. The parameter is an {@code AssertionError} or subclass thereof, and it
4 changes: 3 additions & 1 deletion core/src/main/java/com/google/common/truth/FloatSubject.java
Original file line number Diff line number Diff line change
@@ -26,13 +26,15 @@
import static java.lang.Float.NaN;
import static java.lang.Float.floatToIntBits;

import org.checkerframework.checker.nullness.qual.Nullable;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

/**
* Propositions for {@link Float} subjects.
*
* @author Kurt Alfred Kluever
*/
@NullMarked
public final class FloatSubject extends ComparableSubject<Float> {
private static final int NEG_ZERO_BITS = floatToIntBits(-0.0f);

2 changes: 2 additions & 0 deletions core/src/main/java/com/google/common/truth/GraphMatching.java
Original file line number Diff line number Diff line change
@@ -27,13 +27,15 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Queue;
import org.jspecify.annotations.NullMarked;

/**
* Helper routines related to <a href="https://en.wikipedia.org/wiki/Matching_(graph_theory)">graph
* matchings</a>.
*
* @author Pete Gillin
*/
@NullMarked
final class GraphMatching {

/**
Original file line number Diff line number Diff line change
@@ -19,7 +19,8 @@
import static com.google.common.truth.Fact.simpleFact;

import com.google.common.base.Optional;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

/**
* Propositions for Guava {@link Optional} subjects.
@@ -28,6 +29,7 @@
*
* @author Christian Gruber
*/
@NullMarked
public final class GuavaOptionalSubject extends Subject {
@SuppressWarnings("NullableOptional") // Truth always accepts nulls, no matter the type
private final @Nullable Optional<?> actual;
Original file line number Diff line number Diff line change
@@ -21,9 +21,11 @@
import static java.lang.annotation.ElementType.TYPE;

import java.lang.annotation.Target;
import org.jspecify.annotations.NullMarked;

/**
* Disables Animal Sniffer's checking of compatibility with older versions of Java/Android.
*/
@Target({METHOD, CONSTRUCTOR, TYPE})
@NullMarked
@interface IgnoreJRERequirement {}
Loading

0 comments on commit ee680cb

Please sign in to comment.