Skip to content

Commit e27b373

Browse files
authored
Merge pull request #1548 from hvitved/csharp/cfg/simplify-goto-completions
Approved by calumgrant
2 parents 5f55cb0 + f56c17f commit e27b373

File tree

12 files changed

+177
-255
lines changed

12 files changed

+177
-255
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: 22 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,10 @@ module ControlFlow {
746746
result = TSelf(any(Completion c | c.isValidFor(cfe)))
747747
}
748748

749+
private TRec specificBoolean(boolean value) {
750+
result = TRec(TLastRecSpecificCompletion(any(BooleanCompletion bc | bc.getValue() = value)))
751+
}
752+
749753
/**
750754
* Gets an element from which the last element of `cfe` can be computed
751755
* (recursively) based on computation specification `c`. The predicate
@@ -788,7 +792,7 @@ module ControlFlow {
788792
cfe = any(LogicalAndExpr lae |
789793
// Left operand exits with a false completion
790794
result = lae.getLeftOperand() and
791-
c = TRec(TLastRecSpecificCompletion(any(FalseCompletion fc)))
795+
c = specificBoolean(false)
792796
or
793797
// Left operand exits abnormally
794798
result = lae.getLeftOperand() and
@@ -802,7 +806,7 @@ module ControlFlow {
802806
cfe = any(LogicalOrExpr loe |
803807
// Left operand exits with a true completion
804808
result = loe.getLeftOperand() and
805-
c = TRec(TLastRecSpecificCompletion(any(TrueCompletion tc)))
809+
c = specificBoolean(true)
806810
or
807811
// Left operand exits abnormally
808812
result = loe.getLeftOperand() and
@@ -887,7 +891,7 @@ module ControlFlow {
887891
cfe = any(IfStmt is |
888892
// Condition exits with a false completion and there is no `else` branch
889893
result = is.getCondition() and
890-
c = TRec(TLastRecSpecificCompletion(any(FalseCompletion fc))) and
894+
c = specificBoolean(false) and
891895
not exists(is.getElse())
892896
or
893897
// Condition exits abnormally
@@ -936,7 +940,7 @@ module ControlFlow {
936940
c = TRec(TLastRecSpecificNegCompletion(any(MatchingCompletion mc | mc.isMatch())))
937941
or
938942
result = cs.getCondition() and
939-
c = TRec(TLastRecSpecificCompletion(any(FalseCompletion fc)))
943+
c = specificBoolean(false)
940944
)
941945
or
942946
// Last statement exits with any non-break completion
@@ -966,7 +970,7 @@ module ControlFlow {
966970
cfe = any(Case case |
967971
// Condition exists with a `false` completion
968972
result = case.getCondition() and
969-
c = TRec(TLastRecSpecificCompletion(any(FalseCompletion fc)))
973+
c = specificBoolean(false)
970974
or
971975
// Condition exists abnormally
972976
result = case.getCondition() and
@@ -987,7 +991,7 @@ module ControlFlow {
987991
|
988992
// Condition exits with a false completion
989993
result = ls.getCondition() and
990-
c = TRec(TLastRecSpecificCompletion(any(FalseCompletion fc)))
994+
c = specificBoolean(false)
991995
or
992996
// Condition exits abnormally
993997
result = ls.getCondition() and
@@ -1046,7 +1050,7 @@ module ControlFlow {
10461050
or
10471051
// Incompatible filter
10481052
result = scc.getFilterClause() and
1049-
c = TRec(TLastRecSpecificCompletion(any(FalseCompletion fc)))
1053+
c = specificBoolean(false)
10501054
)
10511055
)
10521056
or
@@ -1087,6 +1091,12 @@ module ControlFlow {
10871091
)
10881092
}
10891093

1094+
pragma[noinline]
1095+
private LabeledStmt getLabledStmt(string label, Callable c) {
1096+
result.getEnclosingCallable() = c and
1097+
label = result.getLabel()
1098+
}
1099+
10901100
/**
10911101
* Gets a potential last element executed within control flow element `cfe`,
10921102
* as well as its completion.
@@ -1148,8 +1158,8 @@ module ControlFlow {
11481158
rec = TLastRecSwitchAbnormalCompletion() and
11491159
not c instanceof BreakCompletion and
11501160
not c instanceof NormalCompletion and
1151-
not c instanceof GotoDefaultCompletion and
1152-
not c instanceof GotoCaseCompletion and
1161+
not getLabledStmt(c.(GotoCompletion).getLabel(), cfe.getEnclosingCallable()) instanceof
1162+
CaseStmt and
11531163
c = c0
11541164
or
11551165
rec = TLastRecInvalidOperationException() and
@@ -1515,24 +1525,6 @@ module ControlFlow {
15151525
c instanceof NormalCompletion and
15161526
result = first(ss.getStmt(i + 1))
15171527
)
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-
)
15361528
)
15371529
or
15381530
exists(Case case |
@@ -1766,13 +1758,13 @@ module ControlFlow {
17661758
or
17671759
// Flow from element with `goto` completion to first element of relevant
17681760
// target
1769-
c = any(GotoLabelCompletion glc |
1770-
cfe = last(_, glc) and
1761+
c = any(GotoCompletion gc |
1762+
cfe = last(_, gc) and
17711763
// Special case: when a `goto` happens inside a `try` statement with a
17721764
// `finally` block, flow does not go directly to the target, but instead
17731765
// to the `finally` block (and from there possibly to the target)
17741766
not cfe = getBlockOrCatchFinallyPred(any(TryStmt ts | ts.hasFinally()), _) and
1775-
result = first(glc.getGotoStmt().getTarget())
1767+
result = first(getLabledStmt(gc.getLabel(), cfe.getEnclosingCallable()))
17761768
)
17771769
or
17781770
// Standard left-to-right evaluation

0 commit comments

Comments
 (0)