Skip to content

Commit adf5688

Browse files
authored
Don't get stuck on empty matches (#415)
This fixes an issue where calling `matches(of:)` with an pattern that matches an empty substring gets stuck searching the same position over and over.
1 parent 9cf3cfc commit adf5688

File tree

3 files changed

+20
-4
lines changed

3 files changed

+20
-4
lines changed

Sources/_StringProcessing/Algorithms/Matching/Matches.swift

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,14 +213,22 @@ extension BidirectionalCollection where SubSequence == Substring {
213213
let regex = r.regex
214214

215215
var result = [Regex<R.RegexOutput>.Match]()
216-
while start < end {
216+
while start <= end {
217217
guard let match = try? regex._firstMatch(
218218
slice.base, in: start..<end
219219
) else {
220220
break
221221
}
222222
result.append(match)
223-
start = match.range.upperBound
223+
if match.range.isEmpty {
224+
if match.range.upperBound == end {
225+
break
226+
}
227+
// FIXME: semantic level
228+
start = slice.index(after: match.range.upperBound)
229+
} else {
230+
start = match.range.upperBound
231+
}
224232
}
225233
return result
226234
}

Sources/_StringProcessing/Regex/Match.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,13 +154,13 @@ extension Regex {
154154

155155
var low = inputRange.lowerBound
156156
let high = inputRange.upperBound
157-
while low < high {
157+
while true {
158158
if let m = try _match(input, in: low..<high, mode: .partialFromFront) {
159159
return m
160160
}
161+
if low == high { return nil }
161162
input.formIndex(after: &low)
162163
}
163-
return nil
164164
}
165165
}
166166

Tests/RegexTests/AlgorithmsTests.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ class AlgorithmTests: XCTestCase {
6767
let actualCol: [Range<Int>] = string[...].ranges(of: regex)[...].map(string.offsets(of:))
6868
XCTAssertEqual(actualCol, expected, file: file, line: line)
6969

70+
let matchRanges = string.matches(of: regex).map { string.offsets(of: $0.range) }
71+
XCTAssertEqual(matchRanges, expected, file: file, line: line)
72+
7073
let firstRange = string.firstRange(of: regex).map(string.offsets(of:))
7174
XCTAssertEqual(firstRange, expected.first, file: file, line: line)
7275
}
@@ -332,6 +335,11 @@ class AlgorithmTests: XCTestCase {
332335
XCTAssertEqual(
333336
s2.matches(of: regex).map(\.0),
334337
["aa"])
338+
339+
XCTAssertEqual(
340+
s2.matches(of: try Regex("a*?")).map { s2.offsets(of: $0.range) }, [0..<0, 1..<1, 2..<2])
341+
XCTAssertEqual(
342+
s2.ranges(of: try Regex("a*?")).map(s2.offsets(of:)), [0..<0, 1..<1, 2..<2])
335343
}
336344

337345
func testSwitches() {

0 commit comments

Comments
 (0)