-
Notifications
You must be signed in to change notification settings - Fork 135
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Error prone ExceptionSpecificity check to avoid catching too broadly #1074
Conversation
Generate changelog in
|
Small flag for people to be aware - scala treats all exceptions as unchecked so this autofix can break things that integrate with some scala code where you catch an exception (however, java already yells at you for catching checked exception where one is not defined) |
ee1c274
to
36219e5
Compare
Prefer more specific catch types than Exception and Throwable. When methods are updated to throw new checked exceptions they expect callers to handle failure types explicitly. Catching broad types defeats the type system. By catching the most specific types possible we leverage existing compiler functionality to detect unreachable code.
36219e5
to
0ceeece
Compare
|
||
@Override | ||
@SuppressWarnings("CyclomaticComplexity") | ||
public Description matchMethod(MethodTree tree, VisitorState state) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wondering if it would be clearer if this lived in a separate class? That way we'd have two error-prone checks that just do one thing, e.g. CatchSpecificity and ThrowsSpecificity?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could go either way on this, there are two manifestations of the same conceptual issue. I suppose it boils down to whether we want bug checkers mapped to concepts, or implementation/output?
I was thinking about this yesterday, specifically for the assertj cleanup checkers -- it might be easiest for consumers to understand if all assertj refactor checks were part of a single 'AssertjUsage' check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I'm mainly thinking about this from a maintenance perspective, as shorter classes that just do one thing are easier to understand as I can be sure each private method is somehow relevant to the one purpose of the class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good point
baseline-error-prone/src/main/java/com/palantir/baseline/errorprone/ExceptionSpecificity.java
Outdated
Show resolved
Hide resolved
encounteredTypes.add(catchType); | ||
} | ||
return Description.NO_MATCH; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think i'm still struggling with this method - there's quite a lot of intricacy in the de-duping and selecting only exceptions&throwables that is all woven together to get this all done in one pass.
what about if we did a loop through all the possible tree.getCatches()
to accumulate the encounteredTypes
, then could call out to a dedicated function to process each single catch?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also the possibility of fully returning from deep inside a loop & if condition feels a bit weird - are we saying if we hit one catch(Exception)
then a later catch(Throwable)
might not get processed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
are we saying if we hit one catch(Exception) then a later catch(Throwable) might not get processed?
Yep, we have a test for that case which takes two rounds to resolve. We can do it in one loop, but I worried about the additional complexity. I can take a look at implementing it and we can decide from there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally I'm fine with it just doing one thing at a time - minimum complexity = happy dan. Just wanted to check my understanding.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Turns out it's straightforward to fix 'em all at once. I think that blocks us from splitting into multiple loops though because the changes mutate state, and we would have to keep expected handled types in sync with the code that builds suggested fixes, leading us back to the original model.
+ "new checked exceptions they expect callers to handle failure types explicitly. Catching broad " | ||
+ "types defeats the type system. By catching the most specific types possible we leverage existing " | ||
+ "compiler functionality to detect unreachable code.") | ||
public final class ThrowSpecificity extends BugChecker implements BugChecker.MethodTreeMatcher { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also is this just a strictly better version of https://github.com/google/error-prone/blob/master/core/src/main/java/com/google/errorprone/bugpatterns/ThrowSpecificExceptions.java ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh wow is state.errorProneOptions().isTestOnlyTarget()
new?
I think there's a lot of overlap, ThrowSpecificExceptions.java
recommends changing throw <throwable>
blocks to use more specific types, where this check produces no runtime changes, only provides stronger type safety by narrowing declared throws types on methods.
Before this PR
After this PR
==COMMIT_MSG==
Error prone ExceptionSpecificity check to avoid catching too broadly
Prefer more specific catch types than Exception and Throwable.
When methods are updated to throw new checked exceptions they expect
callers to handle failure types explicitly. Catching broad types defeats
the type system. By catching the most specific types possible we
leverage existing compiler functionality to detect unreachable code.
==COMMIT_MSG==
Possible downsides?
Possible to create a large diff in projects which don't follow best practices.
As always there's a chance the rewrite is not completely accurate.