Skip to content

Commit de70e45

Browse files
jgardn3rcirras
authored andcommitted
Extract utility method into ControlFlowGraphUtils
1 parent 90ad25a commit de70e45

File tree

3 files changed

+177
-35
lines changed

3 files changed

+177
-35
lines changed

delphi-checks/src/main/java/au/com/integradev/delphi/checks/LoopExecutingAtMostOnceCheck.java

Lines changed: 5 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,12 @@
1818
*/
1919
package au.com.integradev.delphi.checks;
2020

21-
import au.com.integradev.delphi.antlr.ast.node.AnonymousMethodNodeImpl;
22-
import au.com.integradev.delphi.antlr.ast.node.RoutineImplementationNodeImpl;
2321
import au.com.integradev.delphi.cfg.ControlFlowGraphFactory;
2422
import au.com.integradev.delphi.cfg.api.Block;
2523
import au.com.integradev.delphi.cfg.api.Branch;
2624
import au.com.integradev.delphi.cfg.api.ControlFlowGraph;
2725
import au.com.integradev.delphi.cfg.api.Terminated;
26+
import au.com.integradev.delphi.utils.ControlFlowGraphUtils;
2827
import java.util.ArrayDeque;
2928
import java.util.ArrayList;
3029
import java.util.Deque;
@@ -33,22 +32,16 @@
3332
import java.util.Optional;
3433
import java.util.Queue;
3534
import java.util.Set;
36-
import java.util.function.Supplier;
3735
import org.sonar.check.Rule;
38-
import org.sonar.plugins.communitydelphi.api.ast.CompoundStatementNode;
39-
import org.sonar.plugins.communitydelphi.api.ast.DelphiAst;
4036
import org.sonar.plugins.communitydelphi.api.ast.DelphiNode;
41-
import org.sonar.plugins.communitydelphi.api.ast.FinalizationSectionNode;
4237
import org.sonar.plugins.communitydelphi.api.ast.ForInStatementNode;
4338
import org.sonar.plugins.communitydelphi.api.ast.ForStatementNode;
4439
import org.sonar.plugins.communitydelphi.api.ast.ForToStatementNode;
4540
import org.sonar.plugins.communitydelphi.api.ast.GotoStatementNode;
4641
import org.sonar.plugins.communitydelphi.api.ast.IfStatementNode;
47-
import org.sonar.plugins.communitydelphi.api.ast.InitializationSectionNode;
4842
import org.sonar.plugins.communitydelphi.api.ast.NameReferenceNode;
4943
import org.sonar.plugins.communitydelphi.api.ast.RaiseStatementNode;
5044
import org.sonar.plugins.communitydelphi.api.ast.RepeatStatementNode;
51-
import org.sonar.plugins.communitydelphi.api.ast.StatementListNode;
5245
import org.sonar.plugins.communitydelphi.api.ast.StatementNode;
5346
import org.sonar.plugins.communitydelphi.api.ast.WhileStatementNode;
5447
import org.sonar.plugins.communitydelphi.api.check.DelphiCheck;
@@ -282,34 +275,11 @@ private static boolean isDescendant(DelphiNode descendant, DelphiNode target) {
282275
return false;
283276
}
284277

