Skip to content

Commit 91ed111

Browse files
committed
C#: Improve CFG for exception handlers
Use generic CFG splitting to add a new type of split for exception handlers, `ExceptionHandlerSplit`, which tags eachs node belonging to a `catch` clause with the type of exception being caught. This allows for a more accurate CFG for `try-catch` statements, where exception filters are handled properly.
1 parent a705b3a commit 91ed111

File tree

10 files changed

+721
-351
lines changed

10 files changed

+721
-351
lines changed

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -971,6 +971,8 @@ class TryStmt extends Stmt, @try_stmt {
971971
* general `catch` clause (`GeneralCatchClause`).
972972
*/
973973
class CatchClause extends Stmt, @catch {
974+
/** Gets the `try` statement that this `catch` clause belongs to. */
975+
TryStmt getTryStmt() { result.getACatchClause() = this }
974976

975977
/** Gets the block of this `catch` clause. */
976978
BlockStmt getBlock() { result.getParent() = this }
@@ -1007,6 +1009,15 @@ class CatchClause extends Stmt, @catch {
10071009

10081010
/** Holds if this `catch` clause has a filter. */
10091011
predicate hasFilterClause() { exists(getFilterClause()) }
1012+
1013+
/** Holds if this is the last `catch` clause in the `try` statement that it belongs to. */
1014+
predicate isLast() {
1015+
exists(TryStmt ts, int last |
1016+
ts = this.getTryStmt() and
1017+
last = max(int i | exists(ts.getCatchClause(i))) and
1018+
this = ts.getCatchClause(last)
1019+
)
1020+
}
10101021
}
10111022

10121023
/**

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

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ class Completion extends TCompletion {
101101
or
102102
not isNullnessConstant(cfe, _) and
103103
this = TNullnessCompletion(_)
104-
else if mustHaveMatchingCompletion(_, cfe) then
104+
else if mustHaveMatchingCompletion(cfe) then
105105
exists(boolean value |
106106
isMatchingConstant(cfe, value) |
107107
this = TMatchingCompletion(value)
@@ -314,6 +314,11 @@ private predicate inBooleanContext(Expr e, boolean isBooleanCompletionForParent)
314314
isBooleanCompletionForParent = false
315315
)
316316
or
317+
exists(SpecificCatchClause scc |
318+
scc.getFilterClause() = e |
319+
isBooleanCompletionForParent = false
320+
)
321+
or
317322
exists(LogicalNotExpr lne |
318323
lne.getAnOperand() = e |
319324
inBooleanContext(lne, _) and
@@ -396,14 +401,20 @@ private predicate inNullnessContext(Expr e, boolean isNullnessCompletionForParen
396401
)
397402
}
398403

404+
private predicate mustHaveMatchingCompletion(SwitchStmt ss, ControlFlowElement cfe) {
405+
cfe = ss.getAConstCase().getExpr()
406+
or
407+
cfe = ss.getATypeCase().getTypeAccess() // use type access to represent the type test
408+
}
409+
399410
/**
400-
* Holds if a normal completion of `e` must be a matching completion. Thats is,
401-
* whether `e` determines a match in a `switch` statement.
411+
* Holds if a normal completion of `cfe` must be a matching completion. Thats is,
412+
* whether `cfe` determines a match in a `switch` statement or `catch` clause.
402413
*/
403-
private predicate mustHaveMatchingCompletion(SwitchStmt ss, Expr e) {
404-
e = ss.getAConstCase().getExpr()
414+
private predicate mustHaveMatchingCompletion(ControlFlowElement cfe) {
415+
mustHaveMatchingCompletion(_, cfe)
405416
or
406-
e = ss.getATypeCase().getTypeAccess() // use type access to represent the type test
417+
cfe instanceof SpecificCatchClause
407418
}
408419

409420
/**

0 commit comments

Comments
 (0)