Skip to content

Commit 8434dc3

Browse files
committed
Controlflow: Add a shared SuccessorType implementation.
1 parent 70a871c commit 8434dc3

File tree

1 file changed

+341
-0
lines changed

1 file changed

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

0 commit comments

Comments
 (0)