66 * (which does not use the shared data flow libraries).
77 */
88
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+
918/** Companion module to the `AccessPath` class. */
1019module AccessPath {
1120 /** A string that should be parsed as an access path. */
1221 abstract class Range extends string {
1322 bindingset [ this ]
1423 Range ( ) { any ( ) }
1524 }
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+ }
16112}
17113
18114/** Gets the `n`th token on the access path as a string. */
@@ -53,7 +149,7 @@ class AccessPath extends string instanceof AccessPath::Range {
53149 * An access part token such as `Argument[1]` or `ReturnValue`, appearing in one or more access paths.
54150 */
55151class AccessPathToken extends string {
56- AccessPathToken ( ) { this = getRawToken ( any ( AccessPath path ) , _) }
152+ AccessPathToken ( ) { this = getRawToken ( _ , _) }
57153
58154 private string getPart ( int part ) {
59155 result = this .regexpCapture ( "([^\\[]+)(?:\\[([^\\]]*)\\])?" , part )
@@ -71,9 +167,16 @@ class AccessPathToken extends string {
71167 /** Gets the `n`th argument to this token, such as `x` or `y` from `Member[x,y]`. */
72168 string getArgument ( int n ) { result = this .getArgumentList ( ) .splitAt ( "," , n ) .trim ( ) }
73169
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+
74174 /** Gets an argument to this token, such as `x` or `y` from `Member[x,y]`. */
75175 string getAnArgument ( ) { result = this .getArgument ( _) }
76176
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+
77180 /** Gets the number of arguments to this token, such as 2 for `Member[x,y]` or zero for `ReturnValue`. */
78181 int getNumArgument ( ) { result = count ( int n | exists ( this .getArgument ( n ) ) ) }
79182}
0 commit comments