|
16 | 16 | package org.openrewrite.staticanalysis; |
17 | 17 |
|
18 | 18 | import lombok.Getter; |
| 19 | +import org.jspecify.annotations.Nullable; |
19 | 20 | import org.openrewrite.ExecutionContext; |
20 | 21 | import org.openrewrite.Recipe; |
21 | 22 | import org.openrewrite.Repeat; |
@@ -50,41 +51,103 @@ public TreeVisitor<?, ExecutionContext> getVisitor() { |
50 | 51 | @Override |
51 | 52 | public J.Block visitBlock(J.Block block, ExecutionContext ctx) { |
52 | 53 | J.Block b = visitAndCast(block, ctx, super::visitBlock); |
53 | | - AtomicReference<Space> endWhitespace = new AtomicReference<>(null); |
| 54 | + AtomicReference<@Nullable Space> endWhitespace = new AtomicReference<>(null); |
54 | 55 | J.Block alteredBlock = b.withStatements(ListUtils.flatMap(b.getStatements(), statement -> { |
55 | 56 | if (statement instanceof J.If) { |
56 | 57 | J.If ifStatement = (J.If) statement; |
57 | 58 | if (ifStatement.getElsePart() != null && endsWithReturnOrThrow(ifStatement.getThenPart())) { |
58 | | - J.If newIf = ifStatement.withElsePart(null); |
59 | 59 | Statement elsePart = ifStatement.getElsePart().getBody(); |
60 | | - if (elsePart instanceof J.Block) { |
61 | | - J.Block elseBlock = (J.Block) elsePart; |
62 | | - endWhitespace.set(elseBlock.getEnd()); |
63 | | - return ListUtils.concat(newIf, ListUtils.mapFirst(elseBlock.getStatements(), elseStmt -> { |
64 | | - // Combine comments from the else block itself and the first statement |
65 | | - List<Comment> elseComments = elseBlock.getPrefix().getComments(); |
66 | | - List<Comment> stmtComments = elseStmt.getPrefix().getComments(); |
67 | | - if (!elseComments.isEmpty() || !stmtComments.isEmpty()) { |
68 | | - return elseStmt.withComments(ListUtils.concatAll(elseComments, stmtComments)); |
| 60 | + if (elsePart instanceof J.If) { |
| 61 | + // Else-if chain: find and unwrap the innermost else |
| 62 | + J.If innermost = findInnermostIfWithElse((J.If) elsePart); |
| 63 | + if (innermost != null && |
| 64 | + innermost.getElsePart() != null && |
| 65 | + endsWithReturnOrThrow(innermost.getThenPart()) && |
| 66 | + !(innermost.getElsePart().getBody() instanceof J.If)) { |
| 67 | + // Unwrap the innermost else |
| 68 | + J.If modifiedChain = removeInnermostElse(ifStatement); |
| 69 | + Statement innermostElseBody = innermost.getElsePart().getBody(); |
| 70 | + if (innermostElseBody instanceof J.Block) { |
| 71 | + J.Block elseBlock = (J.Block) innermostElseBody; |
| 72 | + endWhitespace.set(elseBlock.getEnd()); |
| 73 | + return ListUtils.concat(modifiedChain, ListUtils.mapFirst(elseBlock.getStatements(), elseStmt -> { |
| 74 | + List<Comment> elseComments = elseBlock.getPrefix().getComments(); |
| 75 | + List<Comment> stmtComments = elseStmt.getPrefix().getComments(); |
| 76 | + if (!elseComments.isEmpty() || !stmtComments.isEmpty()) { |
| 77 | + return elseStmt.withComments(ListUtils.concatAll(elseComments, stmtComments)); |
| 78 | + } |
| 79 | + String whitespace = innermost.getElsePart().getPrefix().getWhitespace(); |
| 80 | + return elseStmt.withPrefix(elseStmt.getPrefix().withWhitespace(whitespace)); |
| 81 | + })); |
69 | 82 | } |
70 | | - String whitespace = ifStatement.getElsePart().getPrefix().getWhitespace(); |
71 | | - return elseStmt.withPrefix(elseStmt.getPrefix().withWhitespace(whitespace)); |
72 | | - })); |
| 83 | + return Arrays.asList(modifiedChain, innermostElseBody.<Statement>withPrefix(innermost.getElsePart().getPrefix())); |
| 84 | + } |
| 85 | + } else { |
| 86 | + // Plain else block: unwrap directly |
| 87 | + J.If newIf = ifStatement.withElsePart(null); |
| 88 | + if (elsePart instanceof J.Block) { |
| 89 | + J.Block elseBlock = (J.Block) elsePart; |
| 90 | + endWhitespace.set(elseBlock.getEnd()); |
| 91 | + return ListUtils.concat(newIf, ListUtils.mapFirst(elseBlock.getStatements(), elseStmt -> { |
| 92 | + List<Comment> elseComments = elseBlock.getPrefix().getComments(); |
| 93 | + List<Comment> stmtComments = elseStmt.getPrefix().getComments(); |
| 94 | + if (!elseComments.isEmpty() || !stmtComments.isEmpty()) { |
| 95 | + return elseStmt.withComments(ListUtils.concatAll(elseComments, stmtComments)); |
| 96 | + } |
| 97 | + String whitespace = ifStatement.getElsePart().getPrefix().getWhitespace(); |
| 98 | + return elseStmt.withPrefix(elseStmt.getPrefix().withWhitespace(whitespace)); |
| 99 | + })); |
| 100 | + } |
| 101 | + return Arrays.asList(newIf, elsePart.<Statement>withPrefix(ifStatement.getElsePart().getPrefix())); |
73 | 102 | } |
74 | | - return Arrays.asList(newIf, elsePart.<Statement>withPrefix(ifStatement.getElsePart().getPrefix())); |
75 | 103 | } |
76 | 104 | } |
77 | 105 | return statement; |
78 | 106 | })); |
79 | 107 |
|
80 | | - if (endWhitespace.get() != null) { |
81 | | - List<Comment> mergedComments = ListUtils.concatAll(endWhitespace.get().getComments(), b.getEnd().getComments()); |
82 | | - alteredBlock = alteredBlock.withEnd(b.getEnd().withComments(mergedComments).withWhitespace(endWhitespace.get().getWhitespace())); |
| 108 | + Space end = endWhitespace.get(); |
| 109 | + if (end != null) { |
| 110 | + List<Comment> mergedComments = ListUtils.concatAll(end.getComments(), b.getEnd().getComments()); |
| 111 | + alteredBlock = alteredBlock.withEnd(b.getEnd().withComments(mergedComments).withWhitespace(end.getWhitespace())); |
83 | 112 | } |
84 | 113 |
|
85 | 114 | return maybeAutoFormat(b, alteredBlock, ctx); |
86 | 115 | } |
87 | 116 |
|
| 117 | + private J.@Nullable If findInnermostIfWithElse(J.If ifStatement) { |
| 118 | + if (ifStatement.getElsePart() == null) { |
| 119 | + return null; |
| 120 | + } |
| 121 | + Statement elseBody = ifStatement.getElsePart().getBody(); |
| 122 | + if (elseBody instanceof J.If) { |
| 123 | + J.If result = findInnermostIfWithElse((J.If) elseBody); |
| 124 | + return result != null ? result : ifStatement; |
| 125 | + } |
| 126 | + return ifStatement; |
| 127 | + } |
| 128 | + |
| 129 | + private J.If removeInnermostElse(J.If ifStatement) { |
| 130 | + if (ifStatement.getElsePart() == null) { |
| 131 | + return ifStatement; |
| 132 | + } |
| 133 | + Statement elseBody = ifStatement.getElsePart().getBody(); |
| 134 | + if (elseBody instanceof J.If) { |
| 135 | + J.If innerIf = (J.If) elseBody; |
| 136 | + if (innerIf.getElsePart() != null && !(innerIf.getElsePart().getBody() instanceof J.If)) { |
| 137 | + // This is the innermost if with a non-if else, remove its else |
| 138 | + return ifStatement.withElsePart( |
| 139 | + ifStatement.getElsePart().withBody(innerIf.withElsePart(null)) |
| 140 | + ); |
| 141 | + } |
| 142 | + // Recurse deeper into the chain |
| 143 | + return ifStatement.withElsePart( |
| 144 | + ifStatement.getElsePart().withBody(removeInnermostElse(innerIf)) |
| 145 | + ); |
| 146 | + } |
| 147 | + // Direct else (not else-if), remove it |
| 148 | + return ifStatement.withElsePart(null); |
| 149 | + } |
| 150 | + |
88 | 151 | private boolean endsWithReturnOrThrow(Statement statement) { |
89 | 152 | if (statement instanceof J.Return || statement instanceof J.Throw) { |
90 | 153 | return true; |
|
0 commit comments