Skip to content

Commit e4d896c

Browse files
authored
Exception edges should not count when determining exit nodes (#177)
Changes the criteria for exit nodes to either have outdegree==0, or only have exception edges. Closes #175.
1 parent facdaeb commit e4d896c

File tree

6 files changed

+248
-6
lines changed

6 files changed

+248
-6
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how
2121

2222
- Multiple `catch` clauses in C++ are now handled properly, even in case of comments between them.
2323
- Segmentation of `catch` clauses in C++ is now a lot better.
24-
- Fixed an issue where after sharing and reopening the page, clicking the "Share" button again would default the language back to Go instead of preserving the selected one.
24+
- Fixed an issue where after sharing and reopening the page,
25+
clicking the "Share" button again would default the language back to Go instead of preserving the selected one.
26+
- Exit nodes are now drawn properly even inside `try` blocks.
2527

2628
### Changed
2729

src/control-flow/render.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,20 @@ function renderEdge(
294294
return `${source} -> ${target} [${formatStyle(dotAttrs)}];`;
295295
}
296296

297+
export function isExit(graph: CFGGraph, node: string): boolean {
298+
if (graph.outDegree(node) === 0) {
299+
return true;
300+
}
301+
// Exception nodes are invisible and "out-of-band", so they don't affect the
302+
// definition of exits.
303+
for (const { attributes } of graph.outEdgeEntries(node)) {
304+
if (attributes.type !== "exception") {
305+
return false;
306+
}
307+
}
308+
return true;
309+
}
310+
297311
function renderNode(
298312
graph: CFGGraph,
299313
node: string,
@@ -313,7 +327,7 @@ function renderNode(
313327
nodeClass = "default";
314328
} else if (graph.inDegree(node) === 0) {
315329
nodeClass = "entry";
316-
} else if (graph.outDegree(node) === 0) {
330+
} else if (isExit(graph, node)) {
317331
nodeClass = "exit";
318332
}
319333
const dotAttrs = getNodeStyle(nodeClass, context.colorScheme);

src/test/__snapshots__/commentTest.test.ts.snap

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10999,6 +10999,218 @@ Lookup {
1099910999
}
1100011000
`;
1100111001

11002+
exports[`Python: issue-175-return-from-try.py!lookup_type: DOT Snapshot 1`] = `
11003+
"digraph "" {
11004+
node [label=""; style="filled"; shape="box"; class="default"; fillcolor="#d3d3d3"; color="#000000"];
11005+
edge [penwidth=1; color="#0000ff"; headport="n"; tailport="s"];
11006+
bgcolor="#ffffff";
11007+
11008+
subgraph cluster_0 {
11009+
penwidth=0; class="tryComplex"; color="#ffffff"; bgcolor="#ddddff"
11010+
subgraph cluster_1 {
11011+
penwidth=0; class="try"; color="#ffffff"; bgcolor="#ddffdd"
11012+
node2 [shape="house"; class="exit"; fillcolor="#AB3030"; color="#000000"; height=0.5; id="node2"];
11013+
}
11014+
subgraph cluster_2 {
11015+
penwidth=0; class="except"; color="#ffffff"; bgcolor="#ffdddd"
11016+
node3 [label=""; style="filled"; shape="box"; class="default"; fillcolor="#d3d3d3"; color="#000000"; height=0.3; id="node3"];
11017+
}
11018+
}
11019+
subgraph cluster_4 {
11020+
penwidth=0; class="tryComplex"; color="#ffffff"; bgcolor="#ddddff"
11021+
subgraph cluster_5 {
11022+
penwidth=0; class="try"; color="#ffffff"; bgcolor="#ddffdd"
11023+
node5 [shape="house"; class="exit"; fillcolor="#AB3030"; color="#000000"; height=0.5; id="node5"];
11024+
}
11025+
subgraph cluster_6 {
11026+
penwidth=0; class="except"; color="#ffffff"; bgcolor="#ffdddd"
11027+
node6 [label=""; style="filled"; shape="box"; class="default"; fillcolor="#d3d3d3"; color="#000000"; height=0.3; id="node6"];
11028+
}
11029+
}
11030+
subgraph cluster_8 {
11031+
penwidth=0; class="tryComplex"; color="#ffffff"; bgcolor="#ddddff"
11032+
subgraph cluster_9 {
11033+
penwidth=0; class="try"; color="#ffffff"; bgcolor="#ddffdd"
11034+
node8 [shape="house"; class="exit"; fillcolor="#AB3030"; color="#000000"; height=0.5; id="node8"];
11035+
}
11036+
subgraph cluster_10 {
11037+
penwidth=0; class="except"; color="#ffffff"; bgcolor="#ffdddd"
11038+
node9 [label=""; style="filled"; shape="box"; class="default"; fillcolor="#d3d3d3"; color="#000000"; height=0.3; id="node9"];
11039+
}
11040+
}
11041+
node0 [shape="invhouse"; class="entry"; fillcolor="#48AB30"; color="#000000"; height=0.5; id="node0"];
11042+
node2 [shape="house"; class="exit"; fillcolor="#AB3030"; color="#000000"; height=0.5; id="node2"];
11043+
node3 [label=""; style="filled"; shape="box"; class="default"; fillcolor="#d3d3d3"; color="#000000"; height=0.3; id="node3"];
11044+
node1 [label=""; style="filled"; shape="box"; class="default"; fillcolor="#d3d3d3"; color="#000000"; height=0.3; id="node1"];
11045+
node5 [shape="house"; class="exit"; fillcolor="#AB3030"; color="#000000"; height=0.5; id="node5"];
11046+
node6 [label=""; style="filled"; shape="box"; class="default"; fillcolor="#d3d3d3"; color="#000000"; height=0.3; id="node6"];
11047+
node4 [label=""; style="filled"; shape="box"; class="default"; fillcolor="#d3d3d3"; color="#000000"; height=0.3; id="node4"];
11048+
node8 [shape="house"; class="exit"; fillcolor="#AB3030"; color="#000000"; height=0.5; id="node8"];
11049+
node9 [label=""; style="filled"; shape="box"; class="default"; fillcolor="#d3d3d3"; color="#000000"; height=0.3; id="node9"];
11050+
node10 [shape="house"; class="exit"; fillcolor="#AB3030"; color="#000000"; height=0.6; id="node10"];
11051+
node2 -> node3 [style="invis"; headport="e"; tailport="w"];
11052+
node3 -> node1 [class="regular"; color="#0000ff"];
11053+
node5 -> node6 [style="invis"; headport="e"; tailport="w"];
11054+
node6 -> node4 [class="regular"; color="#0000ff"];
11055+
node8 -> node9 [style="invis"; headport="e"; tailport="w"];
11056+
node1 -> node5 [class="regular"; color="#0000ff"];
11057+
node4 -> node8 [class="regular"; color="#0000ff"];
11058+
node0 -> node2 [class="regular"; color="#0000ff"];
11059+
node9 -> node10 [class="regular"; color="#0000ff"];
11060+
}"
11061+
`;
11062+
11063+
exports[`Python: issue-175-return-from-try.py!lookup_type: Segmentation 1`] = `
11064+
Lookup {
11065+
"ranges": [
11066+
{
11067+
"start": 0,
11068+
"value": "node0",
11069+
},
11070+
{
11071+
"start": 11,
11072+
"value": "node0",
11073+
},
11074+
{
11075+
"start": 35,
11076+
"value": "node2",
11077+
},
11078+
{
11079+
"start": 42,
11080+
"value": "node2",
11081+
},
11082+
{
11083+
"start": 70,
11084+
"value": "node2",
11085+
},
11086+
{
11087+
"start": 70,
11088+
"value": "node2",
11089+
},
11090+
{
11091+
"start": 72,
11092+
"value": "node3",
11093+
},
11094+
{
11095+
"start": 92,
11096+
"value": "node3",
11097+
},
11098+
{
11099+
"start": 96,
11100+
"value": "node3",
11101+
},
11102+
{
11103+
"start": 96,
11104+
"value": "node3",
11105+
},
11106+
{
11107+
"start": 96,
11108+
"value": "node2",
11109+
},
11110+
{
11111+
"start": 96,
11112+
"value": "node5",
11113+
},
11114+
{
11115+
"start": 98,
11116+
"value": "node0",
11117+
},
11118+
{
11119+
"start": 98,
11120+
"value": "node5",
11121+
},
11122+
{
11123+
"start": 105,
11124+
"value": "node5",
11125+
},
11126+
{
11127+
"start": 145,
11128+
"value": "node5",
11129+
},
11130+
{
11131+
"start": 145,
11132+
"value": "node5",
11133+
},
11134+
{
11135+
"start": 147,
11136+
"value": "node6",
11137+
},
11138+
{
11139+
"start": 167,
11140+
"value": "node6",
11141+
},
11142+
{
11143+
"start": 171,
11144+
"value": "node6",
11145+
},
11146+
{
11147+
"start": 171,
11148+
"value": "node6",
11149+
},
11150+
{
11151+
"start": 171,
11152+
"value": "node5",
11153+
},
11154+
{
11155+
"start": 171,
11156+
"value": "node8",
11157+
},
11158+
{
11159+
"start": 173,
11160+
"value": "node0",
11161+
},
11162+
{
11163+
"start": 173,
11164+
"value": "node8",
11165+
},
11166+
{
11167+
"start": 180,
11168+
"value": "node8",
11169+
},
11170+
{
11171+
"start": 234,
11172+
"value": "node8",
11173+
},
11174+
{
11175+
"start": 234,
11176+
"value": "node8",
11177+
},
11178+
{
11179+
"start": 236,
11180+
"value": "node9",
11181+
},
11182+
{
11183+
"start": 256,
11184+
"value": "node9",
11185+
},
11186+
{
11187+
"start": 260,
11188+
"value": "node9",
11189+
},
11190+
{
11191+
"start": 260,
11192+
"value": "node9",
11193+
},
11194+
{
11195+
"start": 260,
11196+
"value": "node8",
11197+
},
11198+
{
11199+
"start": 260,
11200+
"value": "node0",
11201+
},
11202+
{
11203+
"start": 260,
11204+
"value": "node8",
11205+
},
11206+
{
11207+
"start": 260,
11208+
"value": "node0",
11209+
},
11210+
],
11211+
}
11212+
`;
11213+
1100211214
exports[`Python: sample.py!For: DOT Snapshot 1`] = `
1100311215
"digraph "" {
1100411216
node [label=""; style="filled"; shape="box"; class="default"; fillcolor="#d3d3d3"; color="#000000"];

src/test/commentTestHandlers.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
mergeNodeAttrs,
1212
} from "../control-flow/cfg-defs";
1313
import { simplifyCFG, trimFor } from "../control-flow/graph-ops";
14-
import { graphToDot } from "../control-flow/render";
14+
import { graphToDot, isExit } from "../control-flow/render";
1515
import type { Requirements, TestFunction } from "./commentTestTypes";
1616

