Skip to content

Commit

Permalink
Standardize on sneakyThrow for "impossible" checked exceptions.
Browse files Browse the repository at this point in the history
This makes our code behave the same for sneaky checked exceptions that occur _when our implementations use reflection_ as for those that occur when our implementations use direct calls.

This shouldn't matter for any of the code that I'm migrating to `sneakyThrow` in this CL, but there are other cases in which it could matter, and we're probably best off standardizing on one approach, just as we've done for _catching_ sneaky checked exceptions.

Plus, pull each package's `sneakyThrow` out into its own top-level class for reuse.

Also, sneak in a few other tiny cleanups:

- We don't normally use `@GwtCompatible` in Truth, so remove it from `J2ktIncompatible`.
- We can now use `Primitives.wrap` to produce a better error message for `isIntanceOf(primitiveType)`.

RELNOTES=n/a
PiperOrigin-RevId: 710060382
  • Loading branch information
cpovirk authored and Google Java Core Libraries committed Dec 27, 2024
1 parent 4067301 commit 0782e12
Show file tree
Hide file tree
Showing 20 changed files with 368 additions and 116 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.google.common.base;

import static com.google.common.base.ReflectionFreeAssertThrows.assertThrows;
import static com.google.common.base.SneakyThrows.sneakyThrow;

import com.google.common.annotations.GwtCompatible;
import com.google.common.annotations.GwtIncompatible;
Expand Down Expand Up @@ -91,8 +92,7 @@ public Integer computeNext() {
throw new AssertionError("Should not have been called again");
} else {
haveBeenCalled = true;
sneakyThrow(new SomeCheckedException());
throw new AssertionError(); // unreachable
throw sneakyThrow(new SomeCheckedException());
}
}
};
Expand Down Expand Up @@ -181,15 +181,4 @@ protected Integer computeNext() {
// Technically we should test other reentrant scenarios (4 combinations of
// hasNext/next), but we'll cop out for now, knowing that
// next() both start by invoking hasNext() anyway.

/** Throws an undeclared checked exception. */
private static void sneakyThrow(Throwable t) {
class SneakyThrower<T extends Throwable> {
@SuppressWarnings("unchecked") // intentionally unsafe for test
void throwIt(Throwable t) throws T {
throw (T) t;
}
}
new SneakyThrower<Error>().throwIt(t);
}
}
43 changes: 43 additions & 0 deletions android/guava-tests/test/com/google/common/base/SneakyThrows.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright (C) 2015 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you
* may not use this file except in compliance with the License. You may
* obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package com.google.common.base;

import com.google.common.annotations.GwtCompatible;
import com.google.errorprone.annotations.CanIgnoreReturnValue;

