Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Streams;
import com.google.errorprone.refaster.Refaster;
import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.AlsoNegation;
Expand All @@ -17,6 +18,7 @@
import java.util.NavigableSet;
import java.util.Optional;
import java.util.Queue;
import java.util.SequencedCollection;
import java.util.Set;
import java.util.SortedSet;
import java.util.function.Consumer;
Expand Down Expand Up @@ -399,7 +401,7 @@ Optional<T> before(Collection<T> collection) {

@BeforeTemplate
Optional<T> before(List<T> collection) {
return collection.isEmpty() ? Optional.empty() : Optional.of(collection.get(0));
return collection.isEmpty() ? Optional.empty() : Optional.of(collection.getFirst());
}

@BeforeTemplate
Expand Down Expand Up @@ -484,6 +486,143 @@ void after(Collection<T> collection, Consumer<? super T> consumer) {
}
}

/** Prefer {@code collection.iterator().next()} over more contrived alternatives. */
static final class CollectionIteratorNext<S, T extends S> {
@BeforeTemplate
S before(Collection<T> collection) {
return collection.stream().findFirst().orElseThrow();
}

@AfterTemplate
S after(Collection<T> collection) {
return collection.iterator().next();
}
}

/** Prefer {@link SequencedCollection#getFirst()} over less idiomatic alternatives. */
static final class SequencedCollectionGetFirst<S, T extends S> {
@BeforeTemplate
S before(SequencedCollection<T> collection) {
return collection.iterator().next();
}

@BeforeTemplate
S before(List<T> collection) {
return collection.get(0);
}

@AfterTemplate
S after(SequencedCollection<T> collection) {
return collection.getFirst();
}
}

/** Prefer {@link SequencedCollection#getLast()} over less idiomatic alternatives. */
static final class SequencedCollectionGetLast<S, T extends S> {
@BeforeTemplate
S before(SequencedCollection<T> collection) {
return Refaster.anyOf(
collection.reversed().getFirst(), Streams.findLast(collection.stream()).orElseThrow());
}

@BeforeTemplate
S before(List<T> collection) {
return collection.get(collection.size() - 1);
}

@AfterTemplate
S after(SequencedCollection<T> collection) {
return collection.getLast();
}
}

/** Prefer {@link List#addFirst(Object)} over less idiomatic alternatives. */
static final class ListAddFirst<S, T extends S> {
@BeforeTemplate
void before(List<S> list, T element) {
list.add(0, element);
}

@AfterTemplate
void after(List<S> list, T element) {
list.addFirst(element);
}
}

/** Prefer {@link List#add(Object)} over less idiomatic alternatives. */
static final class ListAdd<S, T extends S> {
@BeforeTemplate
void before(List<S> list, T element) {
list.addLast(element);
}

@BeforeTemplate
void before2(List<S> list, T element) {
list.add(list.size(), element);
}

@AfterTemplate
void after(List<S> list, T element) {
list.add(element);
}
}

/** Prefer {@link List#removeFirst()}} over less idiomatic alternatives. */
// XXX: This rule changes the exception thrown for empty lists from `IndexOutOfBoundsException` to
// `NoSuchElementException`.
static final class ListRemoveFirst<S, T extends S> {
@BeforeTemplate
S before(List<T> list) {
return list.remove(0);
}

@AfterTemplate
S after(List<T> list) {
return list.removeFirst();
}
}

/** Prefer {@link List#removeLast()}} over less idiomatic alternatives. */
// XXX: This rule changes the exception thrown for empty lists from `IndexOutOfBoundsException` to
// `NoSuchElementException`.
static final class ListRemoveLast<S, T extends S> {
@BeforeTemplate
S before(List<T> list) {
return list.remove(list.size() - 1);
}

@AfterTemplate
S after(List<T> list) {
return list.removeLast();
}
}

/** Prefer {@link SortedSet#first()} over more verbose alternatives. */
static final class SortedSetFirst<S, T extends S> {
@BeforeTemplate
S before(SortedSet<T> set) {
return set.getFirst();
}

@AfterTemplate
S after(SortedSet<T> set) {
return set.first();
}
}

/** Prefer {@link SortedSet#last()} over more verbose alternatives. */
static final class SortedSetLast<S, T extends S> {
@BeforeTemplate
S before(SortedSet<T> set) {
return set.getLast();
}

@AfterTemplate
S after(SortedSet<T> set) {
return set.last();
}
}

