Skip to content

Commit 8a35813

Browse files
committed
C#: Unify goto completions
1 parent 0290c79 commit 8a35813

File tree

12 files changed

+111
-203
lines changed

12 files changed

+111
-203
lines changed

csharp/ql/src/semmle/code/csharp/Stmt.qll

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -669,7 +669,10 @@ class ContinueStmt extends JumpStmt, @continue_stmt {
669669
* Either a `goto` label (`GotoLabelStmt`), a `goto case` (`GotoCaseStmt`), or
670670
* a `goto default` (`GotoDefaultStmt`).
671671
*/
672-
class GotoStmt extends JumpStmt, @goto_any_stmt { }
672+
class GotoStmt extends JumpStmt, @goto_any_stmt {
673+
/** Gets the label that this `goto` statement jumps to. */
674+
string getLabel() { none() }
675+
}
673676

674677
/**
675678
* A `goto` statement that jumps to a labeled statement, for example line 4 in
@@ -684,8 +687,7 @@ class GotoStmt extends JumpStmt, @goto_any_stmt { }
684687
* ```
685688
*/
686689
class GotoLabelStmt extends GotoStmt, @goto_stmt {
687-
/** Gets the label that this `goto` statement jumps to. */
688-
string getLabel() { exprorstmt_name(this, result) }
690+
override string getLabel() { exprorstmt_name(this, result) }
689691

690692
override string toString() { result = "goto ...;" }
691693

@@ -716,8 +718,7 @@ class GotoCaseStmt extends GotoStmt, @goto_case_stmt {
716718
/** Gets the constant expression that this `goto case` statement jumps to. */
717719
Expr getExpr() { result = this.getChild(0) }
718720

719-
/** Gets the label that this `goto case` statement jumps to. */
720-
string getLabel() { result = getExpr().getValue() }
721+
override string getLabel() { result = getExpr().getValue() }
721722

722723
override string toString() { result = "goto case ...;" }
723724
}
@@ -740,6 +741,8 @@ class GotoCaseStmt extends GotoStmt, @goto_case_stmt {
740741
*/
741742
class GotoDefaultStmt extends GotoStmt, @goto_default_stmt {
742743
override string toString() { result = "goto default;" }
744+
745+
override string getLabel() { result = "default" }
743746
}
744747

745748
/**

csharp/ql/src/semmle/code/csharp/controlflow/ControlFlowGraph.qll

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1087,6 +1087,12 @@ module ControlFlow {
10871087
)
10881088
}
10891089

1090+
pragma[noinline]
1091+
private LabeledStmt getLabledStmt(string label, Callable c) {
1092+
result.getEnclosingCallable() = c and
1093+
label = result.getLabel()
1094+
}
1095+
10901096
/**
10911097
* Gets a potential last element executed within control flow element `cfe`,
10921098
* as well as its completion.
@@ -1148,8 +1154,8 @@ module ControlFlow {
11481154
rec = TLastRecSwitchAbnormalCompletion() and
11491155
not c instanceof BreakCompletion and
11501156
not c instanceof NormalCompletion and
1151-
not c instanceof GotoDefaultCompletion and
1152-
not c instanceof GotoCaseCompletion and
1157+
not getLabledStmt(c.(GotoCompletion).getLabel(), cfe.getEnclosingCallable()) instanceof
1158+
CaseStmt and
11531159
c = c0
11541160
or
11551161
rec = TLastRecInvalidOperationException() and
@@ -1515,24 +1521,6 @@ module ControlFlow {
15151521
c instanceof NormalCompletion and
15161522
result = first(ss.getStmt(i + 1))
15171523
)
1518-
or
1519-
exists(GotoCompletion gc |
1520-
cfe = last(ss.getAStmt(), gc) and
1521-
gc = c
1522-
|
1523-
// Flow from last element of a statement with a `goto default` completion
1524-
// to first element `default` statement
1525-
gc instanceof GotoDefaultCompletion and
1526-
result = first(ss.getDefaultCase())
1527-
or
1528-
// Flow from last element of a statement with a `goto case` completion
1529-
// to first element of relevant case
1530-
exists(ConstCase cc |
1531-
cc = ss.getAConstCase() and
1532-
cc.getLabel() = gc.(GotoCaseCompletion).getLabel() and
1533-
result = first(cc.getBody())
1534-
)
1535-
)
15361524
)
15371525
or
15381526
exists(Case case |
@@ -1766,13 +1754,13 @@ module ControlFlow {
17661754
or
17671755
// Flow from element with `goto` completion to first element of relevant
17681756
// target
1769-
c = any(GotoLabelCompletion glc |
1770-
cfe = last(_, glc) and
1757+
c = any(GotoCompletion gc |
1758+
cfe = last(_, gc) and
17711759
// Special case: when a `goto` happens inside a `try` statement with a
17721760
// `finally` block, flow does not go directly to the target, but instead
17731761
// to the `finally` block (and from there possibly to the target)
17741762
not cfe = getBlockOrCatchFinallyPred(any(TryStmt ts | ts.hasFinally()), _) and
1775-
result = first(glc.getGotoStmt().getTarget())
1763+
result = first(getLabledStmt(gc.getLabel(), cfe.getEnclosingCallable()))
17761764
)
17771765
or
17781766
// Standard left-to-right evaluation

csharp/ql/src/semmle/code/csharp/controlflow/internal/Completion.qll

Lines changed: 19 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,7 @@ private newtype TCompletion =
3535
TBreakCompletion() or
3636
TBreakNormalCompletion() or
3737
TContinueCompletion() or
38-
TGotoLabelCompletion(GotoLabelStmt goto) or
39-
TGotoCaseCompletion(GotoCaseStmt goto) or
40-
TGotoDefaultCompletion() or
38+
TGotoCompletion(string label) { label = any(GotoStmt gs).getLabel() } or
4139
TThrowCompletion(ExceptionClass ec) or
4240
TExitCompletion() or
4341
TNestedCompletion(NormalCompletion inner, Completion outer) {
@@ -47,11 +45,7 @@ private newtype TCompletion =
4745
or
4846
outer = TContinueCompletion()
4947
or
50-
outer = TGotoLabelCompletion(_)
51-
or
52-
outer = TGotoCaseCompletion(_)
53-
or
54-
outer = TGotoDefaultCompletion()
48+
outer = TGotoCompletion(_)
5549
or
5650
outer = TThrowCompletion(_)
5751
or
@@ -122,22 +116,17 @@ class Completion extends TCompletion {
122116
if cfe instanceof ContinueStmt
123117
then this = TContinueCompletion()
124118
else
125-
if cfe instanceof GotoDefaultStmt
126-
then this = TGotoDefaultCompletion()
119+
if cfe instanceof GotoStmt
120+
then this = TGotoCompletion(cfe.(GotoStmt).getLabel())
127121
else
128-
if cfe instanceof GotoStmt
129-
then
130-
this = TGotoLabelCompletion(cfe) or
131-
this = TGotoCaseCompletion(cfe)
122+
if cfe instanceof ReturnStmt
123+
then this = TReturnCompletion()
132124
else
133-
if cfe instanceof ReturnStmt
134-
then this = TReturnCompletion()
135-
else
136-
if cfe instanceof YieldBreakStmt
137-
then
138-
// `yield break` behaves like a return statement
139-
this = TReturnCompletion()
140-
else this = TNormalCompletion()
125+
if cfe instanceof YieldBreakStmt
126+
then
127+
// `yield break` behaves like a return statement
128+
this = TReturnCompletion()
129+
else this = TNormalCompletion()
141130
)
142131
}
143132

@@ -726,69 +715,20 @@ class ContinueCompletion extends Completion {
726715
* A completion that represents evaluation of a statement or an
727716
* expression resulting in a `goto` jump.
728717
*/
729-
abstract class GotoCompletion extends Completion { }
730-
731-
/**
732-
* A completion that represents evaluation of a statement or an
733-
* expression resulting in a `goto label` jump.
734-
*/
735-
class GotoLabelCompletion extends GotoCompletion {
736-
private GotoLabelStmt goto;
737-
738-
GotoLabelCompletion() {
739-
this = TGotoLabelCompletion(goto) or
740-
this = TNestedCompletion(_, TGotoLabelCompletion(goto))
741-
}
742-
743-
/** Gets the target of the `goto label` completion. */
744-
string getLabel() { result = this.getGotoStmt().getLabel() }
745-
746-
/** Gets the statement that resulted in this `goto label` completion. */
747-
GotoLabelStmt getGotoStmt() { result = goto }
748-
749-
override string toString() {
750-
// `NestedCompletion` defines `toString()` for the other case
751-
this = TGotoLabelCompletion(goto) and result = "goto(" + this.getLabel() + ")"
752-
}
753-
}
754-
755-
/**
756-
* A completion that represents evaluation of a statement or an
757-
* expression resulting in a `goto case` jump.
758-
*/
759-
class GotoCaseCompletion extends GotoCompletion {
760-
private GotoCaseStmt goto;
718+
class GotoCompletion extends Completion {
719+
private string label;
761720

762-
GotoCaseCompletion() {
763-
this = TGotoCaseCompletion(goto) or
764-
this = TNestedCompletion(_, TGotoCaseCompletion(goto))
721+
GotoCompletion() {
722+
this = TGotoCompletion(label) or
723+
this = TNestedCompletion(_, TGotoCompletion(label))
765724
}
766725

767-
/** Gets the target of the `goto case` completion. */
768-
string getLabel() { result = this.getGotoStmt().getLabel() }
769-
770-
/** Gets the statement that resulted in this `goto case` completion. */
771-
GotoCaseStmt getGotoStmt() { result = goto }
772-
773-
override string toString() {
774-
// `NestedCompletion` defines `toString()` for the other case
775-
this = TGotoCaseCompletion(goto) and result = "goto case(" + this.getLabel() + ")"
776-
}
777-
}
778-
779-
/**
780-
* A completion that represents evaluation of a statement or an
781-
* expression resulting in a `goto default` jump.
782-
*/
783-
class GotoDefaultCompletion extends GotoCompletion {
784-
GotoDefaultCompletion() {
785-
this = TGotoDefaultCompletion() or
786-
this = TNestedCompletion(_, TGotoDefaultCompletion())
787-
}
726+
/** Gets the label of the `goto` completion. */
727+
string getLabel() { result = label }
788728

789729
override string toString() {
790730
// `NestedCompletion` defines `toString()` for the other case
791-
this = TGotoDefaultCompletion() and result = "goto default"
731+
this = TGotoCompletion(label) and result = "goto(" + label + ")"
792732
}
793733
}
794734

csharp/ql/src/semmle/code/csharp/controlflow/internal/SuccessorType.qll

Lines changed: 7 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@ private newtype TSuccessorType =
1818
TReturnSuccessor() or
1919
TBreakSuccessor() or
2020
TContinueSuccessor() or
21-
TGotoLabelSuccessor(GotoLabelStmt goto) or
22-
TGotoCaseSuccessor(GotoCaseStmt goto) or
23-
TGotoDefaultSuccessor() or
21+
TGotoSuccessor(string label) { label = any(GotoStmt gs).getLabel() } or
2422
TExceptionSuccessor(ExceptionClass ec) { exists(ThrowCompletion c | c.getExceptionClass() = ec) } or
2523
TExitSuccessor()
2624

@@ -300,7 +298,7 @@ module SuccessorTypes {
300298
}
301299

302300
/**
303-
* A `goto label` control flow successor.
301+
* A `goto` control flow successor.
304302
*
305303
* Example:
306304
*
@@ -319,68 +317,17 @@ module SuccessorTypes {
319317
* The node `Return: return x` is a `goto label` successor of the node
320318
* `goto Return;`.
321319
*/
322-
class GotoLabelSuccessor extends SuccessorType, TGotoLabelSuccessor {
323-
/** Gets the statement that resulted in this `goto` successor. */
324-
GotoLabelStmt getGotoStmt() { this = TGotoLabelSuccessor(result) }
320+
class GotoSuccessor extends SuccessorType, TGotoSuccessor {
321+
/** Gets the `goto` label. */
322+
string getLabel() { this = TGotoSuccessor(result) }
325323

326-
override string toString() { result = "goto(" + getGotoStmt().getLabel() + ")" }
324+
override string toString() { result = "goto(" + this.getLabel() + ")" }
327325

328326
override predicate matchesCompletion(Completion c) {
329-
c.(GotoLabelCompletion).getGotoStmt() = getGotoStmt()
327+
c.(GotoCompletion).getLabel() = this.getLabel()
330328
}
331329
}
332330

333-
/**
334-
* A `goto case` control flow successor.
335-
*
336-
* Example:
337-
*
338-
* ```
339-
* switch (x)
340-
* {
341-
* case 0 : return 1;
342-
* case 1 : goto case 0;
343-
* default : return -1;
344-
* }
345-
* ```
346-
*
347-
* The node `case 0 : return 1;` is a `goto case` successor of the node
348-
* `goto case 0;`.
349-
*/
350-
class GotoCaseSuccessor extends SuccessorType, TGotoCaseSuccessor {
351-
/** Gets the statement that resulted in this `goto case` successor. */
352-
GotoCaseStmt getGotoStmt() { this = TGotoCaseSuccessor(result) }
353-
354-
override string toString() { result = "goto(" + getGotoStmt().getLabel() + ")" }
355-
356-
override predicate matchesCompletion(Completion c) {
357-
c.(GotoCaseCompletion).getGotoStmt() = getGotoStmt()
358-
}
359-
}
360-
361-
/**
362-
* A `goto default` control flow successor.
363-
*
364-
* Example:
365-
*
366-
* ```
367-
* switch (x)
368-
* {
369-
* case 0 : return 1;
370-
* case 1 : goto default;
371-
* default : return -1;
372-
* }
373-
* ```
374-
*
375-
* The node `default : return -1;` is a `goto default` successor of the node
376-
* `goto default;`.
377-
*/
378-
class GotoDefaultSuccessor extends SuccessorType, TGotoDefaultSuccessor {
379-
override string toString() { result = "goto default" }
380-
381-
override predicate matchesCompletion(Completion c) { c instanceof GotoDefaultCompletion }
382-
}
383-
384331
/**
385332
* An exceptional control flow successor.
386333
*

csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,8 +425,9 @@
425425
| cflow.cs:30:18:33:37 | if (...) ... | cflow.cs:30:22:30:31 | ... == ... | 6 |
426426
| cflow.cs:31:17:31:42 | ...; | cflow.cs:31:17:31:41 | call to method WriteLine | 3 |
427427
| cflow.cs:33:17:33:37 | ...; | cflow.cs:33:17:33:36 | call to method WriteLine | 3 |
428-
| cflow.cs:37:17:37:22 | enter Switch | cflow.cs:41:18:41:18 | 1 | 6 |
428+
| cflow.cs:37:17:37:22 | enter Switch | cflow.cs:39:17:39:17 | access to parameter a | 4 |
429429
| cflow.cs:37:17:37:22 | exit Switch | cflow.cs:37:17:37:22 | exit Switch | 1 |
430+
| cflow.cs:41:13:41:19 | case ...: | cflow.cs:41:18:41:18 | 1 | 2 |
430431
| cflow.cs:42:17:42:39 | ...; | cflow.cs:43:17:43:28 | goto case ...; | 5 |
431432
| cflow.cs:44:13:44:19 | case ...: | cflow.cs:44:18:44:18 | 2 | 2 |
432433
| cflow.cs:45:17:45:39 | ...; | cflow.cs:46:17:46:28 | goto case ...; | 5 |

0 commit comments

Comments
 (0)