Skip to content

Commit 41fa39e

Browse files
committed
Parameterize AccessPathSyntax.qll
1 parent fd7e345 commit 41fa39e

File tree

1 file changed

+124
-86
lines changed

1 file changed

+124
-86
lines changed

shared/dataflow/codeql/dataflow/internal/AccessPathSyntax.qll

Lines changed: 124 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -15,35 +15,69 @@ private predicate regexpCaptureTwo(string input, string regexp, string capture1,
1515
capture2 = input.regexpCapture(regexp, 2)
1616
}
1717

18-
/** Companion module to the `AccessPath` class. */
19-
module AccessPath {
20-
/** A string that should be parsed as an access path. */
21-
abstract class Range extends string {
22-
bindingset[this]
23-
Range() { any() }
24-
}
18+
/**
19+
* Parses an integer constant `n` or interval `n1..n2` (inclusive) and gets the value
20+
* of the constant or any value contained in the interval.
21+
*/
22+
bindingset[arg]
23+
int parseInt(string arg) {
24+
result = arg.toInt()
25+
or
26+
// Match "n1..n2"
27+
exists(string lo, string hi |
28+
regexpCaptureTwo(arg, "(-?\\d+)\\.\\.(-?\\d+)", lo, hi) and
29+
result = [lo.toInt() .. hi.toInt()]
30+
)
31+
}
2532

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-
)
33+
/**
34+
* Parses a lower-bounded interval `n..` and gets the lower bound.
35+
*/
36+
bindingset[arg]
37+
int parseLowerBound(string arg) { result = arg.regexpCapture("(-?\\d+)\\.\\.", 1).toInt() }
38+
39+
/**
40+
* An access part token such as `Argument[1]` or `ReturnValue`.
41+
*/
42+
class AccessPathTokenBase extends string {
43+
bindingset[this]
44+
AccessPathTokenBase() { exists(this) }
45+
46+
bindingset[this]
47+
private string getPart(int part) {
48+
result = this.regexpCapture("([^\\[]+)(?:\\[([^\\]]*)\\])?", part)
3949
}
4050

51+
/** Gets the name of the token, such as `Member` from `Member[x]` */
52+
bindingset[this]
53+
string getName() { result = this.getPart(1) }
54+
4155
/**
42-
* Parses a lower-bounded interval `n..` and gets the lower bound.
56+
* Gets the argument list, such as `1,2` from `Member[1,2]`,
57+
* or has no result if there are no arguments.
4358
*/
44-
bindingset[arg]
45-
int parseLowerBound(string arg) { result = arg.regexpCapture("(-?\\d+)\\.\\.", 1).toInt() }
59+
bindingset[this]
60+
string getArgumentList() { result = this.getPart(2) }
61+
62+
/** Gets the `n`th argument to this token, such as `x` or `y` from `Member[x,y]`. */
63+
bindingset[this]
64+
string getArgument(int n) { result = this.getArgumentList().splitAt(",", n).trim() }
65+
66+
/** Gets an argument to this token, such as `x` or `y` from `Member[x,y]`. */
67+
bindingset[this]
68+
string getAnArgument() { result = this.getArgument(_) }
69+
70+
/** Gets the number of arguments to this token, such as 2 for `Member[x,y]` or zero for `ReturnValue`. */
71+
bindingset[this]
72+
int getNumArgument() { result = count(int n | exists(this.getArgument(n))) }
73+
}
74+
75+
final private class AccessPathTokenBaseFinal = AccessPathTokenBase;
76+
77+
signature predicate accessPathRangeSig(string s);
4678

