From 69c375f27e42a9f1e613adf6e5130008fc26cc42 Mon Sep 17 00:00:00 2001 From: Richard Wei Date: Mon, 20 Jun 2022 00:13:31 -0700 Subject: [PATCH 1/3] Make unary builder return `Regex` type consistently. Currently, unary regex component builder simply forwards the component's base type. However, this is inconsistent with non-unary builder results. The current behavior may lead to surprising results when the user marks a property with `@RegexComponentBuilder`. This patch makes `RegexComponentBuilder.buildPartialBlock(first: R)` return a `Regex` rather than `R` itself. --- Before: ```swift // error: cannot convert value of type 'OneOrMore' to specified type 'Regex' @RegexComponentBuilder var r: Regex { OneOrMore("a") // Adding other components below will make the error go away. } struct MyCustomRegex: RegexComponent { // error: cannot convert value of type 'OneOrMore' to specified type 'Regex' var regex: Regex { OneOrMore("a") } } ``` After: No errors. --- Sources/RegexBuilder/Builder.swift | 6 ++++-- Tests/RegexBuilderTests/RegexDSLTests.swift | 22 ++++++++++++++++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/Sources/RegexBuilder/Builder.swift b/Sources/RegexBuilder/Builder.swift index e6bbd4e26..7afe254c2 100644 --- a/Sources/RegexBuilder/Builder.swift +++ b/Sources/RegexBuilder/Builder.swift @@ -18,8 +18,10 @@ public enum RegexComponentBuilder { _RegexFactory().empty() } - public static func buildPartialBlock(first: R ) -> R { - first + public static func buildPartialBlock( + first component: R + ) -> Regex { + component.regex } public static func buildExpression(_ regex: R) -> R { diff --git a/Tests/RegexBuilderTests/RegexDSLTests.swift b/Tests/RegexBuilderTests/RegexDSLTests.swift index 898f50b6e..24d6f219e 100644 --- a/Tests/RegexBuilderTests/RegexDSLTests.swift +++ b/Tests/RegexBuilderTests/RegexDSLTests.swift @@ -1052,7 +1052,7 @@ class RegexDSLTests: XCTestCase { XCTAssertEqual(str.wholeMatch(of: parser)?.1, version) } } - + func testZeroWidthConsumer() throws { struct Trace: CustomConsumingRegexComponent { typealias RegexOutput = Void @@ -1088,6 +1088,26 @@ class RegexDSLTests: XCTestCase { """) } + + func testRegexComponentBuilderResultType() { + // Test that the user can declare a closure or computed property marked with + // `@RegexComponentBuilder` with `Regex` as the result type. + @RegexComponentBuilder + var unaryWithSingleNonRegex: Regex { + OneOrMore("a") + } + @RegexComponentBuilder + var multiComponent: Regex { + OneOrMore("a") + "b" + } + struct MyCustomRegex: RegexComponent { + @RegexComponentBuilder + var regex: Regex { + OneOrMore("a") + } + } + } } extension Unicode.Scalar { From 2c071bed9b4c8a4a6d68d7c0760fb482a521cc4f Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Tue, 17 May 2022 12:31:01 -0500 Subject: [PATCH 2/3] Keep substring bounds when searching in Regex.wholeMatch --- Sources/_StringProcessing/Regex/Match.swift | 2 +- Tests/RegexBuilderTests/AlgorithmsTests.swift | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Sources/_StringProcessing/Regex/Match.swift b/Sources/_StringProcessing/Regex/Match.swift index 6ccfd6a8e..0fb4e0eb8 100644 --- a/Sources/_StringProcessing/Regex/Match.swift +++ b/Sources/_StringProcessing/Regex/Match.swift @@ -164,7 +164,7 @@ extension BidirectionalCollection where SubSequence == Substring { public func wholeMatch( of r: R ) -> Regex.Match? { - try? r.regex.wholeMatch(in: self[...].base) + try? r.regex.wholeMatch(in: self[...]) } /// Checks for a match against the string, starting at its beginning. diff --git a/Tests/RegexBuilderTests/AlgorithmsTests.swift b/Tests/RegexBuilderTests/AlgorithmsTests.swift index 0a2e6bc21..a7d41b3ed 100644 --- a/Tests/RegexBuilderTests/AlgorithmsTests.swift +++ b/Tests/RegexBuilderTests/AlgorithmsTests.swift @@ -170,6 +170,16 @@ class AlgorithmsResultBuilderTests: XCTestCase { } func testMatches() throws { + do { + let regex = Regex { OneOrMore(.any) } + XCTAssertEqual("abc".wholeMatch(of: regex)!.0, "abc") + XCTAssertEqual("abc".prefixMatch(of: regex)!.0, "abc") + XCTAssertEqual("abc".firstMatch(of: regex)!.0, "abc") + XCTAssertEqual("abc".suffix(1).wholeMatch(of: regex)!.0, "c") + XCTAssertEqual("abc".suffix(1).prefixMatch(of: regex)!.0, "c") + XCTAssertEqual("abc".suffix(1).firstMatch(of: regex)!.0, "c") + } + let int = Capture(OneOrMore(.digit)) { Int($0)! } // Test syntax From 4b5d1f309916a61964927a2cdf26bc88536bd6d2 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Tue, 31 May 2022 15:12:42 +0100 Subject: [PATCH 3/3] Remove outdated comment rdar://92459215 has been fixed. --- Sources/RegexBuilder/Algorithms.swift | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Sources/RegexBuilder/Algorithms.swift b/Sources/RegexBuilder/Algorithms.swift index f809e85f5..19d3506b8 100644 --- a/Sources/RegexBuilder/Algorithms.swift +++ b/Sources/RegexBuilder/Algorithms.swift @@ -11,11 +11,6 @@ @_spi(RegexBuilder) import _StringProcessing -// FIXME(rdar://92459215): We should be using 'some RegexComponent' instead of -// for the methods below that don't impose any additional -// requirements on the type. Currently the generic parameter is needed to work -// around a compiler issue. - extension BidirectionalCollection where SubSequence == Substring { /// Matches a regex in its entirety, where the regex is created by /// the given closure.