From 747924eca675b79c3047d204814a4bc91e58fe70 Mon Sep 17 00:00:00 2001 From: cpovirk Date: Thu, 14 Dec 2023 12:32:25 -0800 Subject: [PATCH] Make more classes catch `Exception` instead of `RuntimeException` even when only `RuntimeException` is theoretically possible. This is the followup to cl/587701612. It covers cases in which I thought non-`RuntimeException` exceptions were less likely to happen. I'm not sure that my judgment of that was actually great: Notably, I definitely worry about bad implementations of `Future`, or at least I used to worry about such things years ago. I hope that this covers the remaining cases. I don't remember offhand how I identified all the sites, though, nor am I certain that I recovered the right snapshot of my earlier work. But this should at least be close to everything. And if my testing from cl/587701612 was correct, we will see no impact on Google's tests from this CL. If there's anything left to do here, it's probably to add static analysis to _require_ us to always catch the more general `Exception`. But my guess is that that isn't likely to end up being worth the effort. (I skipped a `catch (RuntimeException e)` block in `MapInterfaceTest` on the grounds that that's the kind of exception that it should be throwing. Of course, we'd like to use something more like `assertThrows`: b/299927833. And probably it should be catching `NullPointerException` specifically....) RELNOTES=n/a PiperOrigin-RevId: 591020660 --- .../testing/AbstractIteratorTester.java | 20 +++++++++-------- .../common/testing/ClassSanityTester.java | 6 +++-- .../com/google/common/hash/BloomFilter.java | 5 ++++- .../com/google/common/reflect/Invokable.java | 3 ++- .../concurrent/AbstractCatchingFuture.java | 4 ++-- .../util/concurrent/AbstractFuture.java | 11 +++++----- .../concurrent/AbstractTransformFuture.java | 3 ++- .../util/concurrent/AggregateFuture.java | 2 +- .../util/concurrent/AggregateFutureState.java | 2 +- .../util/concurrent/FakeTimeLimiter.java | 3 ++- .../util/concurrent/FuturesGetChecked.java | 2 +- .../util/concurrent/JdkFutureAdapters.java | 8 +++---- .../common/util/concurrent/MoreExecutors.java | 9 ++++++-- .../concurrent/UncaughtExceptionHandlers.java | 2 +- .../testing/AbstractIteratorTester.java | 20 +++++++++-------- .../common/testing/ClassSanityTester.java | 6 +++-- .../com/google/common/collect/Streams.java | 22 +++++++++++++++---- .../com/google/common/hash/BloomFilter.java | 5 ++++- .../com/google/common/reflect/Invokable.java | 3 ++- .../concurrent/AbstractCatchingFuture.java | 4 ++-- .../util/concurrent/AbstractFuture.java | 11 +++++----- .../concurrent/AbstractTransformFuture.java | 3 ++- .../util/concurrent/AggregateFuture.java | 2 +- .../util/concurrent/AggregateFutureState.java | 2 +- .../util/concurrent/FakeTimeLimiter.java | 3 ++- .../util/concurrent/FuturesGetChecked.java | 2 +- .../util/concurrent/JdkFutureAdapters.java | 8 +++---- .../common/util/concurrent/MoreExecutors.java | 9 ++++++-- .../concurrent/UncaughtExceptionHandlers.java | 2 +- 29 files changed, 112 insertions(+), 70 deletions(-) diff --git a/android/guava-testlib/src/com/google/common/collect/testing/AbstractIteratorTester.java b/android/guava-testlib/src/com/google/common/collect/testing/AbstractIteratorTester.java index b4e5b35e80b1..f26e6826beb9 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/AbstractIteratorTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/AbstractIteratorTester.java @@ -58,7 +58,7 @@ private abstract static class PermittedMetaException extends RuntimeException { static final PermittedMetaException UOE_OR_ISE = new PermittedMetaException("UnsupportedOperationException or IllegalStateException") { @Override - boolean isPermitted(RuntimeException exception) { + boolean isPermitted(Exception exception) { return exception instanceof UnsupportedOperationException || exception instanceof IllegalStateException; } @@ -66,21 +66,21 @@ boolean isPermitted(RuntimeException exception) { static final PermittedMetaException UOE = new PermittedMetaException("UnsupportedOperationException") { @Override - boolean isPermitted(RuntimeException exception) { + boolean isPermitted(Exception exception) { return exception instanceof UnsupportedOperationException; } }; static final PermittedMetaException ISE = new PermittedMetaException("IllegalStateException") { @Override - boolean isPermitted(RuntimeException exception) { + boolean isPermitted(Exception exception) { return exception instanceof IllegalStateException; } }; static final PermittedMetaException NSEE = new PermittedMetaException("NoSuchElementException") { @Override - boolean isPermitted(RuntimeException exception) { + boolean isPermitted(Exception exception) { return exception instanceof NoSuchElementException; } }; @@ -89,9 +89,9 @@ private PermittedMetaException(String message) { super(message); } - abstract boolean isPermitted(RuntimeException exception); + abstract boolean isPermitted(Exception exception); - void assertPermitted(RuntimeException exception) { + void assertPermitted(Exception exception) { if (!isPermitted(exception)) { String message = "Exception " @@ -313,10 +313,11 @@ public enum KnownOrder { protected void verify(List elements) {} /** Executes the test. */ + @SuppressWarnings("CatchingUnchecked") // sneaky checked exception public final void test() { try { recurse(0); - } catch (RuntimeException e) { + } catch (Exception e) { // sneaky checked exception throw new RuntimeException(Arrays.toString(stimuli), e); } } @@ -372,16 +373,17 @@ private interface IteratorOperation { * * @see Stimulus#executeAndCompare(ListIterator, Iterator) */ + @SuppressWarnings("CatchingUnchecked") // sneaky checked exception private > void internalExecuteAndCompare( T reference, T target, IteratorOperation method) { Object referenceReturnValue = null; PermittedMetaException referenceException = null; Object targetReturnValue = null; - RuntimeException targetException = null; + Exception targetException = null; try { targetReturnValue = method.execute(target); - } catch (RuntimeException e) { + } catch (Exception e) { // sneaky checked exception targetException = e; } diff --git a/android/guava-testlib/src/com/google/common/testing/ClassSanityTester.java b/android/guava-testlib/src/com/google/common/testing/ClassSanityTester.java index 3a41a7fe133f..2c80552aed20 100644 --- a/android/guava-testlib/src/com/google/common/testing/ClassSanityTester.java +++ b/android/guava-testlib/src/com/google/common/testing/ClassSanityTester.java @@ -496,13 +496,14 @@ public FactoryMethodReturnValueTester testEquals() throws Exception { * @return this tester */ @CanIgnoreReturnValue + @SuppressWarnings("CatchingUnchecked") // sneaky checked exception public FactoryMethodReturnValueTester testSerializable() throws Exception { for (Invokable factory : getFactoriesToTest()) { Object instance = instantiate(factory); if (instance != null) { try { SerializableTester.reserialize(instance); - } catch (RuntimeException e) { + } catch (Exception e) { // sneaky checked exception AssertionError error = new AssertionFailedError("Serialization failed on return value of " + factory); error.initCause(e.getCause()); @@ -522,6 +523,7 @@ public FactoryMethodReturnValueTester testSerializable() throws Exception { * @return this tester */ @CanIgnoreReturnValue + @SuppressWarnings("CatchingUnchecked") // sneaky checked exception public FactoryMethodReturnValueTester testEqualsAndSerializable() throws Exception { for (Invokable factory : getFactoriesToTest()) { try { @@ -533,7 +535,7 @@ public FactoryMethodReturnValueTester testEqualsAndSerializable() throws Excepti if (instance != null) { try { SerializableTester.reserializeAndAssert(instance); - } catch (RuntimeException e) { + } catch (Exception e) { // sneaky checked exception AssertionError error = new AssertionFailedError("Serialization failed on return value of " + factory); error.initCause(e.getCause()); diff --git a/android/guava/src/com/google/common/hash/BloomFilter.java b/android/guava/src/com/google/common/hash/BloomFilter.java index da2134cb466d..557d05593ad0 100644 --- a/android/guava/src/com/google/common/hash/BloomFilter.java +++ b/android/guava/src/com/google/common/hash/BloomFilter.java @@ -530,6 +530,7 @@ public void writeTo(OutputStream out) throws IOException { * @throws IOException if the InputStream throws an {@code IOException}, or if its data does not * appear to be a BloomFilter serialized using the {@linkplain #writeTo(OutputStream)} method. */ + @SuppressWarnings("CatchingUnchecked") // sneaky checked exception public static BloomFilter readFrom( InputStream in, Funnel funnel) throws IOException { checkNotNull(in, "InputStream"); @@ -554,7 +555,9 @@ public void writeTo(OutputStream out) throws IOException { } return new BloomFilter(dataArray, numHashFunctions, funnel, strategy); - } catch (RuntimeException e) { + } catch (IOException e) { + throw e; + } catch (Exception e) { // sneaky checked exception String message = "Unable to deserialize BloomFilter from InputStream." + " strategyOrdinal: " diff --git a/android/guava/src/com/google/common/reflect/Invokable.java b/android/guava/src/com/google/common/reflect/Invokable.java index 2e3d1a8a7433..ca405566f2d3 100644 --- a/android/guava/src/com/google/common/reflect/Invokable.java +++ b/android/guava/src/com/google/common/reflect/Invokable.java @@ -116,13 +116,14 @@ public final void setAccessible(boolean flag) { } /** See {@link java.lang.reflect.AccessibleObject#trySetAccessible()}. */ + @SuppressWarnings("CatchingUnchecked") // sneaky checked exception public final boolean trySetAccessible() { // We can't call accessibleObject.trySetAccessible since that was added in Java 9 and this code // should work on Java 8. So we emulate it this way. try { accessibleObject.setAccessible(true); return true; - } catch (RuntimeException e) { + } catch (Exception e) { // sneaky checked exception return false; } } diff --git a/android/guava/src/com/google/common/util/concurrent/AbstractCatchingFuture.java b/android/guava/src/com/google/common/util/concurrent/AbstractCatchingFuture.java index e5df53835be7..8fa6500280d6 100644 --- a/android/guava/src/com/google/common/util/concurrent/AbstractCatchingFuture.java +++ b/android/guava/src/com/google/common/util/concurrent/AbstractCatchingFuture.java @@ -108,8 +108,8 @@ public final void run() { + e.getClass() + " without a cause"); } - } catch (RuntimeException | Error e) { // this includes cancellation exception - throwable = e; + } catch (Throwable t) { // this includes CancellationException and sneaky checked exception + throwable = t; } if (throwable == null) { diff --git a/android/guava/src/com/google/common/util/concurrent/AbstractFuture.java b/android/guava/src/com/google/common/util/concurrent/AbstractFuture.java index 2f7c6fe12867..a0ddc4d2e254 100644 --- a/android/guava/src/com/google/common/util/concurrent/AbstractFuture.java +++ b/android/guava/src/com/google/common/util/concurrent/AbstractFuture.java @@ -155,7 +155,7 @@ public final boolean cancel(boolean mayInterruptIfRunning) { try { helper = new UnsafeAtomicHelper(); - } catch (RuntimeException | Error unsafeFailure) { + } catch (Exception | Error unsafeFailure) { // sneaky checked exception thrownUnsafeFailure = unsafeFailure; // catch absolutely everything and fall through to our 'SafeAtomicHelper' // The access control checks that ARFU does means the caller class has to be AbstractFuture @@ -168,7 +168,8 @@ public final boolean cancel(boolean mayInterruptIfRunning) { newUpdater(AbstractFuture.class, Waiter.class, "waiters"), newUpdater(AbstractFuture.class, Listener.class, "listeners"), newUpdater(AbstractFuture.class, Object.class, "value")); - } catch (RuntimeException | Error atomicReferenceFieldUpdaterFailure) { + } catch (Exception // sneaky checked exception + | Error atomicReferenceFieldUpdaterFailure) { // Some Android 5.0.x Samsung devices have bugs in JDK reflection APIs that cause // getDeclaredField to throw a NoSuchFieldException when the field is definitely there. // For these users fallback to a suboptimal implementation, based on synchronized. This will @@ -874,7 +875,7 @@ protected boolean setFuture(ListenableFuture future) { Failure failure; try { failure = new Failure(t); - } catch (RuntimeException | Error oomMostLikely) { + } catch (Exception | Error oomMostLikely) { // sneaky checked exception failure = Failure.FALLBACK_INSTANCE; } // Note: The only way this CAS could fail is if cancel() has raced with us. That is ok. @@ -969,7 +970,7 @@ private static Object getFutureValue(ListenableFuture future) { cancellation)); } return new Cancellation(false, cancellation); - } catch (RuntimeException | Error t) { + } catch (Exception | Error t) { // sneaky checked exception return new Failure(t); } } @@ -1386,8 +1387,6 @@ public sun.misc.Unsafe run() throws Exception { UNSAFE = unsafe; } catch (NoSuchFieldException e) { throw new RuntimeException(e); - } catch (RuntimeException e) { - throw e; } } diff --git a/android/guava/src/com/google/common/util/concurrent/AbstractTransformFuture.java b/android/guava/src/com/google/common/util/concurrent/AbstractTransformFuture.java index 5581b5fae5bc..3c5f30b63fb9 100644 --- a/android/guava/src/com/google/common/util/concurrent/AbstractTransformFuture.java +++ b/android/guava/src/com/google/common/util/concurrent/AbstractTransformFuture.java @@ -66,6 +66,7 @@ abstract class AbstractTransformFuture< } @Override + @SuppressWarnings("CatchingUnchecked") // sneaky checked exception public final void run() { ListenableFuture localInputFuture = inputFuture; F localFunction = function; @@ -104,7 +105,7 @@ public final void run() { // Set the cause of the exception as this future's exception. setException(e.getCause()); return; - } catch (RuntimeException e) { + } catch (Exception e) { // sneaky checked exception // Bug in inputFuture.get(). Propagate to the output Future so that its consumers don't hang. setException(e); return; diff --git a/android/guava/src/com/google/common/util/concurrent/AggregateFuture.java b/android/guava/src/com/google/common/util/concurrent/AggregateFuture.java index 6a8ae32f7c00..35347741667d 100644 --- a/android/guava/src/com/google/common/util/concurrent/AggregateFuture.java +++ b/android/guava/src/com/google/common/util/concurrent/AggregateFuture.java @@ -267,7 +267,7 @@ private void collectValueFromNonCancelledFuture(int index, Future new SafeAtomicHelper( newUpdater(AggregateFutureState.class, Set.class, "seenExceptions"), newUpdater(AggregateFutureState.class, "remaining")); - } catch (RuntimeException | Error reflectionFailure) { + } catch (Throwable reflectionFailure) { // sneaky checked exception // Some Android 5.0.x Samsung devices have bugs in JDK reflection APIs that cause // getDeclaredField to throw a NoSuchFieldException when the field is definitely there. // For these users fallback to a suboptimal implementation, based on synchronized. This will diff --git a/android/guava/src/com/google/common/util/concurrent/FakeTimeLimiter.java b/android/guava/src/com/google/common/util/concurrent/FakeTimeLimiter.java index 1c16cfb146d9..0f8a17894f59 100644 --- a/android/guava/src/com/google/common/util/concurrent/FakeTimeLimiter.java +++ b/android/guava/src/com/google/common/util/concurrent/FakeTimeLimiter.java @@ -77,12 +77,13 @@ public T newProxy( } @Override + @SuppressWarnings("CatchingUnchecked") // sneaky checked exception public void runWithTimeout(Runnable runnable, long timeoutDuration, TimeUnit timeoutUnit) { checkNotNull(runnable); checkNotNull(timeoutUnit); try { runnable.run(); - } catch (RuntimeException e) { + } catch (Exception e) { // sneaky checked exception throw new UncheckedExecutionException(e); } catch (Error e) { throw new ExecutionError(e); diff --git a/android/guava/src/com/google/common/util/concurrent/FuturesGetChecked.java b/android/guava/src/com/google/common/util/concurrent/FuturesGetChecked.java index cc9c3cb48512..0a4e7bd4352e 100644 --- a/android/guava/src/com/google/common/util/concurrent/FuturesGetChecked.java +++ b/android/guava/src/com/google/common/util/concurrent/FuturesGetChecked.java @@ -183,7 +183,7 @@ private static boolean hasConstructorUsableByGetChecked( try { Exception unused = newWithCause(exceptionClass, new Exception()); return true; - } catch (RuntimeException | Error e) { + } catch (Throwable t) { // sneaky checked exception return false; } } diff --git a/android/guava/src/com/google/common/util/concurrent/JdkFutureAdapters.java b/android/guava/src/com/google/common/util/concurrent/JdkFutureAdapters.java index 33403c978a09..eb3a24707270 100644 --- a/android/guava/src/com/google/common/util/concurrent/JdkFutureAdapters.java +++ b/android/guava/src/com/google/common/util/concurrent/JdkFutureAdapters.java @@ -19,7 +19,6 @@ import com.google.common.annotations.GwtIncompatible; import com.google.common.annotations.J2ktIncompatible; -import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.Future; @@ -162,10 +161,11 @@ public void addListener(Runnable listener, Executor exec) { * to return a proper ListenableFuture instead of using listenInPoolThread. */ getUninterruptibly(delegate); - } catch (ExecutionException | RuntimeException | Error e) { - // (including CancellationException) + } catch (Throwable t) { + // (including CancellationException and sneaky checked exception) // The task is presumably done, run the listeners. - // TODO(cpovirk): Do *something* in case of Error (and maybe RuntimeException)? + // TODO(cpovirk): Do *something* in case of Error (and maybe + // non-CancellationException, non-ExecutionException exceptions)? } executionList.execute(); }); diff --git a/android/guava/src/com/google/common/util/concurrent/MoreExecutors.java b/android/guava/src/com/google/common/util/concurrent/MoreExecutors.java index 4c9bd2bd23e9..cbf051e72292 100644 --- a/android/guava/src/com/google/common/util/concurrent/MoreExecutors.java +++ b/android/guava/src/com/google/common/util/concurrent/MoreExecutors.java @@ -707,7 +707,10 @@ protected String pendingToString() { * An implementation of {@link ExecutorService#invokeAny} for {@link ListeningExecutorService} * implementations. */ - @SuppressWarnings("GoodTime") // should accept a java.time.Duration + @SuppressWarnings({ + "GoodTime", // should accept a java.time.Duration + "CatchingUnchecked", // sneaky checked exception + }) @J2ktIncompatible @GwtIncompatible @ParametricNullness @@ -770,7 +773,9 @@ protected String pendingToString() { return f.get(); } catch (ExecutionException eex) { ee = eex; - } catch (RuntimeException rex) { + } catch (InterruptedException iex) { + throw iex; + } catch (Exception rex) { // sneaky checked exception ee = new ExecutionException(rex); } } diff --git a/android/guava/src/com/google/common/util/concurrent/UncaughtExceptionHandlers.java b/android/guava/src/com/google/common/util/concurrent/UncaughtExceptionHandlers.java index 14df42aea235..9890c89d6786 100644 --- a/android/guava/src/com/google/common/util/concurrent/UncaughtExceptionHandlers.java +++ b/android/guava/src/com/google/common/util/concurrent/UncaughtExceptionHandlers.java @@ -73,7 +73,7 @@ public void uncaughtException(Thread t, Throwable e) { SEVERE, String.format(Locale.ROOT, "Caught an exception in %s. Shutting down.", t), e); - } catch (RuntimeException | Error errorInLogging) { + } catch (Throwable errorInLogging) { // sneaky checked exception // If logging fails, e.g. due to missing memory, at least try to log the // message and the cause for the failed logging. System.err.println(e.getMessage()); diff --git a/guava-testlib/src/com/google/common/collect/testing/AbstractIteratorTester.java b/guava-testlib/src/com/google/common/collect/testing/AbstractIteratorTester.java index e9242a79401e..43a3d066d620 100644 --- a/guava-testlib/src/com/google/common/collect/testing/AbstractIteratorTester.java +++ b/guava-testlib/src/com/google/common/collect/testing/AbstractIteratorTester.java @@ -58,7 +58,7 @@ private abstract static class PermittedMetaException extends RuntimeException { static final PermittedMetaException UOE_OR_ISE = new PermittedMetaException("UnsupportedOperationException or IllegalStateException") { @Override - boolean isPermitted(RuntimeException exception) { + boolean isPermitted(Exception exception) { return exception instanceof UnsupportedOperationException || exception instanceof IllegalStateException; } @@ -66,21 +66,21 @@ boolean isPermitted(RuntimeException exception) { static final PermittedMetaException UOE = new PermittedMetaException("UnsupportedOperationException") { @Override - boolean isPermitted(RuntimeException exception) { + boolean isPermitted(Exception exception) { return exception instanceof UnsupportedOperationException; } }; static final PermittedMetaException ISE = new PermittedMetaException("IllegalStateException") { @Override - boolean isPermitted(RuntimeException exception) { + boolean isPermitted(Exception exception) { return exception instanceof IllegalStateException; } }; static final PermittedMetaException NSEE = new PermittedMetaException("NoSuchElementException") { @Override - boolean isPermitted(RuntimeException exception) { + boolean isPermitted(Exception exception) { return exception instanceof NoSuchElementException; } }; @@ -89,9 +89,9 @@ private PermittedMetaException(String message) { super(message); } - abstract boolean isPermitted(RuntimeException exception); + abstract boolean isPermitted(Exception exception); - void assertPermitted(RuntimeException exception) { + void assertPermitted(Exception exception) { if (!isPermitted(exception)) { String message = "Exception " @@ -313,10 +313,11 @@ public enum KnownOrder { protected void verify(List elements) {} /** Executes the test. */ + @SuppressWarnings("CatchingUnchecked") // sneaky checked exception public final void test() { try { recurse(0); - } catch (RuntimeException e) { + } catch (Exception e) { // sneaky checked exception throw new RuntimeException(Arrays.toString(stimuli), e); } } @@ -388,16 +389,17 @@ private interface IteratorOperation { * * @see Stimulus#executeAndCompare(ListIterator, Iterator) */ + @SuppressWarnings("CatchingUnchecked") // sneaky checked exception private > void internalExecuteAndCompare( T reference, T target, IteratorOperation method) { Object referenceReturnValue = null; PermittedMetaException referenceException = null; Object targetReturnValue = null; - RuntimeException targetException = null; + Exception targetException = null; try { targetReturnValue = method.execute(target); - } catch (RuntimeException e) { + } catch (Exception e) { // sneaky checked exception targetException = e; } diff --git a/guava-testlib/src/com/google/common/testing/ClassSanityTester.java b/guava-testlib/src/com/google/common/testing/ClassSanityTester.java index d327666fee95..e0998f0afbf7 100644 --- a/guava-testlib/src/com/google/common/testing/ClassSanityTester.java +++ b/guava-testlib/src/com/google/common/testing/ClassSanityTester.java @@ -496,13 +496,14 @@ public FactoryMethodReturnValueTester testEquals() throws Exception { * @return this tester */ @CanIgnoreReturnValue + @SuppressWarnings("CatchingUnchecked") // sneaky checked exception public FactoryMethodReturnValueTester testSerializable() throws Exception { for (Invokable factory : getFactoriesToTest()) { Object instance = instantiate(factory); if (instance != null) { try { SerializableTester.reserialize(instance); - } catch (RuntimeException e) { + } catch (Exception e) { // sneaky checked exception AssertionError error = new AssertionFailedError("Serialization failed on return value of " + factory); error.initCause(e.getCause()); @@ -522,6 +523,7 @@ public FactoryMethodReturnValueTester testSerializable() throws Exception { * @return this tester */ @CanIgnoreReturnValue + @SuppressWarnings("CatchingUnchecked") // sneaky checked exception public FactoryMethodReturnValueTester testEqualsAndSerializable() throws Exception { for (Invokable factory : getFactoriesToTest()) { try { @@ -533,7 +535,7 @@ public FactoryMethodReturnValueTester testEqualsAndSerializable() throws Excepti if (instance != null) { try { SerializableTester.reserializeAndAssert(instance); - } catch (RuntimeException e) { + } catch (Exception e) { // sneaky checked exception AssertionError error = new AssertionFailedError("Serialization failed on return value of " + factory); error.initCause(e.getCause()); diff --git a/guava/src/com/google/common/collect/Streams.java b/guava/src/com/google/common/collect/Streams.java index f2a2e819d14b..8209cedb55ff 100644 --- a/guava/src/com/google/common/collect/Streams.java +++ b/guava/src/com/google/common/collect/Streams.java @@ -148,16 +148,17 @@ public static DoubleStream stream(OptionalDouble optional) { return optional.isPresent() ? DoubleStream.of(optional.getAsDouble()) : DoubleStream.empty(); } + @SuppressWarnings("CatchingUnchecked") // sneaky checked exception private static void closeAll(BaseStream[] toClose) { - // If one of the streams throws a RuntimeException, continue closing the others, then throw the + // If one of the streams throws an exception, continue closing the others, then throw the // exception later. If more than one stream throws an exception, the later ones are added to the // first as suppressed exceptions. We don't catch Error on the grounds that it should be allowed // to propagate immediately. - RuntimeException exception = null; + Exception exception = null; for (BaseStream stream : toClose) { try { stream.close(); - } catch (RuntimeException e) { + } catch (Exception e) { // sneaky checked exception if (exception == null) { exception = e; } else { @@ -166,10 +167,23 @@ private static void closeAll(BaseStream[] toClose) { } } if (exception != null) { - throw exception; + // Normally this is a RuntimeException that doesn't need sneakyThrow. + // But theoretically we could see sneaky checked exception + sneakyThrow(exception); } } + /** Throws an undeclared checked exception. */ + private static void sneakyThrow(Throwable t) { + class SneakyThrower { + @SuppressWarnings("unchecked") // not really safe, but that's the point + void throwIt(Throwable t) throws T { + throw (T) t; + } + } + new SneakyThrower().throwIt(t); + } + /** * Returns a {@link Stream} containing the elements of the first stream, followed by the elements * of the second stream, and so on. diff --git a/guava/src/com/google/common/hash/BloomFilter.java b/guava/src/com/google/common/hash/BloomFilter.java index 2a398286499f..fb0542f899ed 100644 --- a/guava/src/com/google/common/hash/BloomFilter.java +++ b/guava/src/com/google/common/hash/BloomFilter.java @@ -597,6 +597,7 @@ public void writeTo(OutputStream out) throws IOException { * @throws IOException if the InputStream throws an {@code IOException}, or if its data does not * appear to be a BloomFilter serialized using the {@linkplain #writeTo(OutputStream)} method. */ + @SuppressWarnings("CatchingUnchecked") // sneaky checked exception public static BloomFilter readFrom( InputStream in, Funnel funnel) throws IOException { checkNotNull(in, "InputStream"); @@ -621,7 +622,9 @@ public void writeTo(OutputStream out) throws IOException { } return new BloomFilter(dataArray, numHashFunctions, funnel, strategy); - } catch (RuntimeException e) { + } catch (IOException e) { + throw e; + } catch (Exception e) { // sneaky checked exception String message = "Unable to deserialize BloomFilter from InputStream." + " strategyOrdinal: " diff --git a/guava/src/com/google/common/reflect/Invokable.java b/guava/src/com/google/common/reflect/Invokable.java index 0b2b41679c7c..2e2c04193bbf 100644 --- a/guava/src/com/google/common/reflect/Invokable.java +++ b/guava/src/com/google/common/reflect/Invokable.java @@ -117,13 +117,14 @@ public final void setAccessible(boolean flag) { } /** See {@link java.lang.reflect.AccessibleObject#trySetAccessible()}. */ + @SuppressWarnings("CatchingUnchecked") // sneaky checked exception public final boolean trySetAccessible() { // We can't call accessibleObject.trySetAccessible since that was added in Java 9 and this code // should work on Java 8. So we emulate it this way. try { accessibleObject.setAccessible(true); return true; - } catch (RuntimeException e) { + } catch (Exception e) { // sneaky checked exception return false; } } diff --git a/guava/src/com/google/common/util/concurrent/AbstractCatchingFuture.java b/guava/src/com/google/common/util/concurrent/AbstractCatchingFuture.java index e5df53835be7..8fa6500280d6 100644 --- a/guava/src/com/google/common/util/concurrent/AbstractCatchingFuture.java +++ b/guava/src/com/google/common/util/concurrent/AbstractCatchingFuture.java @@ -108,8 +108,8 @@ public final void run() { + e.getClass() + " without a cause"); } - } catch (RuntimeException | Error e) { // this includes cancellation exception - throwable = e; + } catch (Throwable t) { // this includes CancellationException and sneaky checked exception + throwable = t; } if (throwable == null) { diff --git a/guava/src/com/google/common/util/concurrent/AbstractFuture.java b/guava/src/com/google/common/util/concurrent/AbstractFuture.java index eee352c5b48c..a886f10c2ccb 100644 --- a/guava/src/com/google/common/util/concurrent/AbstractFuture.java +++ b/guava/src/com/google/common/util/concurrent/AbstractFuture.java @@ -155,7 +155,7 @@ public final boolean cancel(boolean mayInterruptIfRunning) { try { helper = new UnsafeAtomicHelper(); - } catch (RuntimeException | Error unsafeFailure) { + } catch (Exception | Error unsafeFailure) { // sneaky checked exception thrownUnsafeFailure = unsafeFailure; // catch absolutely everything and fall through to our 'SafeAtomicHelper' // The access control checks that ARFU does means the caller class has to be AbstractFuture @@ -168,7 +168,8 @@ public final boolean cancel(boolean mayInterruptIfRunning) { newUpdater(AbstractFuture.class, Waiter.class, "waiters"), newUpdater(AbstractFuture.class, Listener.class, "listeners"), newUpdater(AbstractFuture.class, Object.class, "value")); - } catch (RuntimeException | Error atomicReferenceFieldUpdaterFailure) { + } catch (Exception // sneaky checked exception + | Error atomicReferenceFieldUpdaterFailure) { // Some Android 5.0.x Samsung devices have bugs in JDK reflection APIs that cause // getDeclaredField to throw a NoSuchFieldException when the field is definitely there. // For these users fallback to a suboptimal implementation, based on synchronized. This will @@ -874,7 +875,7 @@ protected boolean setFuture(ListenableFuture future) { Failure failure; try { failure = new Failure(t); - } catch (RuntimeException | Error oomMostLikely) { + } catch (Exception | Error oomMostLikely) { // sneaky checked exception failure = Failure.FALLBACK_INSTANCE; } // Note: The only way this CAS could fail is if cancel() has raced with us. That is ok. @@ -969,7 +970,7 @@ private static Object getFutureValue(ListenableFuture future) { cancellation)); } return new Cancellation(false, cancellation); - } catch (RuntimeException | Error t) { + } catch (Exception | Error t) { // sneaky checked exception return new Failure(t); } } @@ -1386,8 +1387,6 @@ public sun.misc.Unsafe run() throws Exception { UNSAFE = unsafe; } catch (NoSuchFieldException e) { throw new RuntimeException(e); - } catch (RuntimeException e) { - throw e; } } diff --git a/guava/src/com/google/common/util/concurrent/AbstractTransformFuture.java b/guava/src/com/google/common/util/concurrent/AbstractTransformFuture.java index 5581b5fae5bc..3c5f30b63fb9 100644 --- a/guava/src/com/google/common/util/concurrent/AbstractTransformFuture.java +++ b/guava/src/com/google/common/util/concurrent/AbstractTransformFuture.java @@ -66,6 +66,7 @@ abstract class AbstractTransformFuture< } @Override + @SuppressWarnings("CatchingUnchecked") // sneaky checked exception public final void run() { ListenableFuture localInputFuture = inputFuture; F localFunction = function; @@ -104,7 +105,7 @@ public final void run() { // Set the cause of the exception as this future's exception. setException(e.getCause()); return; - } catch (RuntimeException e) { + } catch (Exception e) { // sneaky checked exception // Bug in inputFuture.get(). Propagate to the output Future so that its consumers don't hang. setException(e); return; diff --git a/guava/src/com/google/common/util/concurrent/AggregateFuture.java b/guava/src/com/google/common/util/concurrent/AggregateFuture.java index 6a8ae32f7c00..35347741667d 100644 --- a/guava/src/com/google/common/util/concurrent/AggregateFuture.java +++ b/guava/src/com/google/common/util/concurrent/AggregateFuture.java @@ -267,7 +267,7 @@ private void collectValueFromNonCancelledFuture(int index, Future new SafeAtomicHelper( newUpdater(AggregateFutureState.class, Set.class, "seenExceptions"), newUpdater(AggregateFutureState.class, "remaining")); - } catch (RuntimeException | Error reflectionFailure) { + } catch (Throwable reflectionFailure) { // sneaky checked exception // Some Android 5.0.x Samsung devices have bugs in JDK reflection APIs that cause // getDeclaredField to throw a NoSuchFieldException when the field is definitely there. // For these users fallback to a suboptimal implementation, based on synchronized. This will diff --git a/guava/src/com/google/common/util/concurrent/FakeTimeLimiter.java b/guava/src/com/google/common/util/concurrent/FakeTimeLimiter.java index 1c16cfb146d9..0f8a17894f59 100644 --- a/guava/src/com/google/common/util/concurrent/FakeTimeLimiter.java +++ b/guava/src/com/google/common/util/concurrent/FakeTimeLimiter.java @@ -77,12 +77,13 @@ public T newProxy( } @Override + @SuppressWarnings("CatchingUnchecked") // sneaky checked exception public void runWithTimeout(Runnable runnable, long timeoutDuration, TimeUnit timeoutUnit) { checkNotNull(runnable); checkNotNull(timeoutUnit); try { runnable.run(); - } catch (RuntimeException e) { + } catch (Exception e) { // sneaky checked exception throw new UncheckedExecutionException(e); } catch (Error e) { throw new ExecutionError(e); diff --git a/guava/src/com/google/common/util/concurrent/FuturesGetChecked.java b/guava/src/com/google/common/util/concurrent/FuturesGetChecked.java index 425e7d5559cf..17e0675b6319 100644 --- a/guava/src/com/google/common/util/concurrent/FuturesGetChecked.java +++ b/guava/src/com/google/common/util/concurrent/FuturesGetChecked.java @@ -224,7 +224,7 @@ private static boolean hasConstructorUsableByGetChecked( try { Exception unused = newWithCause(exceptionClass, new Exception()); return true; - } catch (RuntimeException | Error e) { + } catch (Throwable t) { // sneaky checked exception return false; } } diff --git a/guava/src/com/google/common/util/concurrent/JdkFutureAdapters.java b/guava/src/com/google/common/util/concurrent/JdkFutureAdapters.java index 33403c978a09..eb3a24707270 100644 --- a/guava/src/com/google/common/util/concurrent/JdkFutureAdapters.java +++ b/guava/src/com/google/common/util/concurrent/JdkFutureAdapters.java @@ -19,7 +19,6 @@ import com.google.common.annotations.GwtIncompatible; import com.google.common.annotations.J2ktIncompatible; -import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.Future; @@ -162,10 +161,11 @@ public void addListener(Runnable listener, Executor exec) { * to return a proper ListenableFuture instead of using listenInPoolThread. */ getUninterruptibly(delegate); - } catch (ExecutionException | RuntimeException | Error e) { - // (including CancellationException) + } catch (Throwable t) { + // (including CancellationException and sneaky checked exception) // The task is presumably done, run the listeners. - // TODO(cpovirk): Do *something* in case of Error (and maybe RuntimeException)? + // TODO(cpovirk): Do *something* in case of Error (and maybe + // non-CancellationException, non-ExecutionException exceptions)? } executionList.execute(); }); diff --git a/guava/src/com/google/common/util/concurrent/MoreExecutors.java b/guava/src/com/google/common/util/concurrent/MoreExecutors.java index 824c2001538e..46536fbe7ca6 100644 --- a/guava/src/com/google/common/util/concurrent/MoreExecutors.java +++ b/guava/src/com/google/common/util/concurrent/MoreExecutors.java @@ -785,7 +785,10 @@ protected String pendingToString() { * An implementation of {@link ExecutorService#invokeAny} for {@link ListeningExecutorService} * implementations. */ - @SuppressWarnings("GoodTime") // should accept a java.time.Duration + @SuppressWarnings({ + "GoodTime", // should accept a java.time.Duration + "CatchingUnchecked", // sneaky checked exception + }) @J2ktIncompatible @GwtIncompatible @ParametricNullness @@ -848,7 +851,9 @@ protected String pendingToString() { return f.get(); } catch (ExecutionException eex) { ee = eex; - } catch (RuntimeException rex) { + } catch (InterruptedException iex) { + throw iex; + } catch (Exception rex) { // sneaky checked exception ee = new ExecutionException(rex); } } diff --git a/guava/src/com/google/common/util/concurrent/UncaughtExceptionHandlers.java b/guava/src/com/google/common/util/concurrent/UncaughtExceptionHandlers.java index 14df42aea235..9890c89d6786 100644 --- a/guava/src/com/google/common/util/concurrent/UncaughtExceptionHandlers.java +++ b/guava/src/com/google/common/util/concurrent/UncaughtExceptionHandlers.java @@ -73,7 +73,7 @@ public void uncaughtException(Thread t, Throwable e) { SEVERE, String.format(Locale.ROOT, "Caught an exception in %s. Shutting down.", t), e); - } catch (RuntimeException | Error errorInLogging) { + } catch (Throwable errorInLogging) { // sneaky checked exception // If logging fails, e.g. due to missing memory, at least try to log the // message and the cause for the failed logging. System.err.println(e.getMessage());