Skip to content

Commit 6e80021

Browse files
committed
Swift: Support multiple parse mode flags.
1 parent f503456 commit 6e80021

File tree

4 files changed

+46
-27
lines changed

4 files changed

+46
-27
lines changed

swift/ql/lib/codeql/swift/regex/internal/ParseRegex.qll

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ abstract class RegExp extends Expr {
2424
predicate isIgnoreCase() { this.getAMode() = "IGNORECASE" }
2525

2626
/**
27-
* Gets the flags for this `RegExp`, or the empty string if it has no flags.
27+
* Gets a string repreenting the flags for this `RegExp`, or the empty string if it has no flags.
2828
*/
29-
string getFlags() { result = concat(string mode | mode = this.getAMode() | mode, " | ")}
29+
string getFlags() { result = concat(string mode | mode = this.getAMode() | mode, " | ") }
3030

3131
/**
3232
* Helper predicate for `charSetStart(int start, int end)`.
@@ -275,27 +275,34 @@ abstract class RegExp extends Expr {
275275
private predicate isGroupStart(int i) { this.nonEscapedCharAt(i) = "(" and not this.inCharSet(i) }
276276

277277
/**
278-
* Holds if `start` and `end` are the range of the mode prefix substring (if any) of this
279-
* regular expression, and `c` is a mode prefix character specified in it. For example
280-
* in the following regular expression, `start` is `0`, `end` is `3` and `c` is `i`.
278+
* Holds if a parse mode prefix starts between `start` and `end`. For example:
281279
* ```
282-
* (?i)one|two
280+
* (?i)
283281
* ```
284282
*/
285-
private predicate flagGroupStart(int start, int end, string c) {
286-
// TODO: I believe this fails with multiple mode specifiers such as (?is) at the moment.
283+
private predicate flagGroupStart(int start, int end) {
287284
this.isGroupStart(start) and
288285
this.getChar(start + 1) = "?" and
289-
end = start + 3 and
290-
c = this.getChar(start + 2) and
291-
c in ["i", "m", "s", "u", "x", "U"]
286+
end = start + 2
287+
}
288+
289+
/**
290+
* Holds if a parse mode prefix group is between `start` and `end`, and includes the
291+
* mode flag `c`.
292+
*/
293+
private predicate flagGroup(int start, int end, string c) {
294+
exists(int inStart, int inEnd |
295+
this.flagGroupStart(start, inStart) and
296+
this.groupContents(start, end, inStart, inEnd) and
297+
this.getChar([inStart .. inEnd - 1]) = c
298+
)
292299
}
293300

294301
/**
295302
* Gets a mode of this regular expression string if it is defined by a mode prefix.
296303
*/
297304
string getModeFromPrefix() {
298-
exists(string c | this.flagGroupStart(_, _, c) |
305+
exists(string c | this.flagGroup(_, _, c) |
299306
// TODO: are these correct in Swift?
300307
c = "i" and result = "IGNORECASE"
301308
or
@@ -322,10 +329,13 @@ abstract class RegExp extends Expr {
322329
* UNICODECLASS
323330
*/
324331
string getAMode() {
325-
/* TODO
326-
result != "None" and
327-
usedAsRegex(this, result, _)
328-
or*/
332+
/*
333+
* TODO
334+
* result != "None" and
335+
* usedAsRegex(this, result, _)
336+
* or
337+
*/
338+
329339
result = this.getModeFromPrefix()
330340
}
331341

@@ -709,7 +719,7 @@ abstract class RegExp extends Expr {
709719
or
710720
this.simpleGroupStart(start, end)
711721
or
712-
this.flagGroupStart(start, end, _)
722+
this.flagGroupStart(start, end)
713723
}
714724

715725
/** Matches the start of a non-capturing group, e.g. `(?:` */

swift/ql/test/library-tests/regex/parse.expected

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1618,13 +1618,16 @@ redos_variants.swift:
16181618

16191619
# 142| [RegExpConstant, RegExpNormalChar] !
16201620

1621-
# 146| [RegExpZeroWidthMatch] (?s)
1621+
# 146| [RegExpGroup] (?s)
1622+
#-----| 0 -> [RegExpConstant, RegExpNormalChar] s
16221623

16231624
# 146| [RegExpSequence] (?s)(.|\n)*!
1624-
#-----| 0 -> [RegExpZeroWidthMatch] (?s)
1625+
#-----| 0 -> [RegExpGroup] (?s)
16251626
#-----| 1 -> [RegExpStar] (.|\n)*
16261627
#-----| 2 -> [RegExpConstant, RegExpNormalChar] !
16271628

1629+
# 146| [RegExpConstant, RegExpNormalChar] s
1630+
16281631
# 146| [RegExpGroup] (.|\n)
16291632
#-----| 0 -> [RegExpAlt] .|\n
16301633

@@ -6489,30 +6492,36 @@ regex.swift:
64896492

64906493
# 205| [RegExpNamedCharacterProperty] [:aaaaa:]
64916494

6492-
# 209| [RegExpZeroWidthMatch] (?i)
6495+
# 209| [RegExpGroup] (?i)
6496+
#-----| 0 -> [RegExpConstant, RegExpNormalChar] i
64936497

64946498
# 209| [RegExpSequence] (?i)abc
6495-
#-----| 0 -> [RegExpZeroWidthMatch] (?i)
6499+
#-----| 0 -> [RegExpGroup] (?i)
64966500
#-----| 1 -> [RegExpConstant, RegExpNormalChar] abc
64976501

6502+
# 209| [RegExpConstant, RegExpNormalChar] i
6503+
64986504
# 209| [RegExpConstant, RegExpNormalChar] abc
64996505

6500-
# 210| [RegExpZeroWidthMatch] (?s)
6506+
# 210| [RegExpGroup] (?s)
6507+
#-----| 0 -> [RegExpConstant, RegExpNormalChar] s
65016508

65026509
# 210| [RegExpSequence] (?s)abc
6503-
#-----| 0 -> [RegExpZeroWidthMatch] (?s)
6510+
#-----| 0 -> [RegExpGroup] (?s)
65046511
#-----| 1 -> [RegExpConstant, RegExpNormalChar] abc
65056512

6513+
# 210| [RegExpConstant, RegExpNormalChar] s
6514+
65066515
# 210| [RegExpConstant, RegExpNormalChar] abc
65076516

65086517
# 211| [RegExpGroup] (?is)
6509-
#-----| 0 -> [RegExpConstant, RegExpNormalChar] s
6518+
#-----| 0 -> [RegExpConstant, RegExpNormalChar] is
65106519

65116520
# 211| [RegExpSequence] (?is)abc
65126521
#-----| 0 -> [RegExpGroup] (?is)
65136522
#-----| 1 -> [RegExpConstant, RegExpNormalChar] abc
65146523

6515-
# 211| [RegExpConstant, RegExpNormalChar] s
6524+
# 211| [RegExpConstant, RegExpNormalChar] is
65166525

65176526
# 211| [RegExpConstant, RegExpNormalChar] abc
65186527

swift/ql/test/library-tests/regex/regex.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ module RegexTest implements TestSig {
3434
location = eval.getLocation() and
3535
element = eval.toString() and
3636
tag = "modes" and
37-
value = regex.getFlags() and
37+
value = quote(regex.getFlags()) and
3838
value != ""
3939
)
4040
}

swift/ql/test/library-tests/regex/regex.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ func myRegexpMethodsTests(b: Bool, str_unknown: String) throws {
208208

209209
_ = try Regex("(?i)abc").firstMatch(in: input) // $ input=input modes=IGNORECASE regex=(?i)abc
210210
_ = try Regex("(?s)abc").firstMatch(in: input) // $ input=input modes=DOTALL regex=(?s)abc
211-
_ = try Regex("(?is)abc").firstMatch(in: input) // $ input=input regex=(?is)abc MISSING: modes="DOTALL | IGNORECASE" SPURIOUS: modes=IGNORECASE
211+
_ = try Regex("(?is)abc").firstMatch(in: input) // $ input=input modes="DOTALL | IGNORECASE" regex=(?is)abc
212212

213213
_ = try Regex("abc").dotMatchesNewlines(true).firstMatch(in: input) // $ input=input regex=abc MISSING: modes=DOTALL
214214
_ = try Regex("abc").dotMatchesNewlines(false).firstMatch(in: input) // $ input=input regex=abc

0 commit comments

Comments
 (0)