mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-31 06:52:49 +00:00
test(r): pin the CFG edge structure and CDG labels for empty if arms
Review finding: an empty then/else arm loses its cond-true/cond-false edge.
Not reproducible as lost information: an empty `{}` arm (like a missing
`else`) is the header's seq fall-through edge, the same convention as every
other CFG visitor, and control dependence labels it as the complement of the
sibling explicit arm. Probed on the current code: `if (c) {} else work()`
gives c-cond-false->work() with CDG c->work():F, and
`if (c) work() else {}` gives CDG c->work():T.
Adds exact edge-structure and CDG-label tests (all passed on the unmodified
visitor) and documents the fall-through convention in the visitor header.
No behaviour change.
This commit is contained in:
parent
3b8682f23e
commit
9c39aefdb6
2 changed files with 61 additions and 0 deletions
|
|
@ -21,6 +21,10 @@
|
|||
* - break / next / return(…) -> `break` / `continue` / `return`
|
||||
* - straight-line -> `seq`
|
||||
*
|
||||
* A fall-through arm (no `else`, or an empty `{}` arm) produces no block and is the
|
||||
* header's `seq` edge to the join; control dependence labels it as the complement of
|
||||
* the sibling arm's explicit `cond-true` / `cond-false`, so no branch sense is lost.
|
||||
*
|
||||
* Every loop (including `repeat` and `while (TRUE)`) gets the structural
|
||||
* `header -> loopExit` `cond-false` edge, so EXIT stays reverse-reachable and
|
||||
* the post-dominator / control-dependence pass is never skipped for a function
|
||||
|
|
|
|||
|
|
@ -76,6 +76,63 @@ describe('R CfgVisitor — branches', () => {
|
|||
]);
|
||||
});
|
||||
|
||||
// An empty `{}` arm produces no block, so that arm is the fall-through edge from the
|
||||
// header (`seq`), exactly like a missing `else`. The sibling arm keeps its explicit
|
||||
// cond-true / cond-false kind and control dependence reads the fall-through arm as
|
||||
// its complement, so no branch sense is lost (the same convention as every visitor).
|
||||
it('empty then with a real else: cond-false arm plus a seq fall-through, CDG label F', () => {
|
||||
const cfg = r.cfgOf(`f <- function(c) {\n if (c) {} else work()\n after()\n}\n`);
|
||||
expect(shape(cfg)).toEqual([
|
||||
'ENTRY -seq-> c',
|
||||
'after() -seq-> EXIT',
|
||||
'c -cond-false-> work()',
|
||||
'c -seq-> after()',
|
||||
'work() -seq-> after()',
|
||||
]);
|
||||
expect(computeControlDependence(cfg).edges).toEqual([
|
||||
{ controllerBlock: block(cfg, 'c'), dependentBlock: block(cfg, 'work()'), label: 'F' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('real then with an empty else: cond-true arm plus a seq fall-through, CDG label T', () => {
|
||||
const cfg = r.cfgOf(`f <- function(c) {\n if (c) work() else {}\n after()\n}\n`);
|
||||
expect(shape(cfg)).toEqual([
|
||||
'ENTRY -seq-> c',
|
||||
'after() -seq-> EXIT',
|
||||
'c -cond-true-> work()',
|
||||
'c -seq-> after()',
|
||||
'work() -seq-> after()',
|
||||
]);
|
||||
expect(computeControlDependence(cfg).edges).toEqual([
|
||||
{ controllerBlock: block(cfg, 'c'), dependentBlock: block(cfg, 'work()'), label: 'T' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('both arms empty: a single seq edge and no control dependence (nothing differs)', () => {
|
||||
for (const body of ['if (c) {} else {}', 'if (c) {}']) {
|
||||
const cfg = r.cfgOf(`f <- function(c) {\n ${body}\n after()\n}\n`);
|
||||
expect(shape(cfg)).toEqual(['ENTRY -seq-> c', 'after() -seq-> EXIT', 'c -seq-> after()']);
|
||||
expect(computeControlDependence(cfg).edges).toEqual([]);
|
||||
}
|
||||
});
|
||||
|
||||
it('empty then followed by an else-if: the chain keeps its own explicit arms', () => {
|
||||
const cfg = r.cfgOf(`f <- function(p, q) {\n if (p) {} else if (q) a()\n after()\n}\n`);
|
||||
expect(shape(cfg)).toEqual([
|
||||
'ENTRY -seq-> p',
|
||||
'a() -seq-> after()',
|
||||
'after() -seq-> EXIT',
|
||||
'p -cond-false-> q',
|
||||
'p -seq-> after()',
|
||||
'q -cond-true-> a()',
|
||||
'q -seq-> after()',
|
||||
]);
|
||||
expect(computeControlDependence(cfg).edges).toEqual([
|
||||
{ controllerBlock: block(cfg, 'p'), dependentBlock: block(cfg, 'q'), label: 'F' },
|
||||
{ controllerBlock: block(cfg, 'q'), dependentBlock: block(cfg, 'a()'), label: 'T' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('else-if chain nests through the alternative', () => {
|
||||
const cfg = r.cfgOf(`f <- function(p, q) {\n if (p) a() else if (q) b() else c()\n}\n`);
|
||||
expect(shape(cfg)).toEqual([
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue