6
6
* (which does not use the shared data flow libraries).
7
7
*/
8
8
9
+ /**
10
+ * Convenience-predicate for extracting two capture groups at once.
11
+ */
12
+ bindingset [ input, regexp]
13
+ private predicate regexpCaptureTwo ( string input , string regexp , string capture1 , string capture2 ) {
14
+ capture1 = input .regexpCapture ( regexp , 1 ) and
15
+ capture2 = input .regexpCapture ( regexp , 2 )
16
+ }
17
+
9
18
/** Companion module to the `AccessPath` class. */
10
19
module AccessPath {
11
20
/** A string that should be parsed as an access path. */
12
21
abstract class Range extends string {
13
22
bindingset [ this ]
14
23
Range ( ) { any ( ) }
15
24
}
25
+
26
+ /**
27
+ * Parses an integer constant `n` or interval `n1..n2` (inclusive) and gets the value
28
+ * of the constant or any value contained in the interval.
29
+ */
30
+ bindingset [ arg]
31
+ int parseInt ( string arg ) {
32
+ result = arg .toInt ( )
33
+ or
34
+ // Match "n1..n2"
35
+ exists ( string lo , string hi |
36
+ regexpCaptureTwo ( arg , "(-?\\d+)\\.\\.(-?\\d+)" , lo , hi ) and
37
+ result = [ lo .toInt ( ) .. hi .toInt ( ) ]
38
+ )
39
+ }
40
+
41
+ /**
42
+ * Parses a lower-bounded interval `n..` and gets the lower bound.
43
+ */
44
+ bindingset [ arg]
45
+ int parseLowerBound ( string arg ) { result = arg .regexpCapture ( "(-?\\d+)\\.\\." , 1 ) .toInt ( ) }
46
+
47
+ /**
48
+ * Parses an integer constant or interval (bounded or unbounded) that explicitly
49
+ * references the arity, such as `N-1` or `N-3..N-1`.
50
+ *
51
+ * Note that expressions of form `N-x` will never resolve to a negative index,
52
+ * even if `N` is zero (it will have no result in that case).
53
+ */
54
+ bindingset [ arg, arity]
55
+ private int parseIntWithExplicitArity ( string arg , int arity ) {
56
+ result >= 0 and // do not allow N-1 to resolve to a negative index
57
+ exists ( string lo |
58
+ // N-x
59
+ lo = arg .regexpCapture ( "N-(\\d+)" , 1 ) and
60
+ result = arity - lo .toInt ( )
61
+ or
62
+ // N-x..
63
+ lo = arg .regexpCapture ( "N-(\\d+)\\.\\." , 1 ) and
64
+ result = [ arity - lo .toInt ( ) , arity - 1 ]
65
+ )
66
+ or
67
+ exists ( string lo , string hi |
68
+ // x..N-y
69
+ regexpCaptureTwo ( arg , "(-?\\d+)\\.\\.N-(\\d+)" , lo , hi ) and
70
+ result = [ lo .toInt ( ) .. arity - hi .toInt ( ) ]
71
+ or
72
+ // N-x..N-y
73
+ regexpCaptureTwo ( arg , "N-(\\d+)\\.\\.N-(\\d+)" , lo , hi ) and
74
+ result = [ arity - lo .toInt ( ) .. arity - hi .toInt ( ) ] and
75
+ result >= 0
76
+ or
77
+ // N-x..y
78
+ regexpCaptureTwo ( arg , "N-(\\d+)\\.\\.(\\d+)" , lo , hi ) and
79
+ result = [ arity - lo .toInt ( ) .. hi .toInt ( ) ] and
80
+ result >= 0
81
+ )
82
+ }
83
+
84
+ /**
85
+ * Parses an integer constant or interval (bounded or unbounded) and gets any
86
+ * of the integers contained within (of which there may be infinitely many).
87
+ *
88
+ * Has no result for arguments involving an explicit arity, such as `N-1`.
89
+ */
90
+ bindingset [ arg, result ]
91
+ int parseIntUnbounded ( string arg ) {
92
+ result = parseInt ( arg )
93
+ or
94
+ result >= parseLowerBound ( arg )
95
+ }
96
+
97
+ /**
98
+ * Parses an integer constant or interval (bounded or unbounded) that
99
+ * may reference the arity of a call, such as `N-1` or `N-3..N-1`.
100
+ *
101
+ * Note that expressions of form `N-x` will never resolve to a negative index,
102
+ * even if `N` is zero (it will have no result in that case).
103
+ */
104
+ bindingset [ arg, arity]
105
+ int parseIntWithArity ( string arg , int arity ) {
106
+ result = parseInt ( arg )
107
+ or
108
+ result in [ parseLowerBound ( arg ) .. arity - 1 ]
109
+ or
110
+ result = parseIntWithExplicitArity ( arg , arity )
111
+ }
16
112
}
17
113
18
114
/** Gets the `n`th token on the access path as a string. */
@@ -53,7 +149,7 @@ class AccessPath extends string instanceof AccessPath::Range {
53
149
* An access part token such as `Argument[1]` or `ReturnValue`, appearing in one or more access paths.
54
150
*/
55
151
class AccessPathToken extends string {
56
- AccessPathToken ( ) { this = getRawToken ( any ( AccessPath path ) , _) }
152
+ AccessPathToken ( ) { this = getRawToken ( _ , _) }
57
153
58
154
private string getPart ( int part ) {
59
155
result = this .regexpCapture ( "([^\\[]+)(?:\\[([^\\]]*)\\])?" , part )
@@ -71,9 +167,16 @@ class AccessPathToken extends string {
71
167
/** Gets the `n`th argument to this token, such as `x` or `y` from `Member[x,y]`. */
72
168
string getArgument ( int n ) { result = this .getArgumentList ( ) .splitAt ( "," , n ) .trim ( ) }
73
169
170
+ /** Gets the `n`th argument to this `name` token, such as `x` or `y` from `Member[x,y]`. */
171
+ pragma [ nomagic]
172
+ string getArgument ( string name , int n ) { name = this .getName ( ) and result = this .getArgument ( n ) }
173
+
74
174
/** Gets an argument to this token, such as `x` or `y` from `Member[x,y]`. */
75
175
string getAnArgument ( ) { result = this .getArgument ( _) }
76
176
177
+ /** Gets an argument to this `name` token, such as `x` or `y` from `Member[x,y]`. */
178
+ string getAnArgument ( string name ) { result = this .getArgument ( name , _) }
179
+
77
180
/** Gets the number of arguments to this token, such as 2 for `Member[x,y]` or zero for `ReturnValue`. */
78
181
int getNumArgument ( ) { result = count ( int n | exists ( this .getArgument ( n ) ) ) }
79
182
}
0 commit comments