79+
/** Companion module to the `AccessPath` class. */
80+
module AccessPath<accessPathRangeSig/1 accessPathRange> {
4781
/**
4882
* Parses an integer constant or interval (bounded or unbounded) that explicitly
4983
* references the arity, such as `N-1` or `N-3..N-1`.
@@ -109,74 +143,78 @@ module AccessPath {
109143
or
110144
result = parseIntWithExplicitArity(arg, arity)
111145
}
112-
}
113-
114-
/** Gets the `n`th token on the access path as a string. */
115-
private string getRawToken(AccessPath path, int n) {
116-
// Avoid splitting by '.' since tokens may contain dots, e.g. `Field[foo.Bar.x]`.
117-
// Instead use regexpFind to match valid tokens, and supplement with a final length
118-
// check (in `AccessPath.hasSyntaxError`) to ensure all characters were included in a token.
119-
result = path.regexpFind("\\w+(?:\\[[^\\]]*\\])?(?=\\.|$)", n, _)
120-
}
121-
122-
/**
123-
* A string that occurs as an access path (either identifying or input/output spec)
124-
* which might be relevant for this database.
125-
*/
126-
class AccessPath extends string instanceof AccessPath::Range {
127-
/** Holds if this string is not a syntactically valid access path. */
128-
predicate hasSyntaxError() {
129-
// If the lengths match, all characters must haven been included in a token
130-
// or seen by the `.` lookahead pattern.
131-
this != "" and
132-
not this.length() = sum(int n | | getRawToken(this, n).length() + 1) - 1
133-
}
134-
135-
/** Gets the `n`th token on the access path (if there are no syntax errors). */
136-
AccessPathToken getToken(int n) {
137-
result = getRawToken(this, n) and
138-
not this.hasSyntaxError()
139-
}
140146

141-
/** Gets the number of tokens on the path (if there are no syntax errors). */
142-
int getNumToken() {
143-
result = count(int n | exists(getRawToken(this, n))) and
144-
not this.hasSyntaxError()
147+
/** Gets the `n`th token on the access path as a string. */
148+
private string getRawToken(AccessPath path, int n) {
149+
// Avoid splitting by '.' since tokens may contain dots, e.g. `Field[foo.Bar.x]`.
150+
// Instead use regexpFind to match valid tokens, and supplement with a final length
151+
// check (in `AccessPath.hasSyntaxError`) to ensure all characters were included in a token.
152+
result = path.regexpFind("\\w+(?:\\[[^\\]]*\\])?(?=\\.|$)", n, _)
145153
}
146-
}
147-
148-
/**
149-
* An access part token such as `Argument[1]` or `ReturnValue`, appearing in one or more access paths.
150-
*/
151-
class AccessPathToken extends string {
152-
AccessPathToken() { this = getRawToken(_, _) }
153154

154-
private string getPart(int part) {
155-
result = this.regexpCapture("([^\\[]+)(?:\\[([^\\]]*)\\])?", part)
155+
/**
156+
* A string that occurs as an access path (either identifying or input/output spec)
157+
* which might be relevant for this database.
158+
*/
159+
final class AccessPath extends string {
160+
AccessPath() { accessPathRange(this) }
161+
162+
/** Holds if this string is not a syntactically valid access path. */
163+
predicate hasSyntaxError() {
164+
// If the lengths match, all characters must haven been included in a token
165+
// or seen by the `.` lookahead pattern.
166+
this != "" and
167+
not this.length() = sum(int n | | getRawToken(this, n).length() + 1) - 1
168+
}
169+
170+
/** Gets the `n`th token on the access path (if there are no syntax errors). */
171+
AccessPathToken getToken(int n) {
172+
result = getRawToken(this, n) and
173+
not this.hasSyntaxError()
174+
}
175+
176+
/** Gets the number of tokens on the path (if there are no syntax errors). */
177+
int getNumToken() {
178+
result = count(int n | exists(getRawToken(this, n))) and
179+
not this.hasSyntaxError()
180+
}
156181
}
157182

158-
/** Gets the name of the token, such as `Member` from `Member[x]` */
159-
string getName() { result = this.getPart(1) }
160-
161183
/**
162-
* Gets the argument list, such as `1,2` from `Member[1,2]`,
163-
* or has no result if there are no arguments.
184+
* An access part token such as `Argument[1]` or `ReturnValue`, appearing in one or more access paths.
164185
*/
165-
string getArgumentList() { result = this.getPart(2) }
166-
167-
/** Gets the `n`th argument to this token, such as `x` or `y` from `Member[x,y]`. */
168-
string getArgument(int n) { result = this.getArgumentList().splitAt(",", n).trim() }
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-
174-
/** Gets an argument to this token, such as `x` or `y` from `Member[x,y]`. */
175-
string getAnArgument() { result = this.getArgument(_) }
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-
180-
/** Gets the number of arguments to this token, such as 2 for `Member[x,y]` or zero for `ReturnValue`. */
181-
int getNumArgument() { result = count(int n | exists(this.getArgument(n))) }
186+
class AccessPathToken extends AccessPathTokenBaseFinal {
187+
AccessPathToken() { this = getRawToken(_, _) }
188+
189+
/** Gets the name of the token, such as `Member` from `Member[x]` */
190+
pragma[nomagic]
191+
string getName() { result = super.getName() }
192+
193+
/**
194+
* Gets the argument list, such as `1,2` from `Member[1,2]`,
195+
* or has no result if there are no arguments.
196+
*/
197+
pragma[nomagic]
198+
string getArgumentList() { result = super.getArgumentList() }
199+
200+
/** Gets the `n`th argument to this token, such as `x` or `y` from `Member[x,y]`. */
201+
pragma[nomagic]
202+
string getArgument(int n) { result = super.getArgument(n) }
203+
204+
/** Gets the `n`th argument to this `name` token, such as `x` or `y` from `Member[x,y]`. */
205+
pragma[nomagic]
206+
string getArgument(string name, int n) {
207+
name = this.getName() and result = this.getArgument(n)
208+
}
209+
210+
/** Gets an argument to this token, such as `x` or `y` from `Member[x,y]`. */
211+
string getAnArgument() { result = this.getArgument(_) }
212+
213+
/** Gets an argument to this `name` token, such as `x` or `y` from `Member[x,y]`. */
214+
string getAnArgument(string name) { result = this.getArgument(name, _) }
215+
216+
/** Gets the number of arguments to this token, such as 2 for `Member[x,y]` or zero for `ReturnValue`. */
217+
pragma[nomagic]
218+
int getNumArgument() { result = count(int n | exists(this.getArgument(n))) }
219+
}
182220
}

0 commit comments

Comments
 (0)