1717
const markerPattern: RegExp = /CFG: (\w+)/;
@@ -111,8 +111,8 @@ export const requirementTests: Record<keyof Requirements, RequirementHandler> =
111111
exits(testFunc: TestFunction) {
112112
if (testFunc.reqs.exits !== undefined) {
113113
const cfg = buildSimpleCFG(testFunc.language, testFunc.function);
114-
const exitNodes = cfg.graph.filterNodes(
115-
(node) => cfg.graph.outDegree(node) === 0,
114+
const exitNodes = cfg.graph.filterNodes((node) =>
115+
isExit(cfg.graph, node),
116116
);
117117
if (exitNodes.length !== testFunc.reqs.exits) {
118118
return `expected ${testFunc.reqs.exits} exits but found ${exitNodes.length}`;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# exits: 4
2+
def lookup_type(name):
3+
try:
4+
return gdb.lookup_type(name)
5+
except gdb.error:
6+
pass
7+
try:
8+
return gdb.lookup_type('struct ' + name)
9+
except gdb.error:
10+
pass
11+
try:
12+
return gdb.lookup_type('struct ' + name[1:]).pointer()
13+
except gdb.error:
14+
pass

src/test/commentTestSamples/sample.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ def raise_exception():
163163
raise
164164

165165
# render: true,
166-
# exits: 1,
166+
# exits: 2,
167167
# nodes: 6
168168
def raise_again():
169169
try:

0 commit comments

Comments
 (0)