285-
private static Supplier<ControlFlowGraph> getCFGSupplier(DelphiNode node) {
286-
if (node instanceof RoutineImplementationNodeImpl) {
287-
return ((RoutineImplementationNodeImpl) node)::getControlFlowGraph;
288-
}
289-
if (node instanceof AnonymousMethodNodeImpl) {
290-
return ((AnonymousMethodNodeImpl) node)::getControlFlowGraph;
291-
}
292-
if (node instanceof CompoundStatementNode && node.getParent() instanceof DelphiAst) {
293-
return () -> ControlFlowGraphFactory.create((CompoundStatementNode) node);
294-
}
295-
if (node instanceof StatementListNode
296-
&& (node.getParent() instanceof InitializationSectionNode
297-
|| node.getParent() instanceof FinalizationSectionNode)) {
298-
return () -> ControlFlowGraphFactory.create((StatementListNode) node);
299-
}
300-
return null;
301-
}
302-
303278
private static ControlFlowGraph getCFG(DelphiNode loop) {
304-
DelphiNode parent = loop.getParent();
305-
Supplier<ControlFlowGraph> cfgSupplier = getCFGSupplier(parent);
306-
while (parent != null && cfgSupplier == null) {
307-
parent = parent.getParent();
308-
cfgSupplier = getCFGSupplier(parent);
309-
}
310-
if (cfgSupplier != null) {
311-
return cfgSupplier.get();
279+
ControlFlowGraph cfg = ControlFlowGraphUtils.findContainingCFG(loop);
280+
if (cfg == null) {
281+
return ControlFlowGraphFactory.create(loop.findChildrenOfType(StatementNode.class));
312282
}
313-
return ControlFlowGraphFactory.create(loop.findChildrenOfType(StatementNode.class));
283+
return cfg;
314284
}
315285
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Sonar Delphi Plugin
3+
* Copyright (C) 2025 Integrated Application Development
4+
*
5+
* This program is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU Lesser General Public
7+
* License as published by the Free Software Foundation; either
8+
* version 3 of the License, or (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
* Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public
16+
* License along with this program; if not, write to the Free Software
17+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
18+
*/
19+
package au.com.integradev.delphi.utils;
20+
21+
import au.com.integradev.delphi.antlr.ast.node.AnonymousMethodNodeImpl;
22+
import au.com.integradev.delphi.antlr.ast.node.RoutineImplementationNodeImpl;
23+
import au.com.integradev.delphi.cfg.ControlFlowGraphFactory;
24+
import au.com.integradev.delphi.cfg.api.ControlFlowGraph;
25+
import java.util.function.Supplier;
26+
import org.sonar.plugins.communitydelphi.api.ast.CompoundStatementNode;
27+
import org.sonar.plugins.communitydelphi.api.ast.DelphiAst;
28+
import org.sonar.plugins.communitydelphi.api.ast.DelphiNode;
29+
import org.sonar.plugins.communitydelphi.api.ast.FinalizationSectionNode;
30+
import org.sonar.plugins.communitydelphi.api.ast.InitializationSectionNode;
31+
import org.sonar.plugins.communitydelphi.api.ast.StatementListNode;
32+
33+
public final class ControlFlowGraphUtils {
34+
private ControlFlowGraphUtils() {
35+
// Utility class
36+
}
37+
38+
private static Supplier<ControlFlowGraph> getCFGSupplier(DelphiNode node) {
39+
if (node instanceof RoutineImplementationNodeImpl) {
40+
return ((RoutineImplementationNodeImpl) node)::getControlFlowGraph;
41+
}
42+
if (node instanceof AnonymousMethodNodeImpl) {
43+
return ((AnonymousMethodNodeImpl) node)::getControlFlowGraph;
44+
}
45+
if (node instanceof CompoundStatementNode && node.getParent() instanceof DelphiAst) {
46+
return () -> ControlFlowGraphFactory.create((CompoundStatementNode) node);
47+
}
48+
if (node instanceof StatementListNode
49+
&& (node.getParent() instanceof InitializationSectionNode
50+
|| node.getParent() instanceof FinalizationSectionNode)) {
51+
return () -> ControlFlowGraphFactory.create((StatementListNode) node);
52+
}
53+
return null;
54+
}
55+
56+
public static ControlFlowGraph findContainingCFG(DelphiNode node) {
57+
while (node != null) {
58+
Supplier<ControlFlowGraph> cfgSupplier = getCFGSupplier(node);
59+
if (cfgSupplier != null) {
60+
return cfgSupplier.get();
61+
}
62+
node = node.getParent();
63+
}
64+
return null;
65+
}
66+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
* Sonar Delphi Plugin
3+
* Copyright (C) 2025 Integrated Application Development
4+
*
5+
* This program is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU Lesser General Public
7+
* License as published by the Free Software Foundation; either
8+
* version 3 of the License, or (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
* Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public
16+
* License along with this program; if not, write to the Free Software
17+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
18+
*/
19+
package au.com.integradev.delphi.utils;
20+
21+
import static org.assertj.core.api.Assertions.*;
22+
23+
import au.com.integradev.delphi.builders.DelphiTestProgramBuilder;
24+
import au.com.integradev.delphi.builders.DelphiTestUnitBuilder;
25+
import au.com.integradev.delphi.cfg.api.ControlFlowGraph;
26+
import au.com.integradev.delphi.file.DelphiFile;
27+
import org.junit.jupiter.api.Test;
28+
import org.sonar.plugins.communitydelphi.api.ast.DelphiNode;
29+
import org.sonar.plugins.communitydelphi.api.ast.NameReferenceNode;
30+
31+
class ControlFlowGraphUtilsTest {
32+
private static final String NAME_TO_FIND = "ABC123";
33+
34+
private static void testFindCfg(DelphiFile unit) {
35+
DelphiNode node =
36+
unit.getAst().findDescendantsOfType(NameReferenceNode.class).stream()
37+
.filter(n -> n.getIdentifier().getImage().equals(NAME_TO_FIND))
38+
.findFirst()
39+
.orElseThrow();
40+
41+
ControlFlowGraph cfg = ControlFlowGraphUtils.findContainingCFG(node);
42+
assertThat(cfg).isNotNull();
43+
assertThat(cfg.getBlocks().stream().filter(b -> b.getElements().contains(node)).count())
44+
.isEqualTo(1);
45+
}
46+
47+
@Test
48+
void testFindCfgInRoutine() {
49+
testFindCfg(
50+
new DelphiTestUnitBuilder()
51+
.appendImpl("procedure Test;")
52+
.appendImpl("begin")
53+
.appendImpl(String.format(" %s;", NAME_TO_FIND))
54+
.appendImpl("end;")
55+
.delphiFile());
56+
}
57+
58+
@Test
59+
void testFindCfgInAnonymousRoutine() {
60+
testFindCfg(
61+
new DelphiTestUnitBuilder()
62+
.appendImpl("procedure Test;")
63+
.appendImpl("begin")
64+
.appendImpl(String.format(" %s;", NAME_TO_FIND))
65+
.appendImpl(String.format(" var Proc := procedure begin %s; end;", NAME_TO_FIND))
66+
.appendImpl(String.format(" %s;", NAME_TO_FIND))
67+
.appendImpl("end;")
68+
.delphiFile());
69+
}
70+
71+
@Test
72+
void testFindCfgInUnitBegin() {
73+
testFindCfg(
74+
new DelphiTestUnitBuilder()
75+
.appendImpl("begin")
76+
.appendImpl(String.format(" %s;", NAME_TO_FIND))
77+
.delphiFile());
78+
}
79+
80+
@Test
81+
void testFindCfgInInitialization() {
82+
testFindCfg(
83+
new DelphiTestUnitBuilder()
84+
.appendImpl("initialization")
85+
.appendImpl(String.format(" %s;", NAME_TO_FIND))
86+
.delphiFile());
87+
}
88+
89+
@Test
90+
void testFindCfgInFinalization() {
91+
testFindCfg(
92+
new DelphiTestUnitBuilder()
93+
.appendImpl("initialization")
94+
.appendImpl("finalization")
95+
.appendImpl(String.format(" %s;", NAME_TO_FIND))
96+
.delphiFile());
97+
}
98+
99+
@Test
100+
void testFindCfgInProgram() {
101+
testFindCfg(
102+
new DelphiTestProgramBuilder()
103+
.appendImpl(String.format(" %s;", NAME_TO_FIND))
104+
.delphiFile());
105+
}
106+
}

0 commit comments

Comments
 (0)