11/** Provides classes for assertions. */
22private import semmle.code.csharp.frameworks.system.Diagnostics
33private import semmle.code.csharp.frameworks.test.VisualStudio
4+ private import semmle.code.csharp.frameworks.System
45
56/** An assertion method. */
67abstract class AssertMethod extends Method {
78 /** Gets the index of the parameter being asserted. */
89 abstract int getAssertionIndex ( ) ;
10+
11+ /** Gets the parameter being asserted. */
12+ final Parameter getAssertionParameter ( ) {
13+ result = this .getParameter ( this .getAssertionIndex ( ) )
14+ }
15+
16+ /** Gets the exception being thrown if the assertion fails, if any. */
17+ abstract ExceptionClass getExceptionClass ( ) ;
918}
1019
1120/** A positive assertion method. */
@@ -24,6 +33,36 @@ abstract class AssertNullMethod extends AssertMethod {
2433abstract class AssertNonNullMethod extends AssertMethod {
2534}
2635
36+ /** An assertion, that is, a call to an assertion method. */
37+ class Assertion extends MethodCall {
38+ private AssertMethod target ;
39+
40+ Assertion ( ) { this .getTarget ( ) = target }
41+
42+ /** Gets the assertion method targeted by this assertion. */
43+ AssertMethod getAssertMethod ( ) { result = target }
44+
45+ /** Gets the expression that this assertion pertains to. */
46+ Expr getExpr ( ) {
47+ result = this .getArgumentForParameter ( target .getAssertionParameter ( ) )
48+ }
49+ }
50+
51+ /** A trivially failing assertion, for example `Debug.Assert(false)`. */
52+ class FailingAssertion extends Assertion {
53+ FailingAssertion ( ) {
54+ exists ( AssertMethod am , Expr e |
55+ am = this .getAssertMethod ( ) and
56+ e = this .getExpr ( ) |
57+ am instanceof AssertTrueMethod and
58+ e .( BoolLiteral ) .getBoolValue ( ) = false
59+ or
60+ am instanceof AssertFalseMethod and
61+ e .( BoolLiteral ) .getBoolValue ( ) = true
62+ )
63+ }
64+ }
65+
2766/**
2867 * A `System.Diagnostics.Debug` assertion method.
2968 */
@@ -33,6 +72,12 @@ class SystemDiagnosticsDebugAssertTrueMethod extends AssertTrueMethod {
3372 }
3473
3574 override int getAssertionIndex ( ) { result = 0 }
75+
76+ override ExceptionClass getExceptionClass ( ) {
77+ // A failing assertion generates a message box, see
78+ // https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.debug.assert
79+ none ( )
80+ }
3681}
3782
3883/** A Visual Studio assertion method. */
@@ -42,6 +87,8 @@ class VSTestAssertTrueMethod extends AssertTrueMethod {
4287 }
4388
4489 override int getAssertionIndex ( ) { result = 0 }
90+
91+ override AssertFailedExceptionClass getExceptionClass ( ) { any ( ) }
4592}
4693
4794/** A Visual Studio negated assertion method. */
@@ -51,6 +98,8 @@ class VSTestAssertFalseMethod extends AssertFalseMethod {
5198 }
5299
53100 override int getAssertionIndex ( ) { result = 0 }
101+
102+ override AssertFailedExceptionClass getExceptionClass ( ) { any ( ) }
54103}
55104
56105/** A Visual Studio `null` assertion method. */
@@ -60,6 +109,8 @@ class VSTestAssertNullMethod extends AssertNullMethod {
60109 }
61110
62111 override int getAssertionIndex ( ) { result = 0 }
112+
113+ override AssertFailedExceptionClass getExceptionClass ( ) { any ( ) }
63114}
64115
65116/** A Visual Studio non-`null` assertion method. */
@@ -69,28 +120,50 @@ class VSTestAssertNonNullMethod extends AssertNonNullMethod {
69120 }
70121
71122 override int getAssertionIndex ( ) { result = 0 }
123+
124+ override AssertFailedExceptionClass getExceptionClass ( ) { any ( ) }
72125}
73126
74127/** A method that forwards to another assertion method. */
75128class ForwarderAssertMethod extends AssertMethod {
76129 AssertMethod forwardee ;
77- int assertionIndex ;
130+ Parameter p ;
78131
79132 ForwarderAssertMethod ( ) {
80- exists ( AssignableDefinitions:: ImplicitParameterDefinition def , ParameterRead pr |
81- def .getParameter ( ) = this .getParameter ( assertionIndex ) and
82- pr = def .getAReachableRead ( ) and
83- pr .getAControlFlowNode ( ) .postDominates ( this .getEntryPoint ( ) ) and
84- forwardee .getParameter ( forwardee .getAssertionIndex ( ) ) .getAnAssignedArgument ( ) = pr
133+ p = this .getAParameter ( ) and
134+ strictcount ( AssignableDefinition def | def .getTarget ( ) = p ) = 1 and
135+ forex ( ControlFlowElement body |
136+ body = this .getABody ( ) |
137+ exists ( Assertion a |
138+ body = getAnAssertingElement ( a ) and
139+ a .getExpr ( ) = p .getAnAccess ( ) and
140+ forwardee = a .getAssertMethod ( )
141+ )
85142 )
86143 }
87144
88- override int getAssertionIndex ( ) { result = assertionIndex }
145+ override int getAssertionIndex ( ) { result = p . getPosition ( ) }
89146
147+ override ExceptionClass getExceptionClass ( ) {
148+ result = forwardee .getExceptionClass ( )
149+ }
150+
90151 /** Gets the underlying assertion method that is being forwarded to. */
91152 AssertMethod getUnderlyingAssertMethod ( ) { result = forwardee }
92153}
93154
155+ private ControlFlowElement getAnAssertingElement ( Assertion a ) {
156+ result = a
157+ or
158+ result = getAnAssertingStmt ( a )
159+ }
160+
161+ private Stmt getAnAssertingStmt ( Assertion a ) {
162+ result .( ExprStmt ) .getExpr ( ) = getAnAssertingElement ( a )
163+ or
164+ result .( BlockStmt ) .getFirstStmt ( ) = getAnAssertingElement ( a )
165+ }
166+
94167/** A method that forwards to a positive assertion method. */
95168class ForwarderAssertTrueMethod extends ForwarderAssertMethod , AssertTrueMethod {
96169 ForwarderAssertTrueMethod ( ) {
@@ -121,8 +194,5 @@ class ForwarderAssertNonNullMethod extends ForwarderAssertMethod, AssertNonNullM
121194
122195/** Holds if expression `e` appears in an assertion. */
123196predicate isExprInAssertion ( Expr e ) {
124- exists ( MethodCall call , AssertMethod assertMethod |
125- call .getTarget ( ) = assertMethod |
126- e = call .getArgument ( assertMethod .getAssertionIndex ( ) ) .getAChildExpr * ( )
127- )
197+ e = any ( Assertion a ) .getExpr ( ) .getAChildExpr * ( )
128198}
0 commit comments