Add TryWithResources recipe (RSPEC-S2093)#831
Conversation
Converts try/finally blocks to try-with-resources when the finally block only closes an AutoCloseable resource. Handles direct close, null-guarded close, and try-catch wrapped close patterns in the finally block.
|
At-scale validation resultsRan Summary
Repos with changes (sorted by file count)
Patch reviewReviewed patches from Kafka, Lucene, JMeter, ActiveMQ, and CXF. All transformations are correct:
|
- Replace manual boolean+ArrayList loop with ListUtils.map for idiomatic OpenRewrite style (returns same list reference when unchanged) - Handle single-statement null-guarded close without braces: if (x != null) x.close(); - Add test for braceless null-guard pattern
| J.VariableDeclarations varDecl = (J.VariableDeclarations) stmt; | ||
| J.Try tryStmt = (J.Try) stmts.get(i + 1); | ||
| if (canTransform(varDecl, tryStmt, stmts, i)) { | ||
| return transform(varDecl, tryStmt); |
There was a problem hiding this comment.
Instead of transforming the variable declaration, does it not make more sense to return null here and add the variable to the existing try? That saves us having to call canTransform twice.
|
We can also compare against #599 . Perhaps we find some more corner cases or tests covered there. |
Support resources used after the try block via Java 9+ try(varName) syntax. Handle finally blocks with extra logic by stripping just the close statement and keeping remaining statements. Add negative tests for qualified close calls, field-based resources, and null-initialized resources closed in catch blocks. Based on review feedback from PR #599.
Handle the transform in the try branch (which looks back at the preceding varDecl), and use the varDecl branch only for removal. This makes the try branch the single source of truth for the transformation decision.
Replace custom isNullLiteral helper with the existing J.Literal.isLiteralValue API. Add @nullable to stripCloseFromFinally parameter. Remove redundant parentheses in null-init check.
Thanks! yes had that one in mind as well; now did the explicit comparison and cover a few more cases already. |
src/main/java/org/openrewrite/staticanalysis/TryWithResources.java
Outdated
Show resolved
Hide resolved
src/main/java/org/openrewrite/staticanalysis/TryWithResources.java
Outdated
Show resolved
Hide resolved
| if (stmt instanceof J.Try && i > 0 | ||
| && stmts.get(i - 1) instanceof J.VariableDeclarations) { |
There was a problem hiding this comment.
Is the implication here (and below), that the variable declaration has to happen directly before the try block? It feels like it may miss a number of scenarios where you have the variable declaration for the AutoCloseable, one or more other statements, and then the try block.
There was a problem hiding this comment.
I'd indeed kept that for simplicity for now; we can extend it of course, but figured this first set was a safe default to merge already.
src/main/java/org/openrewrite/staticanalysis/UseTryWithResources.java
Outdated
Show resolved
Hide resolved
src/main/java/org/openrewrite/staticanalysis/UseTryWithResources.java
Outdated
Show resolved
Hide resolved
steve-aom-elliott
left a comment
There was a problem hiding this comment.
Approved so far as an initial version goes, having already noted about the variable / try block detection that could be extended in future.
Summary
TryWithResourcesrecipe that converts try/finally blocks to try-with-resources when the finally block only closes anAutoCloseableresourcePatterns handled
Recognizes close in finally as any of:
in.close()if (in != null) { in.close(); }(with or without braces)try { in.close(); } catch (...) {}Features
try(var)syntax: When the resource variable is used after the try block, usestry (varName) { ... }syntax keeping the declaration in placeSafety guards (skips transformation when)
+=,++, etc.)AutoCloseable.close()call)nullwrapper.getStream().close())Test plan