aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcpovirk <cpovirk@google.com>2021-09-24 08:19:21 -0700
committerGoogle Java Core Libraries <java-core-libraries-team+copybara@google.com>2021-09-24 08:22:07 -0700
commitfe3b76240b05c5421e7fe84862705db582f51f55 (patch)
treedaf4dd7f6cbafae35634c820b0b0284050c15c38
parentf9b25b423ac6c7201d77387efe079ff540954de2 (diff)
downloadguava-fe3b76240b05c5421e7fe84862705db582f51f55.tar.gz
Annotate `ClassToInstanceMap` and `MutableClassToInstanceMap` for nullness.
The docs in `ClassToInstanceMap` describe some compromises we have to make to do this as best we can with our existing nullness annotations. RELNOTES=n/a PiperOrigin-RevId: 398734814
-rw-r--r--android/guava/src/com/google/common/collect/ClassToInstanceMap.java24
-rw-r--r--android/guava/src/com/google/common/collect/ImmutableClassToInstanceMap.java1
-rw-r--r--android/guava/src/com/google/common/collect/MutableClassToInstanceMap.java27
-rw-r--r--guava/src/com/google/common/collect/ClassToInstanceMap.java24
-rw-r--r--guava/src/com/google/common/collect/ImmutableClassToInstanceMap.java1
-rw-r--r--guava/src/com/google/common/collect/MutableClassToInstanceMap.java27
6 files changed, 88 insertions, 16 deletions
diff --git a/android/guava/src/com/google/common/collect/ClassToInstanceMap.java b/android/guava/src/com/google/common/collect/ClassToInstanceMap.java
index 8d454c0c6..9bd826fd9 100644
--- a/android/guava/src/com/google/common/collect/ClassToInstanceMap.java
+++ b/android/guava/src/com/google/common/collect/ClassToInstanceMap.java
@@ -20,7 +20,7 @@ import com.google.common.annotations.GwtCompatible;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.annotations.DoNotMock;
import java.util.Map;
-import org.checkerframework.checker.nullness.compatqual.NullableDecl;
+import javax.annotation.CheckForNull;
/**
* A map, each entry of which maps a Java <a href="http://tinyurl.com/2cmwkz">raw type</a> to an
@@ -30,8 +30,19 @@ import org.checkerframework.checker.nullness.compatqual.NullableDecl;
* <p>Like any other {@code Map<Class, Object>}, this map may contain entries for primitive types,
* and a primitive type and its corresponding wrapper type may map to different values.
*
+ * <p>This class's support for {@code null} requires some explanation: From release 31.0 onward,
+ * Guava specifies the nullness of its types through annotations. In the case of {@code
+ * ClassToInstanceMap}, it specifies that both the key and value types are restricted to
+ * non-nullable types. This specification is reasonable for <i>keys</i>, which must be non-null
+ * classes. This is in contrast to the specification for <i>values</i>: Null values <i>are</i>
+ * supported by the implementation {@link MutableClassToInstanceMap}, even though that
+ * implementation and this interface specify otherwise. Thus, if you use a nullness checker, you can
+ * safely suppress any warnings it produces when you write null values into a {@code
+ * MutableClassToInstanceMap}. Just be sure to be prepared for null values when reading from it,
+ * since nullness checkers will assume that vaules are non-null then, too.
+ *
* <p>See the Guava User Guide article on <a href=
- * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#classtoinstancemap"> {@code
+ * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#classtoinstancemap">{@code
* ClassToInstanceMap}</a>.
*
* <p>To map a generic type to an instance of that type, use {@link
@@ -43,12 +54,18 @@ import org.checkerframework.checker.nullness.compatqual.NullableDecl;
*/
@DoNotMock("Use ImmutableClassToInstanceMap or MutableClassToInstanceMap")
@GwtCompatible
+@ElementTypesAreNonnullByDefault
+// If we ever support non-null projections (https://github.com/jspecify/jspecify/issues/86), we
+// we might annotate this as...
+// ClassToInstanceMap<B extends @Nullable Object> extends Map<Class<? extends @Nonnull B>, B>
+// ...and change its methods similarly (<T extends @Nonnull B> or Class<@Nonnull T>).
public interface ClassToInstanceMap<B> extends Map<Class<? extends B>, B> {
/**
* Returns the value the specified class is mapped to, or {@code null} if no entry for this class
* is present. This will only return a value that was bound to this specific class, not a value
* that may have been bound to a subtype.
*/
+ @CheckForNull
<T extends B> T getInstance(Class<T> type);
/**
@@ -59,5 +76,6 @@ public interface ClassToInstanceMap<B> extends Map<Class<? extends B>, B> {
* null} if there was no previous entry.
*/
@CanIgnoreReturnValue
- <T extends B> T putInstance(Class<T> type, @NullableDecl T value);
+ @CheckForNull
+ <T extends B> T putInstance(Class<T> type, T value);
}
diff --git a/android/guava/src/com/google/common/collect/ImmutableClassToInstanceMap.java b/android/guava/src/com/google/common/collect/ImmutableClassToInstanceMap.java
index 337b4008e..aeabe5d3a 100644
--- a/android/guava/src/com/google/common/collect/ImmutableClassToInstanceMap.java
+++ b/android/guava/src/com/google/common/collect/ImmutableClassToInstanceMap.java
@@ -190,7 +190,6 @@ public final class ImmutableClassToInstanceMap<B> extends ForwardingMap<Class<?
@Override
@DoNotCall("Always throws UnsupportedOperationException")
@CheckForNull
- @SuppressWarnings("nullness") // TODO(cpovirk): Remove after annotating supertype.
public <T extends B> T putInstance(Class<T> type, T value) {
throw new UnsupportedOperationException();
}
diff --git a/android/guava/src/com/google/common/collect/MutableClassToInstanceMap.java b/android/guava/src/com/google/common/collect/MutableClassToInstanceMap.java
index 1f228a8f6..2fcf0d664 100644
--- a/android/guava/src/com/google/common/collect/MutableClassToInstanceMap.java
+++ b/android/guava/src/com/google/common/collect/MutableClassToInstanceMap.java
@@ -27,20 +27,26 @@ import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
+import javax.annotation.CheckForNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
/**
* A mutable class-to-instance map backed by an arbitrary user-provided map. See also {@link
* ImmutableClassToInstanceMap}.
*
* <p>See the Guava User Guide article on <a href=
- * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#classtoinstancemap"> {@code
+ * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#classtoinstancemap">{@code
* ClassToInstanceMap}</a>.
*
+ * <p>This implementation <i>does</i> support null values, despite how it is annotated; see
+ * discussion at {@link ClassToInstanceMap}.
+ *
* @author Kevin Bourrillion
* @since 2.0
*/
@GwtIncompatible
@SuppressWarnings("serial") // using writeReplace instead of standard serialization
+@ElementTypesAreNonnullByDefault
public final class MutableClassToInstanceMap<B> extends ForwardingMap<Class<? extends B>, B>
implements ClassToInstanceMap<B>, Serializable {
@@ -108,11 +114,20 @@ public final class MutableClassToInstanceMap<B> extends ForwardingMap<Class<? ex
@Override
public Object[] toArray() {
- return standardToArray();
+ /*
+ * standardToArray returns `@Nullable Object[]` rather than `Object[]` but only because it
+ * can be used with collections that may contain null. This collection is a collection of
+ * non-null Entry objects (Entry objects that might contain null values but are not
+ * themselves null), so we can treat it as a plain `Object[]`.
+ */
+ @SuppressWarnings("nullness")
+ Object[] result = standardToArray();
+ return result;
}
@Override
- public <T> T[] toArray(T[] array) {
+ @SuppressWarnings("nullness") // b/192354773 in our checker affects toArray declarations
+ public <T extends @Nullable Object> T[] toArray(T[] array) {
return standardToArray(array);
}
};
@@ -120,6 +135,7 @@ public final class MutableClassToInstanceMap<B> extends ForwardingMap<Class<? ex
@Override
@CanIgnoreReturnValue
+ @CheckForNull
public B put(Class<? extends B> key, B value) {
return super.put(key, cast(key, value));
}
@@ -135,17 +151,20 @@ public final class MutableClassToInstanceMap<B> extends ForwardingMap<Class<? ex
@CanIgnoreReturnValue
@Override
+ @CheckForNull
public <T extends B> T putInstance(Class<T> type, T value) {
return cast(type, put(type, value));
}
@Override
+ @CheckForNull
public <T extends B> T getInstance(Class<T> type) {
return cast(type, get(type));
}
@CanIgnoreReturnValue
- private static <B, T extends B> T cast(Class<T> type, B value) {
+ @CheckForNull
+ private static <B, T extends B> T cast(Class<T> type, @CheckForNull B value) {
return Primitives.wrap(type).cast(value);
}
diff --git a/guava/src/com/google/common/collect/ClassToInstanceMap.java b/guava/src/com/google/common/collect/ClassToInstanceMap.java
index a173556b1..9bd826fd9 100644
--- a/guava/src/com/google/common/collect/ClassToInstanceMap.java
+++ b/guava/src/com/google/common/collect/ClassToInstanceMap.java
@@ -20,7 +20,7 @@ import com.google.common.annotations.GwtCompatible;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.annotations.DoNotMock;
import java.util.Map;
-import org.checkerframework.checker.nullness.qual.Nullable;
+import javax.annotation.CheckForNull;
/**
* A map, each entry of which maps a Java <a href="http://tinyurl.com/2cmwkz">raw type</a> to an
@@ -30,8 +30,19 @@ import org.checkerframework.checker.nullness.qual.Nullable;
* <p>Like any other {@code Map<Class, Object>}, this map may contain entries for primitive types,
* and a primitive type and its corresponding wrapper type may map to different values.
*
+ * <p>This class's support for {@code null} requires some explanation: From release 31.0 onward,
+ * Guava specifies the nullness of its types through annotations. In the case of {@code
+ * ClassToInstanceMap}, it specifies that both the key and value types are restricted to
+ * non-nullable types. This specification is reasonable for <i>keys</i>, which must be non-null
+ * classes. This is in contrast to the specification for <i>values</i>: Null values <i>are</i>
+ * supported by the implementation {@link MutableClassToInstanceMap}, even though that
+ * implementation and this interface specify otherwise. Thus, if you use a nullness checker, you can
+ * safely suppress any warnings it produces when you write null values into a {@code
+ * MutableClassToInstanceMap}. Just be sure to be prepared for null values when reading from it,
+ * since nullness checkers will assume that vaules are non-null then, too.
+ *
* <p>See the Guava User Guide article on <a href=
- * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#classtoinstancemap"> {@code
+ * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#classtoinstancemap">{@code
* ClassToInstanceMap}</a>.
*
* <p>To map a generic type to an instance of that type, use {@link
@@ -43,12 +54,18 @@ import org.checkerframework.checker.nullness.qual.Nullable;
*/
@DoNotMock("Use ImmutableClassToInstanceMap or MutableClassToInstanceMap")
@GwtCompatible
+@ElementTypesAreNonnullByDefault
+// If we ever support non-null projections (https://github.com/jspecify/jspecify/issues/86), we
+// we might annotate this as...
+// ClassToInstanceMap<B extends @Nullable Object> extends Map<Class<? extends @Nonnull B>, B>
+// ...and change its methods similarly (<T extends @Nonnull B> or Class<@Nonnull T>).
public interface ClassToInstanceMap<B> extends Map<Class<? extends B>, B> {
/**
* Returns the value the specified class is mapped to, or {@code null} if no entry for this class
* is present. This will only return a value that was bound to this specific class, not a value
* that may have been bound to a subtype.
*/
+ @CheckForNull
<T extends B> T getInstance(Class<T> type);
/**
@@ -59,5 +76,6 @@ public interface ClassToInstanceMap<B> extends Map<Class<? extends B>, B> {
* null} if there was no previous entry.
*/
@CanIgnoreReturnValue
- <T extends B> T putInstance(Class<T> type, @Nullable T value);
+ @CheckForNull
+ <T extends B> T putInstance(Class<T> type, T value);
}
diff --git a/guava/src/com/google/common/collect/ImmutableClassToInstanceMap.java b/guava/src/com/google/common/collect/ImmutableClassToInstanceMap.java
index 337b4008e..aeabe5d3a 100644
--- a/guava/src/com/google/common/collect/ImmutableClassToInstanceMap.java
+++ b/guava/src/com/google/common/collect/ImmutableClassToInstanceMap.java
@@ -190,7 +190,6 @@ public final class ImmutableClassToInstanceMap<B> extends ForwardingMap<Class<?
@Override
@DoNotCall("Always throws UnsupportedOperationException")
@CheckForNull
- @SuppressWarnings("nullness") // TODO(cpovirk): Remove after annotating supertype.
public <T extends B> T putInstance(Class<T> type, T value) {
throw new UnsupportedOperationException();
}
diff --git a/guava/src/com/google/common/collect/MutableClassToInstanceMap.java b/guava/src/com/google/common/collect/MutableClassToInstanceMap.java
index c3ad9080d..83fbe9447 100644
--- a/guava/src/com/google/common/collect/MutableClassToInstanceMap.java
+++ b/guava/src/com/google/common/collect/MutableClassToInstanceMap.java
@@ -28,20 +28,26 @@ import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.Spliterator;
+import javax.annotation.CheckForNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
/**
* A mutable class-to-instance map backed by an arbitrary user-provided map. See also {@link
* ImmutableClassToInstanceMap}.
*
* <p>See the Guava User Guide article on <a href=
- * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#classtoinstancemap"> {@code
+ * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#classtoinstancemap">{@code
* ClassToInstanceMap}</a>.
*
+ * <p>This implementation <i>does</i> support null values, despite how it is annotated; see
+ * discussion at {@link ClassToInstanceMap}.
+ *
* @author Kevin Bourrillion
* @since 2.0
*/
@GwtIncompatible
@SuppressWarnings("serial") // using writeReplace instead of standard serialization
+@ElementTypesAreNonnullByDefault
public final class MutableClassToInstanceMap<B> extends ForwardingMap<Class<? extends B>, B>
implements ClassToInstanceMap<B>, Serializable {
@@ -119,11 +125,20 @@ public final class MutableClassToInstanceMap<B> extends ForwardingMap<Class<? ex
@Override
public Object[] toArray() {
- return standardToArray();
+ /*
+ * standardToArray returns `@Nullable Object[]` rather than `Object[]` but only because it
+ * can be used with collections that may contain null. This collection is a collection of
+ * non-null Entry objects (Entry objects that might contain null values but are not
+ * themselves null), so we can treat it as a plain `Object[]`.
+ */
+ @SuppressWarnings("nullness")
+ Object[] result = standardToArray();
+ return result;
}
@Override
- public <T> T[] toArray(T[] array) {
+ @SuppressWarnings("nullness") // b/192354773 in our checker affects toArray declarations
+ public <T extends @Nullable Object> T[] toArray(T[] array) {
return standardToArray(array);
}
};
@@ -131,6 +146,7 @@ public final class MutableClassToInstanceMap<B> extends ForwardingMap<Class<? ex
@Override
@CanIgnoreReturnValue
+ @CheckForNull
public B put(Class<? extends B> key, B value) {
return super.put(key, cast(key, value));
}
@@ -146,17 +162,20 @@ public final class MutableClassToInstanceMap<B> extends ForwardingMap<Class<? ex
@CanIgnoreReturnValue
@Override
+ @CheckForNull
public <T extends B> T putInstance(Class<T> type, T value) {
return cast(type, put(type, value));
}
@Override
+ @CheckForNull
public <T extends B> T getInstance(Class<T> type) {
return cast(type, get(type));
}
@CanIgnoreReturnValue
- private static <B, T extends B> T cast(Class<T> type, B value) {
+ @CheckForNull
+ private static <B, T extends B> T cast(Class<T> type, @CheckForNull B value) {
return Primitives.wrap(type).cast(value);
}