Skip to content

Commit c8ff14a

Browse files
authored
Added transform to merge stacked try statements. (#350)
Also added this a a last step to ResourceLeakFixer transform. Closes #341.
1 parent 7620608 commit c8ff14a

File tree

7 files changed

+128
-16
lines changed

7 files changed

+128
-16
lines changed

core-codemods/src/main/java/io/codemodder/codemods/ResourceLeakFixer.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,20 @@ public static Optional<TryStmt> checkAndFix(final Expression expr) {
5151
// finds the root expression in a chain of new AutoCloseable objects
5252
ObjectCreationExpr root = findRootExpression(expr.asObjectCreationExpr());
5353
if (isFixable(root)) {
54-
return tryToFix(root);
54+
var maybeFixed = tryToFix(root);
55+
// try to merge with an encompassing try stmt and return
56+
return maybeFixed
57+
.flatMap(ts -> ASTTransforms.mergeStackedTryStmts(ts))
58+
.or(() -> maybeFixed);
5559
}
5660
}
5761
if (expr instanceof MethodCallExpr) {
5862
if (isFixable(expr)) {
59-
return tryToFix(expr.asMethodCallExpr());
63+
var maybeFixed = tryToFix(expr.asMethodCallExpr());
64+
// try to merge with an encompassing try stmt and return
65+
return maybeFixed
66+
.flatMap(ts -> ASTTransforms.mergeStackedTryStmts(ts))
67+
.or(() -> maybeFixed);
6068
}
6169
}
6270
return Optional.empty();

core-codemods/src/test/resources/database-resource-leak/Test.java.after

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,9 @@ public class Test {
5757
}
5858

5959
public void simple(String query) throws SQLException {
60-
try (Statement stmt = conn.createStatement()) {
61-
try (ResultSet rs = stmt.executeQuery(query)) {
62-
}
63-
}
60+
try (Statement stmt = conn.createStatement();
61+
ResultSet rs = stmt.executeQuery(query)) {
62+
}
6463
}
6564

6665
public void assignedToParameter(String query, Statement stmt) throws SQLException {

core-codemods/src/test/resources/prevent-filewriter-leak-with-nio/Test.java.after

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,12 @@ public class Test
99
{
1010
private Response response;
1111
void foo(File f) {
12-
try (Writer change1 = Files.newBufferedWriter(f.toPath())) {
13-
try (Writer change2 = Files.newBufferedWriter(f.toPath())) {
14-
Writer dontChange1 = new BufferedWriter(w);
15-
Writer dontChange2 = new BufferedWriter(w, 256);
16-
Writer dontChange3 = new BufferedWriter(response.getWriter(), 256);
17-
Writer dontChange3 = Files.newBufferedWriter(f.toPath());
18-
}
12+
try (Writer change1 = Files.newBufferedWriter(f.toPath());
13+
Writer change2 = Files.newBufferedWriter(f.toPath())) {
14+
Writer dontChange1 = new BufferedWriter(w);
15+
Writer dontChange2 = new BufferedWriter(w, 256);
16+
Writer dontChange3 = new BufferedWriter(response.getWriter(), 256);
17+
Writer dontChange3 = Files.newBufferedWriter(f.toPath());
1918
}
2019

2120
}

core-codemods/src/test/resources/resource-leak/Test.java.after

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ public class Test {
3838
}
3939
}
4040

41+
public void mergeFixes(File f) throws IOException {
42+
try (FileReader fr = new FileReader(f);
43+
BufferedReader br = new BufferedReader(fr)) {
44+
System.out.println(br.readLine());
45+
}
46+
}
47+
4148
public Reader noFixReturned() throws IOException {
4249
BufferedReader br = new BufferedReader(new FileReader(""));
4350
BufferedReader br2 = new BufferedReader(br);

core-codemods/src/test/resources/resource-leak/Test.java.before

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ public class Test {
3232
BufferedReader br = new BufferedReader(new BufferedReader(new FileReader("~/test.txt")));
3333
}
3434

35+
public void mergeFixes(File f) throws IOException {
36+
FileReader fr = new FileReader(f);
37+
BufferedReader br = new BufferedReader(fr);
38+
System.out.println(br.readLine());
39+
}
40+
3541
public Reader noFixReturned() throws IOException {
3642
BufferedReader br = new BufferedReader(new FileReader(""));
3743
BufferedReader br2 = new BufferedReader(br);

framework/codemodder-base/src/main/java/io/codemodder/ast/ASTTransforms.java

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,13 @@ public final class ASTTransforms {
3030
public static void addImportIfMissing(final CompilationUnit cu, final String className) {
3131
final NodeList<ImportDeclaration> imports = cu.getImports();
3232
final ImportDeclaration newImport = new ImportDeclaration(className, false, false);
33-
if (addInOrdrerIfNeeded(className, imports, newImport)) {
33+
if (addInOrderIfNeeded(className, imports, newImport)) {
3434
return;
3535
}
3636
cu.addImport(className);
3737
}
3838

39-
private static boolean addInOrdrerIfNeeded(
39+
private static boolean addInOrderIfNeeded(
4040
final String className,
4141
final NodeList<ImportDeclaration> imports,
4242
final ImportDeclaration newImport) {
@@ -70,7 +70,7 @@ public static void addImportIfMissing(final CompilationUnit cu, final Class<?> c
7070
public static void addStaticImportIfMissing(final CompilationUnit cu, final String method) {
7171
final NodeList<ImportDeclaration> imports = cu.getImports();
7272
final ImportDeclaration newMethodImport = new ImportDeclaration(method, true, false);
73-
if (addInOrdrerIfNeeded(method, imports, newMethodImport)) {
73+
if (addInOrderIfNeeded(method, imports, newMethodImport)) {
7474
return;
7575
}
7676
cu.addImport(method, true, false);
@@ -371,4 +371,29 @@ private static Optional<ResolvedType> calculateResolvedType(final Expression e)
371371
return Optional.empty();
372372
}
373373
}
374+
375+
/**
376+
* Tries to merge the given try stmt with an enveloping one. Returns the merged try stmts if
377+
* sucessfull.
378+
*/
379+
public static Optional<TryStmt> mergeStackedTryStmts(final TryStmt tryStmt) {
380+
// is the parent a try statement whose single statment in its block is tryStmt?
381+
var maybeTryParent =
382+
tryStmt
383+
.getParentNode()
384+
.flatMap(p -> p.getParentNode())
385+
.map(p -> p instanceof TryStmt ? (TryStmt) p : null)
386+
.filter(
387+
ts ->
388+
ts.getTryBlock().getStatements().size() == 1
389+
&& ts.getTryBlock().getStatement(0) == tryStmt);
390+
if (maybeTryParent.isPresent()) {
391+
tryStmt.remove();
392+
var parent = maybeTryParent.get();
393+
parent.getResources().addAll(tryStmt.getResources());
394+
parent.getTryBlock().getStatements().addAll(tryStmt.getTryBlock().getStatements());
395+
return Optional.of(parent);
396+
}
397+
return Optional.empty();
398+
}
374399
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package io.codemodder.ast;
2+
3+
import static org.hamcrest.MatcherAssert.assertThat;
4+
import static org.hamcrest.Matchers.equalTo;
5+
6+
import com.github.javaparser.ast.CompilationUnit;
7+
import com.github.javaparser.ast.stmt.ExpressionStmt;
8+
import com.github.javaparser.ast.stmt.TryStmt;
9+
import io.codemodder.javaparser.JavaParserFactory;
10+
import java.io.IOException;
11+
import java.util.List;
12+
import org.junit.jupiter.api.Test;
13+
14+
final class MergeStackedTryStmtTest {
15+
16+
@Test
17+
void it_merges_try_stmts() throws IOException {
18+
String code =
19+
"""
20+
class A{
21+
void f(File f){
22+
try(var fr = new FileReader(f)){
23+
try(var br = new BufferedReader(fr)){
24+
System.out.println(br.readLine());
25+
}
26+
}
27+
}
28+
}
29+
""";
30+
CompilationUnit cu =
31+
JavaParserFactory.newFactory().create(List.of()).parse(code).getResult().get();
32+
33+
var tryStmt = cu.findAll(TryStmt.class).get(1);
34+
var call = cu.findAll(ExpressionStmt.class).get(0);
35+
36+
var merged = ASTTransforms.mergeStackedTryStmts(tryStmt);
37+
38+
assertThat(merged.isEmpty(), equalTo(false));
39+
assertThat(merged.get().getResources().size(), equalTo(2));
40+
assertThat(merged.get().getTryBlock().getStatements().isEmpty(), equalTo(false));
41+
assertThat(merged.get().getTryBlock().getStatements().get(0), equalTo(call));
42+
}
43+
44+
@Test
45+
void it_wont_merge_because_not_stacked() throws IOException {
46+
String code =
47+
"""
48+
class A{
49+
void f(File f){
50+
try(var fr = new FileReader(f)){
51+
try(var br = new BufferedReader(fr)){
52+
System.out.println(br.readLine());
53+
}
54+
System.out.println(fr.readLine());
55+
}
56+
}
57+
}
58+
""";
59+
CompilationUnit cu =
60+
JavaParserFactory.newFactory().create(List.of()).parse(code).getResult().get();
61+
62+
var tryStmt = cu.findAll(TryStmt.class).get(1);
63+
64+
var merged = ASTTransforms.mergeStackedTryStmts(tryStmt);
65+
66+
assertThat(merged.isEmpty(), equalTo(true));
67+
}
68+
}

0 commit comments

Comments
 (0)