@@ -26,23 +26,33 @@ func _firstMatch(
26
26
validateOptimizations: Bool ,
27
27
semanticLevel: RegexSemanticLevel = . graphemeCluster,
28
28
syntax: SyntaxOptions = . traditional
29
- ) throws -> ( String , [ String ? ] ) {
29
+ ) throws -> ( String , [ String ? ] ) ? {
30
30
var regex = try Regex ( regexStr, syntax: syntax) . matchingSemantics ( semanticLevel)
31
- guard let result = try regex. firstMatch ( in: input) else {
32
- throw MatchError ( " match not found for \( regexStr) in \( input) " )
33
- }
34
- let caps = result. output. slices ( from: input)
35
-
31
+ let result = try regex. firstMatch ( in: input)
32
+
36
33
if validateOptimizations {
37
34
regex. _setCompilerOptionsForTesting ( . disableOptimizations)
38
- guard let unoptResult = try regex. firstMatch ( in: input) else {
35
+ let unoptResult = try regex. firstMatch ( in: input)
36
+ if result != nil && unoptResult == nil {
39
37
throw MatchError ( " match not found for unoptimized \( regexStr) in \( input) " )
40
38
}
41
- XCTAssertEqual (
42
- String ( input [ result. range] ) ,
43
- String ( input [ unoptResult. range] ) ,
44
- " Unoptimized regex returned a different result " )
39
+ if result == nil && unoptResult != nil {
40
+ throw MatchError ( " match not found in optimized \( regexStr) in \( input) " )
41
+ }
42
+ if let result = result, let unoptResult = unoptResult {
43
+ let optMatch = String ( input [ result. range] )
44
+ let unoptMatch = String ( input [ unoptResult. range] )
45
+ if optMatch != unoptMatch {
46
+ throw MatchError ( """
47
+
48
+ Unoptimized regex returned: ' \( unoptMatch) '
49
+ Optimized regex returned: ' \( optMatch) '
50
+ """ )
51
+ }
52
+ }
45
53
}
54
+ guard let result = result else { return nil }
55
+ let caps = result. output. slices ( from: input)
46
56
return ( String ( input [ result. range] ) , caps. map { $0. map ( String . init) } )
47
57
}
48
58
@@ -153,22 +163,20 @@ func firstMatchTest(
153
163
line: UInt = #line
154
164
) {
155
165
do {
156
- let ( found, _ ) = try _firstMatch (
166
+ let found = try _firstMatch (
157
167
regex,
158
168
input: input,
159
169
validateOptimizations: validateOptimizations,
160
170
semanticLevel: semanticLevel,
161
- syntax: syntax)
171
+ syntax: syntax) ? . 0
162
172
163
173
if xfail {
164
174
XCTAssertNotEqual ( found, match, file: file, line: line)
165
175
} else {
166
176
XCTAssertEqual ( found, match, " Incorrect match " , file: file, line: line)
167
177
}
168
178
} catch {
169
- // FIXME: This allows non-matches to succeed even when xfail'd
170
- // When xfail == true, this should report failure for match == nil
171
- if !xfail && match != nil {
179
+ if !xfail {
172
180
XCTFail ( " \( error) " , file: file, line: line)
173
181
}
174
182
return
@@ -428,8 +436,7 @@ extension RegexTests {
428
436
" a++a " ,
429
437
( " babc " , nil ) ,
430
438
( " baaabc " , nil ) ,
431
- ( " bb " , nil ) ,
432
- xfail: true )
439
+ ( " bb " , nil ) )
433
440
firstMatchTests (
434
441
" a+?a " ,
435
442
( " babc " , nil ) ,
@@ -505,23 +512,19 @@ extension RegexTests {
505
512
( " baabc " , nil ) ,
506
513
( " bb " , nil ) )
507
514
508
- // XFAIL'd versions of the above
509
515
firstMatchTests (
510
516
" a{2,4}+a " ,
511
- ( " baaabc " , nil ) ,
512
- xfail: true )
517
+ ( " baaabc " , nil ) )
513
518
firstMatchTests (
514
519
" a{,4}+a " ,
515
520
( " babc " , nil ) ,
516
521
( " baabc " , nil ) ,
517
- ( " baaabc " , nil ) ,
518
- xfail: true )
522
+ ( " baaabc " , nil ) )
519
523
firstMatchTests (
520
524
" a{2,}+a " ,
521
525
( " baaabc " , nil ) ,
522
526
( " baaaaabc " , nil ) ,
523
- ( " baaaaaaaabc " , nil ) ,
524
- xfail: true )
527
+ ( " baaaaaaaabc " , nil ) )
525
528
526
529
// XFAIL'd possessive tests
527
530
firstMatchTests (
@@ -773,6 +776,11 @@ extension RegexTests {
773
776
}
774
777
firstMatchTest ( #"[\t-\t]"# , input: " \u{8} \u{A} \u{9} " , match: " \u{9} " )
775
778
779
+ // FIXME: This produces a different result with and without optimizations.
780
+ firstMatchTest ( #"[1-2]"# , input: " 1️⃣ " , match: nil , xfail: true )
781
+ firstMatchTest ( #"[1-2]"# , input: " 1️⃣ " , match: nil ,
782
+ validateOptimizations: false )
783
+
776
784
// Currently not supported in the matching engine.
777
785
for c : UnicodeScalar in [ " a " , " b " , " c " ] {
778
786
firstMatchTest ( #"[\c!-\C-#]"# , input: " def \( c) " , match: " \( c) " ,
@@ -1118,8 +1126,8 @@ extension RegexTests {
1118
1126
// TODO: Oniguruma \y and \Y
1119
1127
firstMatchTests (
1120
1128
#"\u{65}"# , // Scalar 'e' is present in both
1121
- ( " Cafe \u{301} " , nil ) , // but scalar mode requires boundary at end of match
1122
- xfail : true )
1129
+ ( " Cafe \u{301} " , nil ) ) // but scalar mode requires boundary at end of match
1130
+
1123
1131
firstMatchTests (
1124
1132
#"\u{65}"# , // Scalar 'e' is present in both
1125
1133
( " Sol Cafe " , " e " ) ) // standalone is okay
@@ -1711,19 +1719,15 @@ extension RegexTests {
1711
1719
firstMatchTest ( #"\u{65 301}$"# , input: eComposed, match: eComposed)
1712
1720
1713
1721
// FIXME: Implicit \y at end of match
1714
- firstMatchTest ( #"\u{65}"# , input: eDecomposed, match: nil ,
1715
- xfail: true )
1722
+ firstMatchTest ( #"\u{65}"# , input: eDecomposed, match: nil )
1716
1723
firstMatchTest ( #"\u{65}$"# , input: eDecomposed, match: nil )
1717
- // FIXME: \y is unsupported
1718
- firstMatchTest ( #"\u{65}\y"# , input: eDecomposed, match: nil ,
1719
- xfail: true )
1724
+ firstMatchTest ( #"\u{65}\y"# , input: eDecomposed, match: nil )
1720
1725
1721
1726
// FIXME: Unicode scalars are only matched at the start of a grapheme cluster
1722
1727
firstMatchTest ( #"\u{301}"# , input: eDecomposed, match: " \u{301} " ,
1723
1728
xfail: true )
1724
- // FIXME: \y is unsupported
1725
- firstMatchTest ( #"\y\u{301}"# , input: eDecomposed, match: nil ,
1726
- xfail: true )
1729
+
1730
+ firstMatchTest ( #"\y\u{301}"# , input: eDecomposed, match: nil )
1727
1731
}
1728
1732
1729
1733
func testCanonicalEquivalence( ) throws {
@@ -1781,13 +1785,11 @@ extension RegexTests {
1781
1785
// \s
1782
1786
firstMatchTest ( #"\s"# , input: " " , match: " " )
1783
1787
// FIXME: \s shouldn't match a number composed with a non-number character
1784
- firstMatchTest ( #"\s\u{305}"# , input: " " , match: nil ,
1785
- xfail: true )
1788
+ firstMatchTest ( #"\s\u{305}"# , input: " " , match: nil )
1786
1789
// \p{Whitespace}
1787
1790
firstMatchTest ( #"\s"# , input: " " , match: " " )
1788
- // FIXME: \p{Whitespace} shouldn't match whitespace composed with a non-whitespace character
1789
- firstMatchTest ( #"\s\u{305}"# , input: " " , match: nil ,
1790
- xfail: true )
1791
+ // \p{Whitespace} shouldn't match whitespace composed with a non-whitespace character
1792
+ firstMatchTest ( #"\s\u{305}"# , input: " " , match: nil )
1791
1793
}
1792
1794
1793
1795
func testCanonicalEquivalenceCustomCharacterClass( ) throws {
0 commit comments