From 7f85e026a63d8b80f20cbe9772a5c4396302a037 Mon Sep 17 00:00:00 2001 From: Ruslan Alikhamov Date: Wed, 20 Dec 2023 01:10:31 +0400 Subject: [PATCH] Support for variadic types as method arguments (#1241) * Added support for variadic types as method arguments * Added changelog entry * added example protocol with varargs * support for vararg in return type's closure * added missing member lookup --- CHANGELOG.md | 1 + Package.resolved | 18 ----------- .../Sources/AST/MethodParameter_Linux.swift | 2 ++ Templates/Templates/AutoMockable.stencil | 20 ++++++++----- Templates/Tests/Context/AutoMockable.swift | 5 ++++ .../Tests/Context_Linux/AutoMockable.swift | 5 ++++ .../Tests/Expected/AutoMockable.expected | 30 ++++++++++++++++++- 7 files changed, 54 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f07cd7b11..2e4096801 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ ([#1224](https://github.com/krzysztofzablocki/Sourcery/pull/1224) - Fix unit tests on Linux ([#1225](https://github.com/krzysztofzablocki/Sourcery/pull/1225)) - Updated XcodeProj to 8.16.0 ([#1228](https://github.com/krzysztofzablocki/Sourcery/pull/1228)) +- Support for variadic arguments as method parameters ([#1222](https://github.com/krzysztofzablocki/Sourcery/issues/1222)) ## 2.1.2 ## Changes diff --git a/Package.resolved b/Package.resolved index 1086cdbcd..6cb06780e 100644 --- a/Package.resolved +++ b/Package.resolved @@ -18,24 +18,6 @@ "version" : "0.9.1" } }, - { - "identity" : "cwlcatchexception", - "kind" : "remoteSourceControl", - "location" : "https://github.com/mattgallagher/CwlCatchException.git", - "state" : { - "revision" : "f809deb30dc5c9d9b78c872e553261a61177721a", - "version" : "2.0.0" - } - }, - { - "identity" : "cwlpreconditiontesting", - "kind" : "remoteSourceControl", - "location" : "https://github.com/mattgallagher/CwlPreconditionTesting.git", - "state" : { - "revision" : "02b7a39a99c4da27abe03cab2053a9034379639f", - "version" : "2.0.0" - } - }, { "identity" : "nimble", "kind" : "remoteSourceControl", diff --git a/SourceryRuntime/Sources/AST/MethodParameter_Linux.swift b/SourceryRuntime/Sources/AST/MethodParameter_Linux.swift index 262c561b3..7dd8c6a9f 100644 --- a/SourceryRuntime/Sources/AST/MethodParameter_Linux.swift +++ b/SourceryRuntime/Sources/AST/MethodParameter_Linux.swift @@ -19,6 +19,8 @@ public class MethodParameter: NSObject, SourceryModel, Typed, Annotated, Diffabl return isClosure case "typeAttributes": return typeAttributes + case "isVariadic": + return isVariadic default: fatalError("unable to lookup: \(member) in \(self)") } diff --git a/Templates/Templates/AutoMockable.stencil b/Templates/Templates/AutoMockable.stencil index 28e17ce47..b0ffb5d3e 100755 --- a/Templates/Templates/AutoMockable.stencil +++ b/Templates/Templates/AutoMockable.stencil @@ -54,7 +54,7 @@ import {{ import }} {% macro closureReturnTypeName method %}{% if method.isOptionalReturnType %}{{ method.unwrappedReturnTypeName }}?{% else %}{{ method.returnTypeName }}{% endif %}{% endmacro %} {% macro methodClosureDeclaration method %} - {% call accessLevel method.accessLevel %}{% call staticSpecifier method %}var {% call methodClosureName method %}: (({% for param in method.parameters %}{% call existentialClosureVariableTypeName param.typeName %}{% if not forloop.last %}, {% endif %}{% endfor %}) {% if method.isAsync %}async {% endif %}{% if method.throws %}throws {% endif %}-> {% if method.isInitializer %}Void{% else %}{% call existentialVariableTypeName method.returnTypeName %}{% endif %})? + {% call accessLevel method.accessLevel %}{% call staticSpecifier method %}var {% call methodClosureName method %}: (({% for param in method.parameters %}{% call existentialClosureVariableTypeName param.typeName param.isVariadic %}{% if not forloop.last %}, {% endif %}{% endfor %}) {% if method.isAsync %}async {% endif %}{% if method.throws %}throws {% endif %}-> {% if method.isInitializer %}Void{% else %}{% call existentialVariableTypeName method.returnTypeName %}{% endif %})? {% endmacro %} {% macro methodClosureCallParameters method %}{% for param in method.parameters %}{{ param.name }}{% if not forloop.last %}, {% endif %}{% endfor %}{% endmacro %} @@ -77,11 +77,11 @@ import {{ import }} {% endfor -%} {% endset %} {% if method.parameters.count == 1 and not hasNonEscapingClosures %} - {% call accessLevel method.accessLevel %}{% call staticSpecifier method %}var {% call swiftifyMethodName method.selectorName %}Received{% for param in method.parameters %}{{ param.name|upperFirstLetter }}: {{ '(' if param.isClosure }}({% call existentialClosureVariableTypeName param.typeName.unwrappedTypeName %}{{ ')' if param.isClosure }})?{% endfor %} - {% call accessLevel method.accessLevel %}{% call staticSpecifier method %}var {% call swiftifyMethodName method.selectorName %}ReceivedInvocations{% for param in method.parameters %}: [{{ '(' if param.isClosure }}({% call existentialClosureVariableTypeName param.typeName.unwrappedTypeName %}){{ ')' if param.isClosure }}{%if param.typeName.isOptional%}?{%endif%}]{% endfor %} = [] + {% call accessLevel method.accessLevel %}{% call staticSpecifier method %}var {% call swiftifyMethodName method.selectorName %}Received{% for param in method.parameters %}{{ param.name|upperFirstLetter }}: {{ '(' if param.isClosure }}({% call existentialClosureVariableTypeName param.typeName.unwrappedTypeName param.isVariadic %}{{ ')' if param.isClosure }})?{% endfor %} + {% call accessLevel method.accessLevel %}{% call staticSpecifier method %}var {% call swiftifyMethodName method.selectorName %}ReceivedInvocations{% for param in method.parameters %}: [{{ '(' if param.isClosure }}({% call existentialClosureVariableTypeName param.typeName.unwrappedTypeName param.isVariadic %}){{ ')' if param.isClosure }}{%if param.typeName.isOptional%}?{%endif%}]{% endfor %} = [] {% elif not method.parameters.count == 0 and not hasNonEscapingClosures %} - {% call accessLevel method.accessLevel %}{% call staticSpecifier method %}var {% call swiftifyMethodName method.selectorName %}ReceivedArguments: ({% for param in method.parameters %}{{ param.name }}: {% if param.typeAttributes.escaping %}{% call existentialClosureVariableTypeName param.typeName.unwrappedTypeName %}{% else %}{% call existentialClosureVariableTypeName param.typeName %}{% endif %}{{ ', ' if not forloop.last }}{% endfor %})? - {% call accessLevel method.accessLevel %}{% call staticSpecifier method %}var {% call swiftifyMethodName method.selectorName %}ReceivedInvocations: [({% for param in method.parameters %}{{ param.name }}: {% if param.typeAttributes.escaping %}{% call existentialClosureVariableTypeName param.typeName.unwrappedTypeName %}{% else %}{% call existentialClosureVariableTypeName param.typeName %}{% endif %}{{ ', ' if not forloop.last }}{% endfor %})] = [] + {% call accessLevel method.accessLevel %}{% call staticSpecifier method %}var {% call swiftifyMethodName method.selectorName %}ReceivedArguments: ({% for param in method.parameters %}{{ param.name }}: {% if param.typeAttributes.escaping %}{% call existentialClosureVariableTypeName param.typeName.unwrappedTypeName param.isVariadic %}{% else %}{% call existentialClosureVariableTypeName param.typeName param.isVariadic %}{% endif %}{{ ', ' if not forloop.last }}{% endfor %})? + {% call accessLevel method.accessLevel %}{% call staticSpecifier method %}var {% call swiftifyMethodName method.selectorName %}ReceivedInvocations: [({% for param in method.parameters %}{{ param.name }}: {% if param.typeAttributes.escaping %}{% call existentialClosureVariableTypeName param.typeName.unwrappedTypeName param.isVariadic %}{% else %}{% call existentialClosureVariableTypeName param.typeName param.isVariadic %}{% endif %}{{ ', ' if not forloop.last }}{% endfor %})] = [] {% endif %} {% if not method.returnTypeName.isVoid and not method.isInitializer %} {% call accessLevel method.accessLevel %}{% call staticSpecifier method %}var {% call swiftifyMethodName method.selectorName %}ReturnValue: {{ '(' if method.returnTypeName.isClosure and not method.isOptionalReturnType or method.returnTypeName|contains:"any "}}{% call existentialVariableTypeName method.returnTypeName %}{{ ')' if method.returnTypeName.isClosure and not method.isOptionalReturnType or method.returnTypeName|contains:"any " }}{{ '!' if not method.isOptionalReturnType }} @@ -221,7 +221,7 @@ import {{ import }} {{ typeName }} {%- endif -%} {%- endmacro %} -{% macro existentialClosureVariableTypeName typeName -%} +{% macro existentialClosureVariableTypeName typeName isVariadic -%} {%- if typeName|contains:"any " and typeName|contains:"!" -%} {{ typeName | replace:"any","(any" | replace:"!",")?" }} {%- elif typeName|contains:"any " and typeName.isClosure and typeName|contains:"?" -%} @@ -234,11 +234,13 @@ import {{ import }} {{ typeName | replace:"some","(any" | replace:"?",")?" }} {%- elif typeName|contains:"some " and typeName|contains:"?" -%} {{ typeName | replace:"some","(any" | replace:"?",")?" }} + {%- elif isVariadic -%} + {{ typeName }}... {%- else -%} {{ typeName|replace:"some ","any " }} {%- endif -%} {%- endmacro %} -{% macro existentialParameterTypeName typeName -%} +{% macro existentialParameterTypeName typeName isVariadic -%} {%- if typeName|contains:"any " and typeName|contains:"?," and typeName|contains:">?" -%} {{ typeName | replace:"any","(any" | replace:"?,",")?," }} {%- elif typeName|contains:"any " and typeName|contains:"!" -%} @@ -253,11 +255,13 @@ import {{ import }} {{ typeName | replace:"some","(some" | replace:"?",")?" }} {%- elif typeName|contains:"some " and typeName.isOptional -%} {{ typeName | replace:"some","(some" | replace:"?",")?" }} + {%- elif isVariadic -%} + {{ typeName }}... {%- else -%} {{ typeName }} {%- endif -%} {%- endmacro %} -{% macro methodName method %}func {{ method.shortName}}({%- for param in method.parameters %}{% if param.argumentLabel == nil %}_ {{ param.name }}{%elif param.argumentLabel == param.name%}{{ param.name }}{%else%}{{ param.argumentLabel }} {{ param.name }}{% endif %}: {% call existentialParameterTypeName param.typeName %}{% if not forloop.last %}, {% endif %}{% endfor -%}){% endmacro %} +{% macro methodName method %}func {{ method.shortName}}({%- for param in method.parameters %}{% if param.argumentLabel == nil %}_ {{ param.name }}{%elif param.argumentLabel == param.name%}{{ param.name }}{%else%}{{ param.argumentLabel }} {{ param.name }}{% endif %}: {% call existentialParameterTypeName param.typeName param.isVariadic %}{% if not forloop.last %}, {% endif %}{% endfor -%}){% endmacro %} {% for type in types.protocols where type.based.AutoMockable or type|annotated:"AutoMockable" %}{% if type.name != "AutoMockable" %} {% call accessLevel type.accessLevel %}class {{ type.name }}Mock: {{ type.name }} { diff --git a/Templates/Tests/Context/AutoMockable.swift b/Templates/Tests/Context/AutoMockable.swift index f1bcfb628..7675d0485 100644 --- a/Templates/Tests/Context/AutoMockable.swift +++ b/Templates/Tests/Context/AutoMockable.swift @@ -210,3 +210,8 @@ protocol HouseProtocol: AutoMockable { var f3Publisher: GenericType? { get } var f4Publisher: GenericType? { get } } + +// sourcery: AutoMockable +protocol ExampleVararg { + func string(key: String, args: CVarArg...) -> String +} diff --git a/Templates/Tests/Context_Linux/AutoMockable.swift b/Templates/Tests/Context_Linux/AutoMockable.swift index f1bcfb628..7675d0485 100644 --- a/Templates/Tests/Context_Linux/AutoMockable.swift +++ b/Templates/Tests/Context_Linux/AutoMockable.swift @@ -210,3 +210,8 @@ protocol HouseProtocol: AutoMockable { var f3Publisher: GenericType? { get } var f4Publisher: GenericType? { get } } + +// sourcery: AutoMockable +protocol ExampleVararg { + func string(key: String, args: CVarArg...) -> String +} diff --git a/Templates/Tests/Expected/AutoMockable.expected b/Templates/Tests/Expected/AutoMockable.expected index b8b566ced..4daa41ebc 100644 --- a/Templates/Tests/Expected/AutoMockable.expected +++ b/Templates/Tests/Expected/AutoMockable.expected @@ -1,4 +1,4 @@ -// Generated using Sourcery 2.0.2 — https://github.com/krzysztofzablocki/Sourcery +// Generated using Sourcery 2.1.2 — https://github.com/krzysztofzablocki/Sourcery // DO NOT EDIT // swiftlint:disable line_length // swiftlint:disable variable_name @@ -663,6 +663,34 @@ class CurrencyPresenterMock: CurrencyPresenter { showSourceCurrencyClosure?(currency) } +} +class ExampleVarargMock: ExampleVararg { + + + + + //MARK: - string + + var stringKeyArgsCallsCount = 0 + var stringKeyArgsCalled: Bool { + return stringKeyArgsCallsCount > 0 + } + var stringKeyArgsReceivedArguments: (key: String, args: CVarArg...)? + var stringKeyArgsReceivedInvocations: [(key: String, args: CVarArg...)] = [] + var stringKeyArgsReturnValue: String! + var stringKeyArgsClosure: ((String, CVarArg...) -> String)? + + func string(key: String, args: CVarArg...) -> String { + stringKeyArgsCallsCount += 1 + stringKeyArgsReceivedArguments = (key: key, args: args) + stringKeyArgsReceivedInvocations.append((key: key, args: args)) + if let stringKeyArgsClosure = stringKeyArgsClosure { + return stringKeyArgsClosure(key, args) + } else { + return stringKeyArgsReturnValue + } + } + } class ExtendableProtocolMock: ExtendableProtocol {