diff --git a/java-checks-test-sources/default/src/main/files/non-compiling/checks/RedundantJumpCheck.java b/java-checks-test-sources/default/src/main/files/non-compiling/checks/RedundantJumpCheck.java index 11c3a11fe9f..e8d1c40dbd8 100644 --- a/java-checks-test-sources/default/src/main/files/non-compiling/checks/RedundantJumpCheck.java +++ b/java-checks-test-sources/default/src/main/files/non-compiling/checks/RedundantJumpCheck.java @@ -1,3 +1,7 @@ +import java.util.function.IntSupplier; +import java.util.function.IntUnaryOperator; +import java.util.function.Supplier; + abstract class A { public void reproducer() { @@ -329,4 +333,115 @@ void continueInTryFinallyOutsideLoop() { foo(); } } + + Runnable emptyLambda = () -> {}; + + Runnable expressionLambda = () -> System.out.println("expression"); + + Supplier expressionReturningLambda = () -> () -> { + return; // Noncompliant {{Remove this redundant return.}} +// ^^^^^^^ + }; + + Runnable fieldInitializer = () -> { + System.out.println("field"); + return; // Noncompliant {{Remove this redundant return.}} +// ^^^^^^^ + }; + + void immediatelyInvokedLambda(int x) { + Runnable r = () -> { + System.out.println(x); + return; // Noncompliant {{Remove this redundant return.}} +// ^^^^^^^ + }; + r.run(); + System.out.println("after lambda"); + } + + Runnable conditionalReturns(int x) { + return () -> { + if (x < 0) { + return; + } + System.out.println(x); + if (x == 0) { + return; // Noncompliant {{Remove this redundant return.}} +// ^^^^^^^ + } + }; + } + + IntSupplier valueReturningLambda(int x) { + return () -> { + System.out.println(x); + return x; + }; + } + + Runnable nestedLambdas(int x) { + return () -> { + Runnable inner = () -> { + System.out.println(x); + return; // Noncompliant {{Remove this redundant return.}} +// ^^^^^^^ + }; + inner.run(); + System.out.println("after inner lambda"); + }; + } + + Runnable loopJumps(int x) { + return () -> { + for (int i = 0; i < x; i++) { + if (i == 1) { + continue; + } + if (i == 2) { + return; + } + System.out.println(i); + continue; // Noncompliant {{Remove this redundant continue.}} +// ^^^^^^^^^ + } + }; + } + + Runnable returnThroughFinally(int x) { + return () -> { + try { + System.out.println(x); + return; // Noncompliant {{Remove this redundant return.}} +// ^^^^^^^ + } finally { + System.out.println("cleanup"); + } + }; + } + + Runnable earlyReturnThroughFinally(int x) { + return () -> { + try { + if (x < 0) { + return; + } + } finally { + System.out.println("cleanup"); + } + System.out.println(x); + }; + } + + IntUnaryOperator continueInSwitchExpressionLambda() { + return x -> switch (x) { + default -> { + for (int i = 0; i < x; i++) { + System.out.println(i); + continue; // Noncompliant {{Remove this redundant continue.}} +// ^^^^^^^^^ + } + yield x; + } + }; + } } diff --git a/java-checks/src/main/java/org/sonar/java/checks/RedundantJumpCheck.java b/java-checks/src/main/java/org/sonar/java/checks/RedundantJumpCheck.java index 46a7aaea4e7..103982f96fc 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/RedundantJumpCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/RedundantJumpCheck.java @@ -24,8 +24,10 @@ import org.sonar.java.cfg.CFG.Block; import org.sonar.java.cfg.CFGUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; +import org.sonar.plugins.java.api.tree.LambdaExpressionTree; import org.sonar.plugins.java.api.tree.MethodTree; import org.sonar.plugins.java.api.tree.ReturnStatementTree; +import org.sonar.plugins.java.api.tree.SyntaxToken; import org.sonar.plugins.java.api.tree.Tree; @Rule(key = "S3626") @@ -35,14 +37,22 @@ public class RedundantJumpCheck extends IssuableSubscriptionVisitor { public List nodesToVisit() { return Arrays.asList( Tree.Kind.METHOD, - Tree.Kind.CONSTRUCTOR); + Tree.Kind.CONSTRUCTOR, + Tree.Kind.LAMBDA_EXPRESSION); } @Override public void visitNode(Tree tree) { - MethodTree methodTree = (MethodTree) tree; - if (methodTree.block() != null) { - CFG cfg = (CFG) methodTree.cfg(); + CFG cfg = null; + if (tree instanceof MethodTree methodTree) { + if (methodTree.block() != null) { + cfg = (CFG) methodTree.cfg(); + } + } else { + // Expression-bodied lambdas can contain redundant continue statements in loops inside switch expressions. + cfg = (CFG) ((LambdaExpressionTree) tree).cfg(); + } + if (cfg != null) { cfg.blocks().forEach(this::checkBlock); } } @@ -64,7 +74,9 @@ && hasValidContinueTarget(terminator)) { Block successor = CFGUtils.nonEmptySuccessor(successors.next()); if (successor.equals(successorWithoutJump) && !CFGUtils.isJumpThroughFinallyWithDistinctContinuation(terminator, successor)) { - reportIssue(terminator, "Remove this redundant jump."); + SyntaxToken fstToken = terminator.firstToken(); + String jump = fstToken != null ? fstToken.text() : "jump"; + reportIssue(terminator, "Remove this redundant %s.".formatted(jump)); } } }