Skip to content

Commit 3819e10

Browse files
romainbrenguieralban-auzeill
authored andcommitted
SONARJAVA-5628 In CFG computation, mark semantic incomplete for unexpected break and continue
Instead of causing the rule to crash with a stack trace in the logs.
1 parent 5f1148d commit 3819e10

5 files changed

Lines changed: 19 additions & 28 deletions

File tree

java-checks/src/main/java/org/sonar/java/checks/ParameterReassignedToCheck.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public void visitMethod(MethodTree tree) {
7777

7878
@Override
7979
public void visitCatch(CatchTree tree) {
80-
CFG cfg = CFG.buildCFG(tree.block().body(), true);
80+
CFG cfg = CFG.buildCFG(tree.block().body());
8181
Symbol variable = tree.parameter().symbol();
8282
boolean liveVar = true;
8383
if(variable.owner().isMethodSymbol()) {
@@ -97,7 +97,7 @@ public void visitCatch(CatchTree tree) {
9797

9898
@Override
9999
public void visitForEachStatement(ForEachStatement tree) {
100-
CFG cfg = CFG.buildCFG(Collections.singletonList(tree), true);
100+
CFG cfg = CFG.buildCFG(Collections.singletonList(tree));
101101
Symbol variable = tree.variable().symbol();
102102
boolean liveVar = true;
103103
if(variable.owner().isMethodSymbol()) {

java-checks/src/main/java/org/sonar/java/checks/SwitchCaseWithoutBreakCheck.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public List<Tree.Kind> nodesToVisit() {
4747
public void visitNode(Tree tree) {
4848
SwitchStatementTree switchStatementTree = (SwitchStatementTree) tree;
4949
Set<CaseGroupTree> caseGroupTrees = new HashSet<>(switchStatementTree.cases());
50-
CFG cfg = CFG.buildCFG(Collections.singletonList(tree), true);
50+
CFG cfg = CFG.buildCFG(Collections.singletonList(tree));
5151
Set<CFG.Block> switchSuccessors = cfg.entryBlock().successors();
5252

5353
Map<CFG.Block, CaseGroupTree> cfgBlockToCaseGroupMap = createMapping(switchSuccessors, caseGroupTrees);

java-checks/src/main/java/org/sonar/java/checks/TryWithResourcesCheck.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ private static boolean isFollowedByTryWithFinally(Tree tree) {
8888
}
8989

9090
if (blockParent != null) {
91-
CFG cfg = CFG.buildCFG(Collections.singletonList(blockParent), true);
91+
CFG cfg = CFG.buildCFG(Collections.singletonList(blockParent));
9292
if (!cfg.blocks().isEmpty()) {
9393
return newFollowedByTryStatement(cfg.blocks().get(0));
9494
}

java-frontend/src/main/java/org/sonar/java/cfg/CFG.java

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@ public class CFG implements ControlFlowGraph {
9797

9898
private static final Logger LOG = LoggerFactory.getLogger(CFG.class);
9999

100-
private final boolean ignoreBreakAndContinue;
101100
@Nullable
102101
private Symbol.MethodSymbol methodSymbol;
103102
private Block currentBlock;
@@ -135,12 +134,11 @@ public void addCatch(Type type, Block catchBlock) {
135134
private Map<String, Block> labelsBreakTarget = new HashMap<>();
136135
private Map<String, Block> labelsContinueTarget = new HashMap<>();
137136

138-
private CFG(List<? extends Tree> trees, @Nullable MethodTree tree, boolean ignoreBreakAndContinue) {
137+
private CFG(List<? extends Tree> trees, @Nullable MethodTree tree) {
139138
if (tree != null) {
140139
methodSymbol = tree.symbol();
141140
checkSymbolSemantic(methodSymbol, "method definition", tree.simpleName().identifierToken());
142141
}
143-
this.ignoreBreakAndContinue = ignoreBreakAndContinue;
144142
exitBlocks.add(createBlock());
145143
currentBlock = createBlock(exitBlock());
146144
outerTry = new TryStatement();
@@ -493,18 +491,14 @@ private Block createBlock() {
493491
return result;
494492
}
495493

496-
public static CFG buildCFG(List<? extends Tree> trees, boolean ignoreBreak) {
497-
return new CFG(trees, null, ignoreBreak);
498-
}
499-
500494
public static CFG buildCFG(List<? extends Tree> trees) {
501-
return new CFG(trees, null, false);
495+
return new CFG(trees, null);
502496
}
503497

504498
public static CFG build(MethodTree tree) {
505499
BlockTree block = tree.block();
506500
Preconditions.checkArgument(block != null, "Cannot build CFG for method with no body.");
507-
return new CFG(block.body(), tree, false);
501+
return new CFG(block.body(), tree);
508502
}
509503

510504
private void build(ListTree<? extends Tree> trees) {
@@ -922,9 +916,7 @@ private void buildBreakStatement(BreakStatementTree tree) {
922916
Block targetBlock = null;
923917
if (label == null) {
924918
if (breakTargets.isEmpty()) {
925-
if (!ignoreBreakAndContinue) {
926-
throw new IllegalStateException("'break' statement not in loop or switch statement");
927-
}
919+
markSemanticAsIncomplete("'break' statement not in loop or switch statement", tree.firstToken());
928920
} else {
929921
targetBlock = breakTargets.getLast();
930922
}
@@ -953,9 +945,7 @@ private void buildContinueStatement(ContinueStatementTree tree) {
953945
Block targetBlock = null;
954946
if (label == null) {
955947
if (continueTargets.isEmpty()) {
956-
if (!ignoreBreakAndContinue) {
957-
throw new IllegalStateException("'continue' statement not in loop or switch statement");
958-
}
948+
markSemanticAsIncomplete("'continue' statement not in loop or switch statement", tree.firstToken());
959949
} else {
960950
targetBlock = continueTargets.getLast();
961951
}

java-frontend/src/test/java/org/sonar/java/cfg/CFGTest.java

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@
4242
import org.sonar.plugins.java.api.tree.VariableTree;
4343

4444
import static org.assertj.core.api.Assertions.assertThat;
45-
import static org.assertj.core.api.Assertions.fail;
4645
import static org.sonar.java.cfg.CFGTestUtils.buildCFG;
4746
import static org.sonar.java.cfg.CFGTestUtils.buildCFGFromLambda;
4847
import static org.sonar.plugins.java.api.tree.Tree.Kind.ARRAY_ACCESS_EXPRESSION;
@@ -3278,6 +3277,9 @@ void test_semantic_completeness() {
32783277

32793278
assertCompleteSemantic("void foo() { Runnable ignoredLambdaContent = () -> { Unknown x; }; }", true);
32803279
assertCompleteSemantic("void foo() { class IgnoredNestedClass { void foo() { Unknown x; } } }", true);
3280+
3281+
assertCompleteSemantic("void foo(int b) { break; }", false, "Incomplete Semantic, 'break' statement not in loop or switch statement 'break' line 1 col 29");
3282+
assertCompleteSemantic("void foo(int b) { continue; }", false, "Incomplete Semantic, 'continue' statement not in loop or switch statement 'continue' line 1 col 29");
32813283
}
32823284

32833285

@@ -3300,16 +3302,15 @@ private void build_partial_cfg(String breakOrContinue) {
33003302
CompilationUnitTree cut = JParserTestUtils.parse("class A {" + methodCode + "}");
33013303
MethodTree methodTree = (MethodTree) ((ClassTree) cut.types().get(0)).members().get(0);
33023304
List<StatementTree> body = methodTree.block().body();
3303-
CFG cfg = CFG.buildCFG(body, true);
3305+
3306+
logTester.setLevel(Level.DEBUG);
3307+
logTester.clear();
3308+
CFG cfg = CFG.buildCFG(body);
33043309
cfg.setMethodSymbol(methodTree.symbol());
33053310
assertThat(cfg.blocks()).hasSize(5);
33063311
assertThat(cfg.methodSymbol()).isSameAs(methodTree.symbol());
3307-
3308-
try {
3309-
CFG.buildCFG(body, false);
3310-
fail("IllegalStateException should have been thrown");
3311-
} catch (IllegalStateException iae) {
3312-
assertThat(iae).hasMessage("'"+breakOrContinue+"' statement not in loop or switch statement");
3313-
}
3312+
assertThat(cfg.hasCompleteSemantic()).isFalse();
3313+
assertThat(logTester.logs(Level.DEBUG))
3314+
.containsExactly("Incomplete Semantic, '" + breakOrContinue + "' statement not in loop or switch statement '" + breakOrContinue + "' " + "line 1 col 80");
33143315
}
33153316
}

0 commit comments

Comments
 (0)