From e6f38966d13dcef90e7b640e7c09daa29aff77a8 Mon Sep 17 00:00:00 2001 From: Richard Wei <rxwei@apple.com> Date: Mon, 20 Jun 2022 00:13:31 -0700 Subject: [PATCH] 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<R>(first: R)` return a `Regex<R.RegexOutput>` rather than `R` itself. --- Before: ```swift // error: cannot convert value of type 'OneOrMore<Substring>' to specified type 'Regex<Substring>' @RegexComponentBuilder var r: Regex<Substring> { OneOrMore("a") // Adding other components below will make the error go away. } struct MyCustomRegex: RegexComponent { // error: cannot convert value of type 'OneOrMore<Substring>' to specified type 'Regex<Substring>' var regex: Regex<Substring> { 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 a50f069ec..dfafd3803 100644 --- a/Sources/RegexBuilder/Builder.swift +++ b/Sources/RegexBuilder/Builder.swift @@ -18,8 +18,10 @@ public enum RegexComponentBuilder { .init(node: .empty) } - public static func buildPartialBlock<R: RegexComponent>(first: R ) -> R { - first + public static func buildPartialBlock<R: RegexComponent>( + first component: R + ) -> Regex<R.RegexOutput> { + component.regex } public static func buildExpression<R: RegexComponent>(_ regex: R) -> R { diff --git a/Tests/RegexBuilderTests/RegexDSLTests.swift b/Tests/RegexBuilderTests/RegexDSLTests.swift index 5b3914e3b..fc31e575f 100644 --- a/Tests/RegexBuilderTests/RegexDSLTests.swift +++ b/Tests/RegexBuilderTests/RegexDSLTests.swift @@ -1015,7 +1015,7 @@ class RegexDSLTests: XCTestCase { XCTAssertEqual(str.wholeMatch(of: parser)?.1, version) } } - + func testZeroWidthConsumer() throws { struct Trace: CustomConsumingRegexComponent { typealias RegexOutput = Void @@ -1051,6 +1051,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<Substring> { + OneOrMore("a") + } + @RegexComponentBuilder + var multiComponent: Regex<Substring> { + OneOrMore("a") + "b" + } + struct MyCustomRegex: RegexComponent { + @RegexComponentBuilder + var regex: Regex<Substring> { + OneOrMore("a") + } + } + } } extension Unicode.Scalar {