/** Static utility method for throwing an undeclared checked exception. */
@GwtCompatible
final class SneakyThrows<T extends Throwable> {
/**
* Throws an undeclared checked exception.
*
* @return never; this method declares a return type of {@link Error} only so that callers can
* write {@code throw sneakyThrow(t);} to convince the compiler that the statement will always
* throw.
*/
@CanIgnoreReturnValue
static Error sneakyThrow(Throwable t) {
throw new SneakyThrows<Error>().throwIt(t);
}

@SuppressWarnings("unchecked") // not really safe, but that's the point
private Error throwIt(Throwable t) throws T {
throw (T) t;
}

private SneakyThrows() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.google.common.collect;

import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows;
import static com.google.common.collect.SneakyThrows.sneakyThrow;

import com.google.common.annotations.GwtCompatible;
import com.google.common.annotations.GwtIncompatible;
Expand Down Expand Up @@ -175,8 +176,7 @@ public Integer computeNext() {
throw new AssertionError("Should not have been called again");
} else {
haveBeenCalled = true;
sneakyThrow(new SomeCheckedException());
throw new AssertionError(); // unreachable
throw sneakyThrow(new SomeCheckedException());
}
}
};
Expand Down Expand Up @@ -250,15 +250,4 @@ protected Integer computeNext() {
// Technically we should test other reentrant scenarios (9 combinations of
// hasNext/next/peek), but we'll cop out for now, knowing that peek() and
// next() both start by invoking hasNext() anyway.

/** Throws an undeclared checked exception. */
private static void sneakyThrow(Throwable t) {
class SneakyThrower<T extends Throwable> {
@SuppressWarnings("unchecked") // not really safe, but that's the point
void throwIt(Throwable t) throws T {
throw (T) t;
}
}
new SneakyThrower<Error>().throwIt(t);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import static com.google.common.util.concurrent.Futures.immediateFailedFuture;
import static com.google.common.util.concurrent.Futures.immediateFuture;
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
import static com.google.common.util.concurrent.SneakyThrows.sneakyThrow;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
Expand Down Expand Up @@ -1070,17 +1071,6 @@ public void testCatchesUndeclaredThrowableFromListener() {

private static final class SomeCheckedException extends Exception {}

/** Throws an undeclared checked exception. */
private static void sneakyThrow(Throwable t) {
class SneakyThrower<T extends Throwable> {
@SuppressWarnings("unchecked") // intentionally unsafe for test
void throwIt(Throwable t) throws T {
throw (T) t;
}
}
new SneakyThrower<Error>().throwIt(t);
}

public void testTrustedGetFailure_completed() {
SettableFuture<String> future = SettableFuture.create();
future.set("261");
Expand Down
43 changes: 43 additions & 0 deletions android/guava/src/com/google/common/collect/SneakyThrows.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright (C) 2015 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you
* may not use this file except in compliance with the License. You may
* obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package com.google.common.collect;

import com.google.common.annotations.GwtCompatible;
import com.google.errorprone.annotations.CanIgnoreReturnValue;

/** Static utility method for throwing an undeclared checked exception. */
@GwtCompatible
final class SneakyThrows<T extends Throwable> {
/**
* Throws an undeclared checked exception.
*
* @return never; this method declares a return type of {@link Error} only so that callers can
* write {@code throw sneakyThrow(t);} to convince the compiler that the statement will always
* throw.
*/
@CanIgnoreReturnValue
static Error sneakyThrow(Throwable t) {
throw new SneakyThrows<Error>().throwIt(t);
}

@SuppressWarnings("unchecked") // not really safe, but that's the point
private Error throwIt(Throwable t) throws T {
throw (T) t;
}

private SneakyThrows() {}
}
12 changes: 1 addition & 11 deletions android/guava/src/com/google/common/collect/Streams.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.NullnessCasts.uncheckedCastNullableTToT;
import static com.google.common.collect.SneakyThrows.sneakyThrow;
import static java.lang.Math.min;
import static java.util.Objects.requireNonNull;

Expand Down Expand Up @@ -179,17 +180,6 @@ private static void closeAll(BaseStream<?, ?>[] toClose) {
}
}

/** Throws an undeclared checked exception. */
private static void sneakyThrow(Throwable t) {
class SneakyThrower<T extends Throwable> {
@SuppressWarnings("unchecked") // not really safe, but that's the point
void throwIt(Throwable t) throws T {
throw (T) t;
}
}
new SneakyThrower<Error>().throwIt(t);
}

/**
* Returns a {@link Stream} containing the elements of the first stream, followed by the elements
* of the second stream, and so on.
Expand Down
43 changes: 43 additions & 0 deletions android/guava/src/com/google/common/hash/SneakyThrows.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright (C) 2015 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you
* may not use this file except in compliance with the License. You may
* obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package com.google.common.hash;

import com.google.common.annotations.GwtCompatible;
import com.google.errorprone.annotations.CanIgnoreReturnValue;

/** Static utility method for throwing an undeclared checked exception. */
@GwtCompatible
final class SneakyThrows<T extends Throwable> {
/**
* Throws an undeclared checked exception.
*
* @return never; this method declares a return type of {@link Error} only so that callers can
* write {@code throw sneakyThrow(t);} to convince the compiler that the statement will always
* throw.
*/
@CanIgnoreReturnValue
static Error sneakyThrow(Throwable t) {
throw new SneakyThrows<Error>().throwIt(t);
}

@SuppressWarnings("unchecked") // not really safe, but that's the point
private Error throwIt(Throwable t) throws T {
throw (T) t;
}

private SneakyThrows() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Throwables.throwIfUnchecked;
import static com.google.common.util.concurrent.Internal.toNanosSaturated;
import static com.google.common.util.concurrent.SneakyThrows.sneakyThrow;
import static java.util.Objects.requireNonNull;

import com.google.common.annotations.GwtCompatible;
Expand All @@ -30,7 +30,6 @@
import com.google.common.util.concurrent.ForwardingListenableFuture.SimpleForwardingListenableFuture;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.UndeclaredThrowableException;
import java.time.Duration;
import java.util.Collection;
import java.util.Iterator;
Expand Down Expand Up @@ -816,9 +815,8 @@ public static ThreadFactory platformThreadFactory() {
} catch (IllegalAccessException | ClassNotFoundException | NoSuchMethodException e) {
throw new RuntimeException("Couldn't invoke ThreadManager.currentRequestThreadFactory", e);
} catch (InvocationTargetException e) {
throwIfUnchecked(e.getCause());
// This should be impossible: `currentRequestThreadFactory` has no `throws` clause.
throw new UndeclaredThrowableException(e.getCause());
// `currentRequestThreadFactory` has no `throws` clause.
throw sneakyThrow(e.getCause());
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright (C) 2015 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you
* may not use this file except in compliance with the License. You may
* obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package com.google.common.util.concurrent;

import com.google.common.annotations.GwtCompatible;
import com.google.errorprone.annotations.CanIgnoreReturnValue;

/** Static utility method for throwing an undeclared checked exception. */
@GwtCompatible
final class SneakyThrows<T extends Throwable> {
/**
* Throws an undeclared checked exception.
*
* @return never; this method declares a return type of {@link Error} only so that callers can
* write {@code throw sneakyThrow(t);} to convince the compiler that the statement will always
* throw.
*/
@CanIgnoreReturnValue
static Error sneakyThrow(Throwable t) {
throw new SneakyThrows<Error>().throwIt(t);
}

@SuppressWarnings("unchecked") // not really safe, but that's the point
private Error throwIt(Throwable t) throws T {
throw (T) t;
}

private SneakyThrows() {}
}
15 changes: 2 additions & 13 deletions guava-tests/test/com/google/common/base/AbstractIteratorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.google.common.base;

import static com.google.common.base.ReflectionFreeAssertThrows.assertThrows;
import static com.google.common.base.SneakyThrows.sneakyThrow;

import com.google.common.annotations.GwtCompatible;
import com.google.common.annotations.GwtIncompatible;
Expand Down Expand Up @@ -91,8 +92,7 @@ public Integer computeNext() {
throw new AssertionError("Should not have been called again");
} else {
haveBeenCalled = true;
sneakyThrow(new SomeCheckedException());
throw new AssertionError(); // unreachable
throw sneakyThrow(new SomeCheckedException());
}
}
};
Expand Down Expand Up @@ -181,15 +181,4 @@ protected Integer computeNext() {
// Technically we should test other reentrant scenarios (4 combinations of
// hasNext/next), but we'll cop out for now, knowing that
// next() both start by invoking hasNext() anyway.

/** Throws an undeclared checked exception. */
private static void sneakyThrow(Throwable t) {
class SneakyThrower<T extends Throwable> {
@SuppressWarnings("unchecked") // intentionally unsafe for test
void throwIt(Throwable t) throws T {
throw (T) t;
}
}
new SneakyThrower<Error>().throwIt(t);
}
}
Loading

0 comments on commit 0782e12

Please sign in to comment.