Fix more GWT/J2CL nullness annotations, and fix a GWT/J2CL nullness bug in cache.

RELNOTES=n/a
PiperOrigin-RevId: 995298922
This commit is contained in:
cpovirk authored and Google Java Core Libraries committed 2026-10-07 13:03:35 -07:00
1 parent 99dfa90a13
commit a24e79ef3e
6 files changed
+78 -15

No files matched your search

@@ -22,6 +22,7 @@ import static com.google.common.cache.TestingRemovalListeners.countingRemovalLis
import static com.google.common.cache.TestingRemovalListeners.nullRemovalListener;
import static com.google.common.cache.TestingRemovalListeners.queuingRemovalListener;
import static com.google.common.cache.TestingWeighers.constantWeigher;
import static com.google.common.collect.Maps.immutableEntry;
import static com.google.common.collect.Sets.newHashSetWithExpectedSize;
import static com.google.common.collect.Sets.union;
import static com.google.common.truth.Truth.assertThat;
@@ -44,6 +45,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
@@ -356,6 +358,34 @@ public class CacheBuilderTest extends TestCase {
assertThat(CacheBuilder.newBuilder().build().asMap().values() instanceof Set).isFalse();
}
@J2ktIncompatible // asMap
public void testAsMapEntrySet_containsAndRemoveNulls() {
CountingRemovalListener<Integer, Integer> listener = countingRemovalListener();
Cache<Integer, Integer> cache = CacheBuilder.newBuilder().removalListener(listener).build();
cache.put(10, 20);
Set<Entry<Integer, Integer>> entries = cache.asMap().entrySet();
assertThat(entries.contains(null)).isFalse();
assertThat(entries.contains(immutableEntry(10, null))).isFalse();
assertThat(entries.contains(immutableEntry(99, null))).isFalse();
assertThat(entries.contains(immutableEntry(null, 20))).isFalse();
assertThat(entries.contains(immutableEntry(null, null))).isFalse();
assertThat(entries.remove(null)).isFalse();
assertThat(entries.remove(immutableEntry(10, null))).isFalse();
assertThat(entries.remove(immutableEntry(99, null))).isFalse();
assertThat(entries.remove(immutableEntry(null, 20))).isFalse();
assertThat(entries.remove(immutableEntry(null, null))).isFalse();
assertThat(cache.asMap().remove(10, null)).isFalse();
assertThat(cache.asMap().remove(99, null)).isFalse();
assertThat(cache.asMap().remove(null, 20)).isFalse();
assertThat(cache.asMap().remove(null, null)).isFalse();
assertThat(listener.getCount()).isEqualTo(0);
assertThat(cache.asMap()).containsExactly(10, 20);
}
@J2ktIncompatible
@GwtIncompatible // CacheTesting
public void testNullCache() {
@@ -37,7 +37,6 @@ import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentMap;
@@ -206,13 +205,16 @@ final class LocalCache<K, V> implements ConcurrentMap<K, V> {
@CanIgnoreReturnValue
@Override
public boolean remove(@Nullable Object key, @Nullable Object value) {
if (key == null || value == null) {
return false;
}
synchronized (lock) {
if (Objects.equals(value, get(key))) {
if (value.equals(get(key))) {
// `key` was in the cache, so it's a K.
// (Or it's a weird case like a LinkedList in a Cache<ArrayList, ...>, but *shrug*.)
@SuppressWarnings({"unchecked", "nullness"})
@SuppressWarnings("unchecked")
K castKey = (K) key;
@SuppressWarnings({"unchecked", "nullness"}) // similar to the above
@SuppressWarnings("unchecked") // similar to the above
V castValue = (V) value;
alertListenerIfPresent(castKey, castValue, RemovalCause.EXPLICIT);
@@ -765,12 +767,12 @@ final class LocalCache<K, V> implements ConcurrentMap<K, V> {
}
@Override
public boolean contains(Object o) {
public boolean contains(@Nullable Object o) {
return map.containsKey(o);
}
@Override
public boolean remove(Object o) {
public boolean remove(@Nullable Object o) {
return map.remove(o) != null;
}
}
@@ -788,7 +790,7 @@ final class LocalCache<K, V> implements ConcurrentMap<K, V> {
}
@Override
public boolean contains(Object o) {
public boolean contains(@Nullable Object o) {
return map.containsValue(o);
}
@@ -820,7 +822,7 @@ final class LocalCache<K, V> implements ConcurrentMap<K, V> {
}
@Override
public boolean contains(Object o) {
public boolean contains(@Nullable Object o) {
if (!(o instanceof Entry)) {
return false;
}
@@ -831,11 +833,11 @@ final class LocalCache<K, V> implements ConcurrentMap<K, V> {
}
V v = LocalCache.this.get(key);
return (v != null) && e.getValue().equals(v);
return (v != null) && v.equals(e.getValue());
}
@Override
public boolean remove(Object o) {
public boolean remove(@Nullable Object o) {
if (!(o instanceof Entry)) {
return false;
}
@@ -414,7 +414,7 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable {
}
@Override
public final @Nullable V remove(Object o) {
public final @Nullable V remove(@Nullable Object o) {
throw new UnsupportedOperationException();
}
@@ -432,12 +432,12 @@ public final class ImmutableSortedMap<K, V> extends ForwardingImmutableMap<K, V>
}
@Override
public @Nullable K firstKey() {
public K firstKey() {
return sortedDelegate.firstKey();
}
@Override
public @Nullable K lastKey() {
public K lastKey() {
return sortedDelegate.lastKey();
}
@@ -18,6 +18,7 @@ package com.google.common.collect;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.Objects.requireNonNull;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.util.ArrayList;
@@ -230,7 +231,7 @@ public abstract class ImmutableSortedSet<E> extends ForwardingImmutableSet<E>
static <E> ImmutableSortedSet<E> unsafeDelegateSortedSet(
SortedSet<E> delegate, boolean isSubset) {
return delegate.isEmpty()
? emptySet(delegate.comparator())
? emptySet(requireNonNull(delegate.comparator()))
: new RegularImmutableSortedSet<E>(delegate, isSubset);
}
@@ -258,7 +259,7 @@ public abstract class ImmutableSortedSet<E> extends ForwardingImmutableSet<E>
@Override
public Comparator<? super E> comparator() {
return sortedDelegate.comparator();
return requireNonNull(sortedDelegate.comparator());
}
@Override // needed to unify SortedIterable and Collection iterator() methods
@@ -22,6 +22,7 @@ import static com.google.common.cache.TestingRemovalListeners.countingRemovalLis
import static com.google.common.cache.TestingRemovalListeners.nullRemovalListener;
import static com.google.common.cache.TestingRemovalListeners.queuingRemovalListener;
import static com.google.common.cache.TestingWeighers.constantWeigher;
import static com.google.common.collect.Maps.immutableEntry;
import static com.google.common.collect.Sets.newHashSetWithExpectedSize;
import static com.google.common.collect.Sets.union;
import static com.google.common.truth.Truth.assertThat;
@@ -44,6 +45,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
@@ -356,6 +358,34 @@ public class CacheBuilderTest extends TestCase {
assertThat(CacheBuilder.newBuilder().build().asMap().values() instanceof Set).isFalse();
}
@J2ktIncompatible // asMap
public void testAsMapEntrySet_containsAndRemoveNulls() {
CountingRemovalListener<Integer, Integer> listener = countingRemovalListener();
Cache<Integer, Integer> cache = CacheBuilder.newBuilder().removalListener(listener).build();
cache.put(10, 20);
Set<Entry<Integer, Integer>> entries = cache.asMap().entrySet();
assertThat(entries.contains(null)).isFalse();
assertThat(entries.contains(immutableEntry(10, null))).isFalse();
assertThat(entries.contains(immutableEntry(99, null))).isFalse();
assertThat(entries.contains(immutableEntry(null, 20))).isFalse();
assertThat(entries.contains(immutableEntry(null, null))).isFalse();
assertThat(entries.remove(null)).isFalse();
assertThat(entries.remove(immutableEntry(10, null))).isFalse();
assertThat(entries.remove(immutableEntry(99, null))).isFalse();
assertThat(entries.remove(immutableEntry(null, 20))).isFalse();
assertThat(entries.remove(immutableEntry(null, null))).isFalse();
assertThat(cache.asMap().remove(10, null)).isFalse();
assertThat(cache.asMap().remove(99, null)).isFalse();
assertThat(cache.asMap().remove(null, 20)).isFalse();
assertThat(cache.asMap().remove(null, null)).isFalse();
assertThat(listener.getCount()).isEqualTo(0);
assertThat(cache.asMap()).containsExactly(10, 20);
}
@J2ktIncompatible
@GwtIncompatible // CacheTesting
public void testNullCache() {