@@ -785,6 +785,30 @@ module ControlFlow {
785785 c .( ThrowCompletion ) .getExceptionClass ( ) = getExceptionClass ( )
786786 }
787787 }
788+
789+ /**
790+ * An exit control flow successor.
791+ *
792+ * Example:
793+ *
794+ * ```
795+ * int M(string s)
796+ * {
797+ * if (s == null)
798+ * System.Environment.Exit(0);
799+ * return s.Length;
800+ * }
801+ * ```
802+ *
803+ * The callable exit node of `M` is an exit successor of the node on line 4.
804+ */
805+ class ExitSuccessor extends SuccessorType , TExitSuccessor {
806+ override string toString ( ) { result = "exit" }
807+
808+ override predicate matchesCompletion ( Completion c ) {
809+ c instanceof ExitCompletion
810+ }
811+ }
788812 }
789813 private import SuccessorTypes
790814
@@ -1386,10 +1410,15 @@ module ControlFlow {
13861410 result = lastTryStmtFinally ( ts , c ) and
13871411 not c instanceof NormalCompletion
13881412 or
1389- // If there is no `finally` block, last elements are from the body, from
1390- // the blocks of one of the `catch` clauses, or from the last `catch` clause
1391- not ts .hasFinally ( ) and
1392- result = getBlockOrCatchFinallyPred ( ts , c )
1413+ result = getBlockOrCatchFinallyPred ( ts , c ) and
1414+ (
1415+ // If there is no `finally` block, last elements are from the body, from
1416+ // the blocks of one of the `catch` clauses, or from the last `catch` clause
1417+ not ts .hasFinally ( )
1418+ or
1419+ // Exit completions ignore the `finally` block
1420+ c instanceof ExitCompletion
1421+ )
13931422 )
13941423 or
13951424 cfe = any ( SpecificCatchClause scc |
@@ -1453,7 +1482,7 @@ module ControlFlow {
14531482 // Propagate completion from a call to a non-terminating callable
14541483 cfe = any ( NonReturningCall nrc |
14551484 result = nrc and
1456- c = nrc .getTarget ( ) . ( NonReturningCallable ) . getACallCompletion ( )
1485+ c = nrc .getACompletion ( )
14571486 )
14581487 }
14591488
@@ -1736,17 +1765,31 @@ module ControlFlow {
17361765 private import semmle.code.csharp.ExprOrStmtParent
17371766 private import semmle.code.csharp.frameworks.System
17381767
1739- /**
1740- * A call that definitely does not return (conservative analysis).
1741- */
1742- class NonReturningCall extends Call {
1743- NonReturningCall ( ) {
1744- this .getTarget ( ) instanceof NonReturningCallable
1768+ /** A call that definitely does not return (conservative analysis). */
1769+ abstract class NonReturningCall extends Call {
1770+ /** Gets a valid completion for this non-returning call. */
1771+ abstract Completion getACompletion ( ) ;
1772+ }
1773+
1774+ private class ExitingCall extends NonReturningCall {
1775+ ExitingCall ( ) {
1776+ this .getTarget ( ) instanceof ExitingCallable
17451777 }
1778+
1779+ override ExitCompletion getACompletion ( ) { any ( ) }
17461780 }
17471781
1748- /** A callable that does not return. */
1749- abstract class NonReturningCallable extends Callable {
1782+ private class ThrowingCall extends NonReturningCall {
1783+ private ThrowCompletion c ;
1784+
1785+ ThrowingCall ( ) {
1786+ c = this .getTarget ( ) .( ThrowingCallable ) .getACallCompletion ( )
1787+ }
1788+
1789+ override ThrowCompletion getACompletion ( ) { result = c }
1790+ }
1791+
1792+ private abstract class NonReturningCallable extends Callable {
17501793 NonReturningCallable ( ) {
17511794 not exists ( ReturnStmt ret | ret .getEnclosingCallable ( ) = this ) and
17521795 not hasAccessorAutoImplementation ( this , _) and
@@ -1756,19 +1799,9 @@ module ControlFlow {
17561799 v = this .( Accessor ) .getDeclaration ( )
17571800 )
17581801 }
1759-
1760- /** Gets a valid completion for a call to this non-returning callable. */
1761- abstract Completion getACallCompletion ( ) ;
17621802 }
17631803
1764- /**
1765- * A callable that exits when called.
1766- */
1767- private abstract class ExitingCallable extends NonReturningCallable {
1768- override Completion getACallCompletion ( ) {
1769- result instanceof ReturnCompletion
1770- }
1771- }
1804+ private abstract class ExitingCallable extends NonReturningCallable { }
17721805
17731806 private class DirectlyExitingCallable extends ExitingCallable {
17741807 DirectlyExitingCallable ( ) {
@@ -1789,7 +1822,8 @@ module ControlFlow {
17891822 }
17901823
17911824 private ControlFlowElement getAnExitingElement ( ) {
1792- result .( Call ) .getTarget ( ) instanceof ExitingCallable or
1825+ result instanceof ExitingCall
1826+ or
17931827 result = getAnExitingStmt ( )
17941828 }
17951829
@@ -1805,9 +1839,6 @@ module ControlFlow {
18051839 )
18061840 }
18071841
1808- /**
1809- * A callable that throws an exception when called.
1810- */
18111842 private class ThrowingCallable extends NonReturningCallable {
18121843 ThrowingCallable ( ) {
18131844 forex ( ControlFlowElement body |
@@ -1816,16 +1847,17 @@ module ControlFlow {
18161847 )
18171848 }
18181849
1819- override ThrowCompletion getACallCompletion ( ) {
1850+ /** Gets a valid completion for a call to this throwing callable. */
1851+ ThrowCompletion getACallCompletion ( ) {
18201852 this .getABody ( ) = getAThrowingElement ( result )
18211853 }
18221854 }
18231855
18241856 private ControlFlowElement getAThrowingElement ( ThrowCompletion c ) {
1825- c = result .( Call ) . getTarget ( ) . ( ThrowingCallable ) . getACallCompletion ( )
1857+ c = result .( ThrowingCall ) . getACompletion ( )
18261858 or
18271859 result = any ( ThrowElement te |
1828- c .( ThrowCompletion ) . getExceptionClass ( ) = te .getThrownExceptionType ( ) and
1860+ c .getExceptionClass ( ) = te .getThrownExceptionType ( ) and
18291861 // For stub implementations, there may exist proper implementations that are not seen
18301862 // during compilation, so we conservatively rule those out
18311863 not isStub ( te )
@@ -1839,12 +1871,13 @@ module ControlFlow {
18391871 or
18401872 result .( BlockStmt ) .getFirstStmt ( ) = getAThrowingStmt ( c )
18411873 or
1842- exists ( IfStmt ifStmt |
1874+ exists ( IfStmt ifStmt , ThrowCompletion c1 , ThrowCompletion c2 |
18431875 result = ifStmt and
1844- ifStmt .getThen ( ) = getAThrowingElement ( _) and
1845- ifStmt .getElse ( ) = getAThrowingElement ( _) |
1846- ifStmt .getThen ( ) = getAThrowingElement ( c ) or
1847- ifStmt .getElse ( ) = getAThrowingElement ( c )
1876+ ifStmt .getThen ( ) = getAThrowingElement ( c1 ) and
1877+ ifStmt .getElse ( ) = getAThrowingElement ( c2 ) |
1878+ c = c1
1879+ or
1880+ c = c2
18481881 )
18491882 }
18501883
@@ -2293,6 +2326,7 @@ module ControlFlow {
22932326 // Flow from last element of `try` block to first element of `finally` block
22942327 cfe = lastTryStmtBlock ( ts , c ) and
22952328 result = first ( ts .getFinally ( ) ) and
2329+ not c instanceof ExitCompletion and
22962330 (
22972331 c instanceof ThrowCompletion
22982332 implies
@@ -3892,6 +3926,8 @@ module ControlFlow {
38923926 TExceptionSuccessor ( ExceptionClass ec ) {
38933927 exists ( ThrowCompletion c | c .getExceptionClass ( ) = ec )
38943928 }
3929+ or
3930+ TExitSuccessor ( )
38953931
38963932 /** Gets a successor node of a given flow type, if any. */
38973933 cached
0 commit comments