// XXX: collection.stream().noneMatch(e -> e.equals(other))
// ^ This is !collection.contains(other). Do we already rewrite variations on this?
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Streams;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
Expand All @@ -19,7 +20,7 @@
final class CollectionRulesTest implements RefasterRuleCollectionTestCase {
@Override
public ImmutableSet<Object> elidedTypesAndStaticImports() {
return ImmutableSet.of(Iterables.class, Lists.class);
return ImmutableSet.of(Iterables.class, Lists.class, Streams.class);
}

ImmutableSet<Boolean> testCollectionIsEmpty() {
Expand Down Expand Up @@ -130,14 +131,18 @@ ImmutableSet<Optional<Integer>> testOptionalFirstCollectionElement() {
ImmutableSet.of(1).isEmpty()
? Optional.empty()
: Optional.of(ImmutableSet.of(1).iterator().next()),
ImmutableList.of(2).isEmpty() ? Optional.empty() : Optional.of(ImmutableList.of(2).get(0)),
ImmutableList.of(2).isEmpty()
? Optional.empty()
: Optional.of(ImmutableList.of(2).getFirst()),
ImmutableSortedSet.of(3).isEmpty()
? Optional.empty()
: Optional.of(ImmutableSortedSet.of(3).first()),
!ImmutableSet.of(1).isEmpty()
? Optional.of(ImmutableSet.of(1).iterator().next())
: Optional.empty(),
!ImmutableList.of(2).isEmpty() ? Optional.of(ImmutableList.of(2).get(0)) : Optional.empty(),
!ImmutableList.of(2).isEmpty()
? Optional.of(ImmutableList.of(2).getFirst())
: Optional.empty(),
!ImmutableSortedSet.of(3).isEmpty()
? Optional.of(ImmutableSortedSet.of(3).first())
: Optional.empty());
Expand Down Expand Up @@ -207,4 +212,45 @@ ImmutableSet<Optional<String>> testRemoveOptionalFirstQueueElement() {
void testCollectionForEach() {
ImmutableSet.of(1).stream().forEach(String::valueOf);
}

String testCollectionIteratorNext() {
return ImmutableSet.of("foo").stream().findFirst().orElseThrow();
}

ImmutableSet<String> testSequencedCollectionGetFirst() {
return ImmutableSet.of(
ImmutableList.of("foo").iterator().next(), ImmutableList.of("bar").get(0));
}

ImmutableSet<String> testSequencedCollectionGetLast() {
return ImmutableSet.of(
ImmutableList.of("foo").reversed().getFirst(),
Streams.findLast(ImmutableList.of("bar").stream()).orElseThrow(),
ImmutableList.of("baz").get(ImmutableList.of("baz").size() - 1));
}

void testListAddFirst() {
new ArrayList<String>().add(0, "foo");
}

void testListAdd() {
new ArrayList<String>(0).addLast("bar");
new ArrayList<String>(1).add(new ArrayList<String>(1).size(), "qux");
}

String testListRemoveFirst() {
return new ArrayList<String>().remove(0);
}

String testListRemoveLast() {
return new ArrayList<String>().remove(new ArrayList<String>().size() - 1);
}

String testSortedSetFirst() {
return ImmutableSortedSet.of("foo").getFirst();
}

String testSortedSetLast() {
return ImmutableSortedSet.of("foo").getLast();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Streams;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
Expand All @@ -19,7 +20,7 @@
final class CollectionRulesTest implements RefasterRuleCollectionTestCase {
@Override
public ImmutableSet<Object> elidedTypesAndStaticImports() {
return ImmutableSet.of(Iterables.class, Lists.class);
return ImmutableSet.of(Iterables.class, Lists.class, Streams.class);
}

ImmutableSet<Boolean> testCollectionIsEmpty() {
Expand Down Expand Up @@ -156,4 +157,44 @@ ImmutableSet<Optional<String>> testRemoveOptionalFirstQueueElement() {
void testCollectionForEach() {
ImmutableSet.of(1).forEach(String::valueOf);
}

String testCollectionIteratorNext() {
return ImmutableSet.of("foo").iterator().next();
}

ImmutableSet<String> testSequencedCollectionGetFirst() {
return ImmutableSet.of(ImmutableList.of("foo").getFirst(), ImmutableList.of("bar").getFirst());
}

ImmutableSet<String> testSequencedCollectionGetLast() {
return ImmutableSet.of(
ImmutableList.of("foo").getLast(),
ImmutableList.of("bar").getLast(),
ImmutableList.of("baz").getLast());
}

void testListAddFirst() {
new ArrayList<String>().addFirst("foo");
}

void testListAdd() {
new ArrayList<String>(0).add("bar");
new ArrayList<String>(1).add("qux");
}

String testListRemoveFirst() {
return new ArrayList<String>().removeFirst();
}

String testListRemoveLast() {
return new ArrayList<String>().removeLast();
}

String testSortedSetFirst() {
return ImmutableSortedSet.of("foo").first();
}

String testSortedSetLast() {
return ImmutableSortedSet.of("foo").last();
}
}