Skip to content

Commit d9989ce

Browse files
committed
Controlflow: Add a shared SuccessorType implementation.
1 parent f232335 commit d9989ce

File tree

1 file changed

+298
-0
lines changed

1 file changed

+298
-0
lines changed
Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
/**
2+
* Provides different types of control flow successor types. These are used as
3+
* edge labels in the control flow graph.
4+
*/
5+
overlay[local]
6+
module;
7+
8+
private import codeql.util.Boolean
9+
10+
/*
11+
* SuccessorType
12+
* |- NormalSuccessor
13+
* | |- DirectSuccessor
14+
* | \- ConditionalSuccessor
15+
* | |- BooleanSuccessor
16+
* | |- NullnessSuccessor
17+
* | |- MatchingSuccessor
18+
* | \- EmptinessSuccessor
19+
* \- AbruptSuccessor
20+
* |- ExceptionSuccessor
21+
* |- ReturnSuccessor
22+
* |- ExitSuccessor (program termination)
23+
* \- JumpSuccessor (break, continue, goto, etc.)
24+
*/
25+
26+
private newtype TSuccessorType =
27+
TDirectSuccessor() or
28+
TBooleanSuccessor(Boolean branch) or
29+
TNullnessSuccessor(Boolean isNull) or
30+
TMatchingSuccessor(Boolean isMatch) or
31+
TEmptinessSuccessor(Boolean isEmpty) or
32+
TExceptionSuccessor() or
33+
TReturnSuccessor() or
34+
TExitSuccessor() or
35+
TJumpSuccessor()
36+
37+
/**
38+
* The type of a control flow successor.
39+
*
40+
* A successor is either normal, which covers direct and conditional
41+
* successors, or abrupt, which covers all other types of successors including
42+
* for example exceptions, returns, and other jumps.
43+
*/
44+
class SuccessorType extends TSuccessorType {
45+
/** Gets a textual representation of this successor type. */
46+
abstract string toString();
47+
}
48+
49+
private class TNormalSuccessor = TDirectSuccessor or TConditionalSuccessor;
50+
51+
/**
52+
* A normal control flow successor. This is either a direct or a conditional
53+
* successor.
54+
*/
55+
abstract class NormalSuccessor extends SuccessorType, TNormalSuccessor { }
56+
57+
/** A direct control flow successor. */
58+
class DirectSuccessor extends NormalSuccessor, TDirectSuccessor {
59+
override string toString() { result = "successor" }
60+
}
61+
62+
private class TConditionalSuccessor =
63+
TBooleanSuccessor or TMatchingSuccessor or TNullnessSuccessor or TEmptinessSuccessor;
64+
65+
/**
66+
* A conditional control flow successor. Either a Boolean successor (`BooleanSuccessor`),
67+
* a nullness successor (`NullnessSuccessor`), a matching successor (`MatchingSuccessor`),
68+
* or an emptiness successor (`EmptinessSuccessor`).
69+
*/
70+
abstract class ConditionalSuccessor extends NormalSuccessor, TConditionalSuccessor {
71+
/** Gets the Boolean value of this successor. */
72+
abstract boolean getValue();
73+
}
74+
75+
/**
76+
* A Boolean control flow successor.
77+
*
78+
* For example, this program fragment:
79+
*
80+
* ```csharp
81+
* if (x < 0)
82+
* return 0;
83+
* else
84+
* return 1;
85+
* ```
86+
*
87+
* has a control flow graph containing Boolean successors:
88+
*
89+
* ```
90+
* if
91+
* |
92+
* x < 0
93+
* / \
94+
* / \
95+
* / \
96+
* true false
97+
* | \
98+
* return 0 return 1
99+
* ```
100+
*/
101+
class BooleanSuccessor extends ConditionalSuccessor, TBooleanSuccessor {
102+
override boolean getValue() { this = TBooleanSuccessor(result) }
103+
104+
override string toString() { result = this.getValue().toString() }
105+
}
106+
107+
/**
108+
* A nullness control flow successor.
109+
*
110+
* For example, this program fragment:
111+
*
112+
* ```csharp
113+
* int? M(string s) => s?.Length;
114+
* ```
115+
*
116+
* has a control flow graph containing nullness successors:
117+
*
118+
* ```
119+
* enter M
120+
* |
121+
* s
122+
* / \
123+
* / \
124+
* / \
125+
* null non-null
126+
* \ |
127+
* \ Length
128+
* \ /
129+
* \ /
130+
* exit M
131+
* ```
132+
*/
133+
class NullnessSuccessor extends ConditionalSuccessor, TNullnessSuccessor {
134+
/** Holds if this is a `null` successor. */
135+
predicate isNull() { this = TNullnessSuccessor(true) }
136+
137+
override boolean getValue() { this = TNullnessSuccessor(result) }
138+
139+
override string toString() { if this.isNull() then result = "null" else result = "non-null" }
140+
}
141+
142+
/**
143+
* A matching control flow successor.
144+
*
145+
* For example, this program fragment:
146+
*
147+
* ```csharp
148+
* switch (x) {
149+
* case 0 :
150+
* return 0;
151+
* default :
152+
* return 1;
153+
* }
154+
* ```
155+
*
156+
* has a control flow graph containing matching successors:
157+
*
158+
* ```
159+
* switch
160+
* |
161+
* x
162+
* |
163+
* case 0
164+
* / \
165+
* / \
166+
* / \
167+
* match no-match
168+
* | \
169+
* return 0 default
170+
* |
171+
* return 1
172+
* ```
173+
*/
174+
class MatchingSuccessor extends ConditionalSuccessor, TMatchingSuccessor {
175+
/** Holds if this is a match successor. */
176+
predicate isMatch() { this = TMatchingSuccessor(true) }
177+
178+
override boolean getValue() { this = TMatchingSuccessor(result) }
179+
180+
override string toString() { if this.isMatch() then result = "match" else result = "no-match" }
181+
}
182+
183+
/**
184+
* An emptiness control flow successor.
185+
*
186+
* For example, this program fragment:
187+
*
188+
* ```csharp
189+
* foreach (var arg in args)
190+
* {
191+
* yield return arg;
192+
* }
193+
* yield return "";
194+
* ```
195+
*
196+
* has a control flow graph containing emptiness successors:
197+
*
198+
* ```
199+
* args
200+
* |
201+
* loop-header------<-----
202+
* / \ \
203+
* / \ |
204+
* / \ |
205+
* / \ |
206+
* empty non-empty |
207+
* | \ |
208+
* yield return "" \ |
209+
* var arg |
210+
* | |
211+
* yield return arg |
212+
* \_________/
213+
* ```
214+
*/
215+
class EmptinessSuccessor extends ConditionalSuccessor, TEmptinessSuccessor {
216+
/** Holds if this is an empty successor. */
217+
predicate isEmpty() { this = TEmptinessSuccessor(true) }
218+
219+
override boolean getValue() { this = TEmptinessSuccessor(result) }
220+
221+
override string toString() { if this.isEmpty() then result = "empty" else result = "non-empty" }
222+
}
223+
224+
private class TAbruptSuccessor =
225+
TExceptionSuccessor or TReturnSuccessor or TExitSuccessor or TJumpSuccessor;
226+
227+
/** An abrupt control flow successor. */
228+
abstract class AbruptSuccessor extends SuccessorType, TAbruptSuccessor { }
229+
230+
/**
231+
* An exceptional control flow successor.
232+
*
233+
* Example:
234+
*
235+
* ```csharp
236+
* int M(string s)
237+
* {
238+
* if (s == null)
239+
* throw new ArgumentNullException(nameof(s));
240+
* return s.Length;
241+
* }
242+
* ```
243+
*
244+
* The callable exit node of `M` is an exceptional successor of the node
245+
* `throw new ArgumentNullException(nameof(s));`.
246+
*/
247+
class ExceptionSuccessor extends AbruptSuccessor, TExceptionSuccessor {
248+
override string toString() { result = "exception" }
249+
}
250+
251+
/**
252+
* A `return` control flow successor.
253+
*
254+
* Example:
255+
*
256+
* ```csharp
257+
* void M()
258+
* {
259+
* return;
260+
* }
261+
* ```
262+
*
263+
* The callable exit node of `M` is a `return` successor of the `return;`
264+
* statement.
265+
*/
266+
class ReturnSuccessor extends AbruptSuccessor, TReturnSuccessor {
267+
override string toString() { result = "return" }
268+
}
269+
270+
/**
271+
* An exit control flow successor.
272+
*
273+
* Example:
274+
*
275+
* ```csharp
276+
* int M(string s)
277+
* {
278+
* if (s == null)
279+
* System.Environment.Exit(0);
280+
* return s.Length;
281+
* }
282+
* ```
283+
*
284+
* The callable exit node of `M` is an exit successor of the node on line 4.
285+
*/
286+
class ExitSuccessor extends AbruptSuccessor, TExitSuccessor {
287+
override string toString() { result = "exit" }
288+
}
289+
290+
/**
291+
* A jump control flow successor.
292+
*
293+
* This covers non-exceptional, non-local control flow, such as `break`,
294+
* `continue`, and `goto`.
295+
*/
296+
class JumpSuccessor extends AbruptSuccessor, TJumpSuccessor {
297+
override string toString() { result = "jump" }
298+
}

0 commit comments

Comments
 (0)