Skip to content

Commit ff4c6ad

Browse files
committed
Added support for final variables
1 parent c597d80 commit ff4c6ad

File tree

2 files changed

+88
-26
lines changed

2 files changed

+88
-26
lines changed

src/main/java/org/openrewrite/staticanalysis/FallThroughVisitor.java

Lines changed: 62 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import lombok.EqualsAndHashCode;
1919
import lombok.Value;
20+
import org.openrewrite.Cursor;
2021
import org.openrewrite.Tree;
2122
import org.openrewrite.internal.ListUtils;
2223
import org.openrewrite.java.JavaIsoVisitor;
@@ -54,7 +55,7 @@ public J.Case visitCase(J.Case case_, P p) {
5455
if (getCursor().firstEnclosing(J.Switch.class) != null) {
5556
J.Switch switch_ = getCursor().dropParentUntil(J.Switch.class::isInstance).getValue();
5657
if (Boolean.TRUE.equals(style.getCheckLastCaseGroup()) || !isLastCase(case_, switch_)) {
57-
if (FindLastLineBreaksOrFallsThroughComments.find(switch_, c).isEmpty() && FindGuaranteedReturns.find(switch_, c).isEmpty()) {
58+
if (FindLastLineBreaksOrFallsThroughComments.find(switch_, c).isEmpty() && FindGuaranteedReturns.find(getCursor(), switch_, c).isEmpty()) {
5859
c = (J.Case) new AddBreak<>(c).visitNonNull(c, p, getCursor().getParentOrThrow());
5960
}
6061
}
@@ -232,30 +233,32 @@ private FindGuaranteedReturns() {
232233
* @param scope the {@link J.Case} to use as a target.
233234
* @return A set representing whether the case contains any guaranteed {@link J.Return} statements.
234235
*/
235-
private static Set<J> find(J.Switch enclosingSwitch, J.Case scope) {
236+
private static Set<J> find(Cursor cursor, J.Switch enclosingSwitch, J.Case scope) {
236237
Set<J> references = new HashSet<>();
237-
new FindGuaranteedReturnsVisitor(scope).visit(enclosingSwitch, references);
238+
new FindGuaranteedReturnsVisitor(cursor, scope).visit(enclosingSwitch, references);
238239
return references;
239240
}
240241

241242
private static class FindGuaranteedReturnsVisitor extends JavaIsoVisitor<Set<J>> {
243+
private Cursor cursor;
242244
private final J.Case scope;
243245

244-
public FindGuaranteedReturnsVisitor(J.Case scope) {
246+
public FindGuaranteedReturnsVisitor(Cursor cursor, J.Case scope) {
247+
this.cursor = cursor;
245248
this.scope = scope;
246249
}
247250

248251

249-
private static boolean hasGuaranteedReturn(List<? extends Statement> trees) {
252+
private boolean hasGuaranteedReturn(List<? extends Statement> trees) {
250253
return trees.stream()
251254
.anyMatch(s -> returns(s));
252255
}
253256

254-
private static boolean returns(Statement s) {
257+
private boolean returns(Statement s) {
255258
if (s instanceof J.ForLoop) {
256259
J.ForLoop forLoop = (J.ForLoop) s;
257260
Expression condition = forLoop.getControl().getCondition();
258-
if (condition == null || condition instanceof J.Empty || (condition instanceof J.Literal && ((J.Literal) condition).getValue() == Boolean.TRUE)) {
261+
if (condition == null || condition instanceof J.Empty || (condition instanceof J.Literal && ((J.Literal) condition).getValue() == Boolean.TRUE) || isFinalTrue(condition)) {
259262
Statement body = forLoop.getBody();
260263
if (body instanceof J.Block) {
261264
return !hasBreak(((J.Block) body).getStatements()) && hasGuaranteedReturn(((J.Block) body).getStatements());
@@ -265,30 +268,71 @@ private static boolean returns(Statement s) {
265268
}
266269
} else if (s instanceof J.WhileLoop) {
267270
J.WhileLoop whileLoop = (J.WhileLoop) s;
268-
Expression condition = whileLoop.getCondition();
269-
if (((J.ControlParentheses<?>) condition).getTree() instanceof J.Literal) {
270-
J.Literal value = (J.Literal) ((J.ControlParentheses<?>) condition).getTree();
271-
if (value.getValue() == Boolean.TRUE) {
272-
Statement body = whileLoop.getBody();
273-
if (body instanceof J.Block) {
274-
return !hasBreak(((J.Block) body).getStatements()) && hasGuaranteedReturn(((J.Block) body).getStatements());
275-
} else {
276-
return hasGuaranteedReturn(Collections.singletonList(whileLoop.getBody()));
277-
}
271+
Expression condition = whileLoop.getCondition().getTree();
272+
if (condition instanceof J.Literal && ((J.Literal) condition).getValue() == Boolean.TRUE || isFinalTrue(condition)) {
273+
Statement body = whileLoop.getBody();
274+
if (body instanceof J.Block) {
275+
return !hasBreak(((J.Block) body).getStatements()) && hasGuaranteedReturn(((J.Block) body).getStatements());
276+
} else {
277+
return hasGuaranteedReturn(Collections.singletonList(whileLoop.getBody()));
278278
}
279279
}
280280
}
281281
return s instanceof J.Return;
282282
}
283283

284+
private boolean isFinalTrue(Expression condition) {
285+
if (condition instanceof J.Identifier && ((J.Identifier) condition).getFieldType() != null && ((J.Identifier) condition).getFieldType().hasFlags(Flag.Final)) {
286+
J.Identifier id = (J.Identifier) condition;
287+
J.VariableDeclarations.NamedVariable declaration = null;
288+
while (declaration == null) {
289+
J value = cursor.getValue();
290+
if (value instanceof J.Case) {
291+
List<Statement> statements = ((J.Case) value).getStatements();
292+
declaration = finalVariableDeclaration(statements, id);
293+
} else if (value instanceof J.MethodDeclaration) {
294+
List<Statement> statements = ((J.MethodDeclaration) value).getBody().getStatements();
295+
declaration = finalVariableDeclaration(statements, id);
296+
} else if (value instanceof J.ClassDeclaration) {
297+
List<Statement> statements = ((J.ClassDeclaration) value).getBody().getStatements();
298+
declaration = finalVariableDeclaration(statements, id);
299+
}
300+
cursor = cursor.getParentTreeCursor();
301+
if (cursor.isRoot()) {
302+
break;
303+
}
304+
}
305+
if (declaration != null) {
306+
return declaration.getInitializer() instanceof J.Literal && ((J.Literal) declaration.getInitializer()).getValue() == Boolean.TRUE;
307+
}
308+
}
309+
return false;
310+
}
311+
312+
private J.VariableDeclarations.NamedVariable finalVariableDeclaration(List<Statement> statements, J.Identifier id) {
313+
for (Statement s : statements) {
314+
if (s instanceof J.VariableDeclarations) {
315+
J.VariableDeclarations vd = (J.VariableDeclarations) s;
316+
if (TypeUtils.isAssignableTo("boolean", vd.getType()) && vd.hasModifier(J.Modifier.Type.Final)) {
317+
for (J.VariableDeclarations.NamedVariable v : vd.getVariables()) {
318+
if (v.getSimpleName().equals(id.getSimpleName())) {
319+
return v;
320+
}
321+
}
322+
}
323+
}
324+
}
325+
return null;
326+
}
327+
284328
private static boolean hasBreak(List<Statement> statements) {
285329
for (Statement s : statements) {
286330
if (s instanceof J.Break) {
287331
return true;
288332
} else if (s instanceof J.If) {
289333
J.If if_ = (J.If) s;
290334
Statement body = if_.getThenPart();
291-
boolean hasBreak = false;
335+
boolean hasBreak;
292336
if (body instanceof J.Block) {
293337
hasBreak = hasBreak(((J.Block) body).getStatements());
294338
} else {

src/test/java/org/openrewrite/staticanalysis/FallThroughTest.java

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -531,11 +531,12 @@ void returnNestedInAlwaysTrueLoop() {
531531
java(
532532
"""
533533
enum Enum {
534-
A, B, C, D, E, F, G
534+
A, B, C, D, E, F, G, H, I
535535
}
536536
public class Test {
537537
void foo(Enum a) {
538538
boolean b = true;
539+
final boolean finalB = true;
539540
switch(a) {
540541
case A:
541542
for (; ; ) {
@@ -550,21 +551,29 @@ void foo(Enum a) {
550551
return;
551552
}
552553
case D:
553-
for (int i = 0; i > 0; i++) {
554+
for (; finalB; ) {
554555
return;
555556
}
556557
case E:
557-
while (b) {
558+
while (finalB) {
558559
return;
559560
}
560561
case F:
562+
for (int i = 0; i > 0; i++) {
563+
return;
564+
}
565+
case G:
566+
while (b) {
567+
return;
568+
}
569+
case H:
561570
for (; ; ) {
562571
if (false) {
563572
break;
564573
}
565574
return;
566575
}
567-
case G:
576+
case I:
568577
while (true) {
569578
if (false) {
570579
break;
@@ -578,11 +587,12 @@ void foo(Enum a) {
578587
""",
579588
"""
580589
enum Enum {
581-
A, B, C, D, E, F, G
590+
A, B, C, D, E, F, G, H, I
582591
}
583592
public class Test {
584593
void foo(Enum a) {
585594
boolean b = true;
595+
final boolean finalB = true;
586596
switch(a) {
587597
case A:
588598
for (; ; ) {
@@ -597,24 +607,32 @@ void foo(Enum a) {
597607
return;
598608
}
599609
case D:
610+
for (; finalB; ) {
611+
return;
612+
}
613+
case E:
614+
while (finalB) {
615+
return;
616+
}
617+
case F:
600618
for (int i = 0; i > 0; i++) {
601619
return;
602620
}
603621
break;
604-
case E:
622+
case G:
605623
while (b) {
606624
return;
607625
}
608626
break;
609-
case F:
627+
case H:
610628
for (; ; ) {
611629
if (false) {
612630
break;
613631
}
614632
return;
615633
}
616634
break;
617-
case G:
635+
case I:
618636
while (true) {
619637
if (false) {
620638
break;

0 commit comments

Comments
 (0)