From 199c2ef57711e00145db63ea125c0711eb18e6a0 Mon Sep 17 00:00:00 2001 From: fummicc1 Date: Sun, 30 Jun 2024 11:39:28 +0900 Subject: [PATCH 01/27] Update swift-syntax to 600.0.0-prerelease --- Package.swift | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Package.swift b/Package.swift index 358fc93a..ccb06c86 100644 --- a/Package.swift +++ b/Package.swift @@ -11,8 +11,14 @@ let package = Package( .library(name: "MockoloFramework", targets: ["MockoloFramework"]), ], dependencies: [ - .package(url: "https://github.com/apple/swift-syntax.git", from: "510.0.1"), - .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.2.2"), + .package( + url: "https://github.com/swiftlang/swift-syntax", + from: "600.0.0-prerelease-2024-06-12" + ), + .package( + url: "https://github.com/apple/swift-argument-parser", + from: "1.2.2" + ), ], targets: [ .executableTarget( From aa0e097da09306bfc29e0347941fc3431ca99674 Mon Sep 17 00:00:00 2001 From: fummicc1 Date: Sun, 30 Jun 2024 11:40:01 +0900 Subject: [PATCH 02/27] Resolve warnings for SwiftSyntax600 --- Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift b/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift index 790ad6ca..6cbae4bb 100644 --- a/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift +++ b/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift @@ -432,7 +432,7 @@ extension FunctionDeclSyntax { genericTypeParams: genericTypeParams, genericWhereClause: genericWhereClause, params: params, - throwsOrRethrows: self.signature.effectSpecifiers?.throwsSpecifier?.text, + throwsOrRethrows: self.signature.effectSpecifiers?.throwsClause?.throwsSpecifier.text, asyncOrReasync: self.signature.effectSpecifiers?.asyncSpecifier?.text, isStatic: isStatic, offset: self.offset, @@ -473,7 +473,7 @@ extension InitializerDeclSyntax { genericTypeParams: genericTypeParams, genericWhereClause: genericWhereClause, params: params, - throwsOrRethrows: self.signature.effectSpecifiers?.throwsSpecifier?.text, + throwsOrRethrows: self.signature.effectSpecifiers?.throwsClause?.throwsSpecifier.text, asyncOrReasync: self.signature.effectSpecifiers?.asyncSpecifier?.text, isStatic: false, offset: self.offset, From 2e7259bd5d03f5ac8f9399a63056da007a382a31 Mon Sep 17 00:00:00 2001 From: fummicc1 Date: Sun, 30 Jun 2024 11:42:39 +0900 Subject: [PATCH 03/27] Update for tests --- Package.swift | 2 +- .../TestFuncThrow/FixtureFuncThrow.swift | 74 ++++++++++++++++--- .../TestFuncThrow/FuncThrowTests.swift | 7 ++ Tests/TestInit/FixtureInit.swift | 20 +++++ Tests/TestInit/InitTests.swift | 7 ++ 5 files changed, 99 insertions(+), 11 deletions(-) diff --git a/Package.swift b/Package.swift index ccb06c86..ac8cec53 100644 --- a/Package.swift +++ b/Package.swift @@ -4,7 +4,7 @@ import PackageDescription let package = Package( name: "Mockolo", platforms: [ - .macOS(.v12), + .macOS(.v13), ], products: [ .executable(name: "mockolo", targets: ["Mockolo"]), diff --git a/Tests/TestFuncs/TestFuncThrow/FixtureFuncThrow.swift b/Tests/TestFuncs/TestFuncThrow/FixtureFuncThrow.swift index 963d316c..ec37388d 100644 --- a/Tests/TestFuncs/TestFuncThrow/FixtureFuncThrow.swift +++ b/Tests/TestFuncs/TestFuncThrow/FixtureFuncThrow.swift @@ -1,16 +1,6 @@ import MockoloFramework -let funcThrow2 = """ -import Foundation - -/// \(String.mockAnnotation) -protocol FuncThrow { -func update(arg1: T, arg2: () throws -> T) rethrows -> T -func update(arg1: T, arg2: @escaping (U) throws -> ()) throws -> ((T) -> (U)) -} -""" - let funcThrow = """ import Foundation @@ -109,4 +99,68 @@ class FuncThrowMock: FuncThrow { } +""" + +let funcTypedThrow = """ +import Foundation + +/// \(String.mockAnnotation) +protocol FuncTypedThrow { + func f1(arg: Int) throws(any LocalizedError) -> String + func f2(arg: Int) throws(any LocalizedError) + func f3(arg: Int) throws(SomeError) -> String + func f4(arg: Int) throws(SomeError) +} +""" +let funcTypedThrowMock = """ + +import Foundation + + +class FuncTypedThrowMock: FuncTypedThrow { + init() { } + + + private(set) var f1CallCount = 0 + var f1Handler: ((Int) throws(any LocalizedError) -> (String))? + func f1(arg: Int) throws(any LocalizedError) -> String { + f1CallCount += 1 + if let f1Handler = f1Handler { + return try f1Handler(arg) + } + return "" + } + + private(set) var f2CallCount = 0 + var f2Handler: ((Int) throws(any LocalizedError) -> ())? + func f2(arg: Int) throws(any LocalizedError) { + f2CallCount += 1 + if let f2Handler = f2Handler { + try f2Handler(arg) + } + + } + + private(set) var f3CallCount = 0 + var f3Handler: ((Int) throws(SomeError) -> (String))? + func f3(arg: Int) throws(SomeError) -> String { + f3CallCount += 1 + if let f3Handler = f3Handler { + return try f3Handler(arg) + } + return "" + } + + private(set) var f4CallCount = 0 + var f4Handler: ((Int) throws(SomeError) -> ())? + func f4(arg: Int) throws(SomeError) { + f4CallCount += 1 + if let f4Handler = f4Handler { + try f4Handler(arg) + } + + } +} + + """ diff --git a/Tests/TestFuncs/TestFuncThrow/FuncThrowTests.swift b/Tests/TestFuncs/TestFuncThrow/FuncThrowTests.swift index a1f0ef53..6856f00b 100644 --- a/Tests/TestFuncs/TestFuncThrow/FuncThrowTests.swift +++ b/Tests/TestFuncs/TestFuncThrow/FuncThrowTests.swift @@ -5,4 +5,11 @@ class FuncThrowTests: MockoloTestCase { verify(srcContent: funcThrow, dstContent: funcThrowMock) } + + func testTypedThrows() { + verify( + srcContent: funcTypedThrow, + dstContent: funcTypedThrowMock + ) + } } diff --git a/Tests/TestInit/FixtureInit.swift b/Tests/TestInit/FixtureInit.swift index 161d1fe3..56710793 100644 --- a/Tests/TestInit/FixtureInit.swift +++ b/Tests/TestInit/FixtureInit.swift @@ -326,3 +326,23 @@ class MyProtocolMock: MyProtocol { } """ + +let throwableInit = """ +/// \(String.mockAnnotation) +protocol MyProtocol { + init(param: String) throws(SomeError) +} +""" + +let throwableInitMock = """ + + + +class MyProtocolMock: MyProtocol { + private var _param: String! + init() { } + required init(param: String = "") throws(SomeError) { + self._param = param + } +} +""" diff --git a/Tests/TestInit/InitTests.swift b/Tests/TestInit/InitTests.swift index a3c2af90..f5d8af86 100644 --- a/Tests/TestInit/InitTests.swift +++ b/Tests/TestInit/InitTests.swift @@ -41,4 +41,11 @@ class InitTests: MockoloTestCase { dstContent: initWithSameParamNameButDifferentTypeMock ) } + + func testThrowableInit() { + verify( + srcContent: throwableInit, + dstContent: throwableInitMock + ) + } } From 725c67d57ff6fda6d219e4c5d28d6a216cd83879 Mon Sep 17 00:00:00 2001 From: fummicc1 Date: Sun, 30 Jun 2024 16:14:54 +0900 Subject: [PATCH 04/27] update to support typed-throw --- Package.swift | 12 +-- .../Parsers/SwiftSyntaxExtensions.swift | 15 +++- .../Utils/StringExtensions.swift | 4 +- .../MockoloFramework/Utils/TypeParser.swift | 30 ++++++- .../TestFuncThrow/FixtureFuncThrow.swift | 86 +++++-------------- .../TestFuncThrow/FuncThrowTests.swift | 7 -- Tests/TestInit/FixtureInit.swift | 22 ++++- Tests/TestInit/InitTests.swift | 19 ++-- 8 files changed, 103 insertions(+), 92 deletions(-) diff --git a/Package.swift b/Package.swift index ac8cec53..88c276b7 100644 --- a/Package.swift +++ b/Package.swift @@ -4,21 +4,15 @@ import PackageDescription let package = Package( name: "Mockolo", platforms: [ - .macOS(.v13), + .macOS(.v12), ], products: [ .executable(name: "mockolo", targets: ["Mockolo"]), .library(name: "MockoloFramework", targets: ["MockoloFramework"]), ], dependencies: [ - .package( - url: "https://github.com/swiftlang/swift-syntax", - from: "600.0.0-prerelease-2024-06-12" - ), - .package( - url: "https://github.com/apple/swift-argument-parser", - from: "1.2.2" - ), + .package(url: "https://github.com/swiftlang/swift-syntax.git", from: "600.0.0-prerelease-2024-06-12"), + .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.2.2"), ], targets: [ .executableTarget( diff --git a/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift b/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift index 6cbae4bb..ddfe8f1a 100644 --- a/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift +++ b/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift @@ -432,7 +432,7 @@ extension FunctionDeclSyntax { genericTypeParams: genericTypeParams, genericWhereClause: genericWhereClause, params: params, - throwsOrRethrows: self.signature.effectSpecifiers?.throwsClause?.throwsSpecifier.text, + throwsOrRethrows: self.signature.effectSpecifiers?.throwsClause?.text, asyncOrReasync: self.signature.effectSpecifiers?.asyncSpecifier?.text, isStatic: isStatic, offset: self.offset, @@ -473,7 +473,7 @@ extension InitializerDeclSyntax { genericTypeParams: genericTypeParams, genericWhereClause: genericWhereClause, params: params, - throwsOrRethrows: self.signature.effectSpecifiers?.throwsClause?.throwsSpecifier.text, + throwsOrRethrows: self.signature.effectSpecifiers?.throwsClause?.text, asyncOrReasync: self.signature.effectSpecifiers?.asyncSpecifier?.text, isStatic: false, offset: self.offset, @@ -570,6 +570,17 @@ extension TypeAliasDeclSyntax { } } +extension ThrowsClauseSyntax { + + var text: String { + if let type { + "\(throwsSpecifier.text)(\(type.description))" + } else { + throwsSpecifier.text + } + } +} + final class EntityVisitor: SyntaxVisitor { var entities: [Entity] = [] var imports: [String: [String]] = [:] diff --git a/Sources/MockoloFramework/Utils/StringExtensions.swift b/Sources/MockoloFramework/Utils/StringExtensions.swift index 4e694ee3..b95aa3fe 100644 --- a/Sources/MockoloFramework/Utils/StringExtensions.swift +++ b/Sources/MockoloFramework/Utils/StringExtensions.swift @@ -108,10 +108,10 @@ extension String { /// """ - var hasThrowsOrRethrows: Bool { return components(separatedBy: .whitespaces).contains { component in - return component == .throws || component == .rethrows + let hasTypedThrow = hasPrefix(String.throws.withLeftParen) && hasSuffix(")") + return component == .throws || hasTypedThrow || component == .rethrows } } diff --git a/Sources/MockoloFramework/Utils/TypeParser.swift b/Sources/MockoloFramework/Utils/TypeParser.swift index 4b89b3d1..caf34358 100644 --- a/Sources/MockoloFramework/Utils/TypeParser.swift +++ b/Sources/MockoloFramework/Utils/TypeParser.swift @@ -510,6 +510,25 @@ public final class `Type` { return mutableArg } + /// Parse throws clause from suffix and return typed-throw's type. + /// + /// - Returns: if typed-throw is used, returns its concrete type, unless returns nil. + static func extractTypedThrow( + suffix: String + ) -> String? { + return suffix + .components(separatedBy: .whitespaces) + .compactMap { clause in + guard let prefixRange = clause.range(of: "\(String.throws)(") else { + return nil + } + let endIndex = clause.dropLast().endIndex + return String( + clause[prefixRange.upperBound.. Type { @@ -558,9 +577,18 @@ public final class `Type` { displayableReturnType = "(\(displayableReturnType))" } + let hasThrowsOrRethrows = suffix.hasThrowsOrRethrows + let typedThrowTypeName = extractTypedThrow(suffix: suffix) + + let thrownSuffix: String = if let typedThrowTypeName { + "\(String.throws)(\(typedThrowTypeName))" + } else { + String.throws + } + let suffixStr = [ suffix.hasAsync ? String.async + " " : nil, - suffix.hasThrowsOrRethrows ? String.throws + " " : nil, + hasThrowsOrRethrows ? thrownSuffix + " " : nil, ].compactMap { $0 }.joined() let typeStr = "((\(displayableParamStr)) \(suffixStr)-> \(displayableReturnType))?" diff --git a/Tests/TestFuncs/TestFuncThrow/FixtureFuncThrow.swift b/Tests/TestFuncs/TestFuncThrow/FixtureFuncThrow.swift index ec37388d..587f3ccc 100644 --- a/Tests/TestFuncs/TestFuncThrow/FixtureFuncThrow.swift +++ b/Tests/TestFuncs/TestFuncThrow/FixtureFuncThrow.swift @@ -8,6 +8,8 @@ import Foundation protocol FuncThrow { func f1(arg: Int) throws -> String func f2(arg: Int) throws + func f3(arg: Int) throws(SomeError) + func f4(arg: Int) throws(SomeError) -> String func g1(arg: (Int) throws -> ()) throws -> String func g2(arg: (Int) throws -> ()) throws @@ -47,6 +49,26 @@ class FuncThrowMock: FuncThrow { } + private(set) var f3CallCount = 0 + var f3Handler: ((Int) throws(SomeError) -> ())? + func f3(arg: Int) throws(SomeError) { + f3CallCount += 1 + if let f3Handler = f3Handler { + try f3Handler(arg) + } + + } + + private(set) var f4CallCount = 0 + var f4Handler: ((Int) throws(SomeError) -> (String))? + func f4(arg: Int) throws(SomeError) -> String { + f4CallCount += 1 + if let f4Handler = f4Handler { + return try f4Handler(arg) + } + return "" + } + private(set) var g1CallCount = 0 var g1Handler: (((Int) throws -> ()) throws -> (String))? func g1(arg: (Int) throws -> ()) throws -> String { @@ -99,68 +121,4 @@ class FuncThrowMock: FuncThrow { } -""" - -let funcTypedThrow = """ -import Foundation - -/// \(String.mockAnnotation) -protocol FuncTypedThrow { - func f1(arg: Int) throws(any LocalizedError) -> String - func f2(arg: Int) throws(any LocalizedError) - func f3(arg: Int) throws(SomeError) -> String - func f4(arg: Int) throws(SomeError) -} -""" -let funcTypedThrowMock = """ - -import Foundation - - -class FuncTypedThrowMock: FuncTypedThrow { - init() { } - - - private(set) var f1CallCount = 0 - var f1Handler: ((Int) throws(any LocalizedError) -> (String))? - func f1(arg: Int) throws(any LocalizedError) -> String { - f1CallCount += 1 - if let f1Handler = f1Handler { - return try f1Handler(arg) - } - return "" - } - - private(set) var f2CallCount = 0 - var f2Handler: ((Int) throws(any LocalizedError) -> ())? - func f2(arg: Int) throws(any LocalizedError) { - f2CallCount += 1 - if let f2Handler = f2Handler { - try f2Handler(arg) - } - - } - - private(set) var f3CallCount = 0 - var f3Handler: ((Int) throws(SomeError) -> (String))? - func f3(arg: Int) throws(SomeError) -> String { - f3CallCount += 1 - if let f3Handler = f3Handler { - return try f3Handler(arg) - } - return "" - } - - private(set) var f4CallCount = 0 - var f4Handler: ((Int) throws(SomeError) -> ())? - func f4(arg: Int) throws(SomeError) { - f4CallCount += 1 - if let f4Handler = f4Handler { - try f4Handler(arg) - } - - } -} - - """ diff --git a/Tests/TestFuncs/TestFuncThrow/FuncThrowTests.swift b/Tests/TestFuncs/TestFuncThrow/FuncThrowTests.swift index 6856f00b..a1f0ef53 100644 --- a/Tests/TestFuncs/TestFuncThrow/FuncThrowTests.swift +++ b/Tests/TestFuncs/TestFuncThrow/FuncThrowTests.swift @@ -5,11 +5,4 @@ class FuncThrowTests: MockoloTestCase { verify(srcContent: funcThrow, dstContent: funcThrowMock) } - - func testTypedThrows() { - verify( - srcContent: funcTypedThrow, - dstContent: funcTypedThrowMock - ) - } } diff --git a/Tests/TestInit/FixtureInit.swift b/Tests/TestInit/FixtureInit.swift index 56710793..eda4ff79 100644 --- a/Tests/TestInit/FixtureInit.swift +++ b/Tests/TestInit/FixtureInit.swift @@ -330,7 +330,7 @@ class MyProtocolMock: MyProtocol { let throwableInit = """ /// \(String.mockAnnotation) protocol MyProtocol { - init(param: String) throws(SomeError) + init(param: String) throws } """ @@ -338,6 +338,26 @@ let throwableInitMock = """ +class MyProtocolMock: MyProtocol { + private var _param: String! + init() { } + required init(param: String = "") throws { + self._param = param + } +} +""" + +let typedThrowableInit = """ +/// \(String.mockAnnotation) +protocol MyProtocol { + init(param: String) throws(SomeError) +} +""" + +let typedThrowableInitMock = """ + + + class MyProtocolMock: MyProtocol { private var _param: String! init() { } diff --git a/Tests/TestInit/InitTests.swift b/Tests/TestInit/InitTests.swift index f5d8af86..2a8da0b0 100644 --- a/Tests/TestInit/InitTests.swift +++ b/Tests/TestInit/InitTests.swift @@ -42,10 +42,17 @@ class InitTests: MockoloTestCase { ) } - func testThrowableInit() { - verify( - srcContent: throwableInit, - dstContent: throwableInitMock - ) - } + func testThrowableInit() { + verify( + srcContent: throwableInit, + dstContent: throwableInitMock + ) + } + + func testTypedThrowableInit() { + verify( + srcContent: typedThrowableInit, + dstContent: typedThrowableInitMock + ) + } } From 7f7028a8efc10e2afc14bfb9b634b151ec870702 Mon Sep 17 00:00:00 2001 From: Fumiya Tanaka Date: Sat, 3 Aug 2024 11:50:10 +0900 Subject: [PATCH 05/27] Update Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift Co-authored-by: Iceman --- Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift b/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift index ddfe8f1a..95267c97 100644 --- a/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift +++ b/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift @@ -574,7 +574,7 @@ extension ThrowsClauseSyntax { var text: String { if let type { - "\(throwsSpecifier.text)(\(type.description))" + "\(throwsSpecifier.text)(\(type))" } else { throwsSpecifier.text } From e3a2da8e8e311e923f1c8fac3fcc3fc483fcf7b2 Mon Sep 17 00:00:00 2001 From: Iceman Date: Fri, 25 Oct 2024 12:44:33 +0900 Subject: [PATCH 06/27] Read accessor and omit setCallCount if protocol has no setter --- .../Models/VariableModel.swift | 11 ++++++ .../Parsers/SwiftSyntaxExtensions.swift | 21 ++++++++++++ .../Templates/VariableTemplate.swift | 34 +++++++++++++++---- Tests/TestActor/FixtureActor.swift | 7 ++-- Tests/TestCombine/FixtureCombine.swift | 9 ++--- .../FixtureExistentialAny.swift | 12 +++---- .../FixtureModifiersTypes.swift | 11 +++--- .../FixtureModuleOverrides.swift | 4 +-- Tests/TestOverloads/FixtureDuplicates4.swift | 10 +++--- Tests/TestOverloads/FixtureInheritance.swift | 4 +-- Tests/TestPATs/FixturePAT.swift | 4 +-- Tests/TestRx/FixtureRxVars.swift | 16 ++++----- Tests/TestVars/FixtureNonSimpleVars.swift | 16 ++++----- 13 files changed, 102 insertions(+), 57 deletions(-) diff --git a/Sources/MockoloFramework/Models/VariableModel.swift b/Sources/MockoloFramework/Models/VariableModel.swift index a2007fdc..84b83189 100644 --- a/Sources/MockoloFramework/Models/VariableModel.swift +++ b/Sources/MockoloFramework/Models/VariableModel.swift @@ -1,6 +1,11 @@ import Foundation final class VariableModel: Model { + enum GetterEffect: Hashable { + case async + case `throws`(errorType: String?) + } + var name: String var type: SwiftType var offset: Int64 @@ -13,6 +18,8 @@ final class VariableModel: Model { var filePath: String = "" var isStatic = false var shouldOverride = false + let getterEffects: Set + let hasSetter: Bool var rxTypes: [String: String]? var customModifiers: [String: Modifier]? var modelDescription: String? = nil @@ -40,6 +47,8 @@ final class VariableModel: Model { acl: String?, encloserType: DeclType, isStatic: Bool, + getterEffects: Set, + hasSetter: Bool, canBeInitParam: Bool, offset: Int64, rxTypes: [String: String]?, @@ -51,6 +60,8 @@ final class VariableModel: Model { self.type = SwiftType(typeName.trimmingCharacters(in: .whitespaces)) self.offset = offset self.isStatic = isStatic + self.getterEffects = getterEffects + self.hasSetter = hasSetter self.shouldOverride = encloserType == .classType self.canBeInitParam = canBeInitParam self.processed = processed diff --git a/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift b/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift index f5c32b44..2b39a8c9 100644 --- a/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift +++ b/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift @@ -368,11 +368,32 @@ extension VariableDeclSyntax { typeName = vtype } + let hasSetter: Bool + var getterEffects = Set() + switch v.accessorBlock?.accessors { + case .accessors(let accessorDecls): + if let getterDecl = accessorDecls.first(where: { $0.accessorSpecifier.tokenKind == .keyword(.get) }) { + if getterDecl.effectSpecifiers?.asyncSpecifier != nil { + getterEffects.insert(.async) + } + if let `throws` = getterDecl.effectSpecifiers?.throwsClause { + getterEffects.insert(.throws(errorType: `throws`.type?.trimmedDescription)) + } + } + hasSetter = accessorDecls.contains(where: { $0.accessorSpecifier.tokenKind == .keyword(.set) }) + case .getter: + hasSetter = false + case nil: + hasSetter = true + } + let varmodel = VariableModel(name: name, typeName: typeName, acl: acl, encloserType: declType, isStatic: isStatic, + getterEffects: getterEffects, + hasSetter: hasSetter, canBeInitParam: potentialInitParam, offset: v.offset, rxTypes: metadata?.varTypes, diff --git a/Sources/MockoloFramework/Templates/VariableTemplate.swift b/Sources/MockoloFramework/Templates/VariableTemplate.swift index f6b19b16..522f0afa 100644 --- a/Sources/MockoloFramework/Templates/VariableTemplate.swift +++ b/Sources/MockoloFramework/Templates/VariableTemplate.swift @@ -58,13 +58,35 @@ extension VariableModel { modifierTypeStr = "" } - var template = "" + let staticSpace = isStatic ? "\(String.static) " : "" + let setCallCountVarDecl = hasSetter ? """ + \(1.tab)\(acl)\(staticSpace)\(privateSetSpace)var \(underlyingSetCallCount) = 0 + """ : "" + var accessorBlockItems: [String] = [] + if hasSetter { + let didSetBlock = """ + didSet { \(setCallCountStmt) } + """ + accessorBlockItems.append(didSetBlock) + } + let accessorBlock: String + switch accessorBlockItems.count { + case 0: accessorBlock = "" + case 1: accessorBlock = " { \(accessorBlockItems[0]) }" + default: + accessorBlock = """ + { + \(accessorBlockItems.map { "\(2.tab)\($0)" }.joined(separator: "\n")) + \(1.tab)} + """ + } + + let template: String if isStatic || underlyingVarDefaultVal == nil { - let staticSpace = isStatic ? "\(String.static) " : "" template = """ - \(1.tab)\(acl)\(staticSpace)\(privateSetSpace)var \(underlyingSetCallCount) = 0 - \(1.tab)\(propertyWrapper)\(staticSpace)private var \(underlyingName): \(underlyingType) \(assignVal) { didSet { \(setCallCountStmt) } } + \(setCallCountVarDecl) + \(1.tab)\(propertyWrapper)\(staticSpace)private var \(underlyingName): \(underlyingType) \(assignVal)\(accessorBlock) \(1.tab)\(acl)\(staticSpace)\(overrideStr)\(modifierTypeStr)var \(name): \(type.typeName) { \(2.tab)get { return \(underlyingName) } \(2.tab)set { \(underlyingName) = newValue } @@ -73,8 +95,8 @@ extension VariableModel { } else { template = """ - \(1.tab)\(acl)\(privateSetSpace)var \(underlyingSetCallCount) = 0 - \(1.tab)\(propertyWrapper)\(acl)\(overrideStr)\(modifierTypeStr)var \(name): \(type.typeName) \(assignVal) { didSet { \(setCallCountStmt) } } + \(setCallCountVarDecl) + \(1.tab)\(propertyWrapper)\(acl)\(overrideStr)\(modifierTypeStr)var \(name): \(type.typeName) \(assignVal)\(accessorBlock) """ } diff --git a/Tests/TestActor/FixtureActor.swift b/Tests/TestActor/FixtureActor.swift index c4a33e29..6ed40963 100644 --- a/Tests/TestActor/FixtureActor.swift +++ b/Tests/TestActor/FixtureActor.swift @@ -23,8 +23,8 @@ actor FooMock: Foo { } fatalError("fooHandler returns can't have a default value thus its handler must be set") } - private(set) var barSetCallCount = 0 - var bar: Int = 0 { didSet { barSetCallCount += 1 } } + + var bar: Int = 0 } """ @@ -48,8 +48,7 @@ actor FooMock: Foo { } - private(set) var barSetCallCount = 0 - var bar: Int = 0 { didSet { barSetCallCount += 1 } } + var bar: Int = 0 private(set) var bazCallCount = 0 var bazHandler: ((String) async -> (Int))? diff --git a/Tests/TestCombine/FixtureCombine.swift b/Tests/TestCombine/FixtureCombine.swift index 0fb0a254..10ecf7f8 100644 --- a/Tests/TestCombine/FixtureCombine.swift +++ b/Tests/TestCombine/FixtureCombine.swift @@ -225,22 +225,19 @@ public class FooMock: Foo { public init() { } - public private(set) var myPublisherSetCallCount = 0 - private var _myPublisher: AnyPublisher! { didSet { myPublisherSetCallCount += 1 } } + private var _myPublisher: AnyPublisher! public var myPublisher: AnyPublisher { get { return _myPublisher } set { _myPublisher = newValue } } - public private(set) var dictionaryPublisherSetCallCount = 0 - private var _dictionaryPublisher: AnyPublisher, Never>! { didSet { dictionaryPublisherSetCallCount += 1 } } + private var _dictionaryPublisher: AnyPublisher, Never>! public var dictionaryPublisher: AnyPublisher, Never> { get { return _dictionaryPublisher } set { _dictionaryPublisher = newValue } } - public private(set) var noDefaultSubjectValueSetCallCount = 0 - private var _noDefaultSubjectValue: AnyPublisher! { didSet { noDefaultSubjectValueSetCallCount += 1 } } + private var _noDefaultSubjectValue: AnyPublisher! public var noDefaultSubjectValue: AnyPublisher { get { return _noDefaultSubjectValue } set { _noDefaultSubjectValue = newValue } diff --git a/Tests/TestExistentialAny/FixtureExistentialAny.swift b/Tests/TestExistentialAny/FixtureExistentialAny.swift index 321c6d5e..be40c1f5 100644 --- a/Tests/TestExistentialAny/FixtureExistentialAny.swift +++ b/Tests/TestExistentialAny/FixtureExistentialAny.swift @@ -20,29 +20,25 @@ class ExistentialAnyMock: ExistentialAny { } - private(set) var fooSetCallCount = 0 - private var _foo: P! { didSet { fooSetCallCount += 1 } } + private var _foo: P! var foo: P { get { return _foo } set { _foo = newValue } } - private(set) var barSetCallCount = 0 - private var _bar: (any R)! { didSet { barSetCallCount += 1 } } + private var _bar: (any R)! var bar: any R { get { return _bar } set { _bar = newValue } } - private(set) var bazSetCallCount = 0 - private var _baz: (any P & Q)! { didSet { bazSetCallCount += 1 } } + private var _baz: (any P & Q)! var baz: any P & Q { get { return _baz } set { _baz = newValue } } - private(set) var quxSetCallCount = 0 - private var _qux: ((any P) -> any P)! { didSet { quxSetCallCount += 1 } } + private var _qux: ((any P) -> any P)! var qux: (any P) -> any P { get { return _qux } set { _qux = newValue } diff --git a/Tests/TestModifiersTypes/FixtureModifiersTypes.swift b/Tests/TestModifiersTypes/FixtureModifiersTypes.swift index 0dd1a7af..56ced15b 100644 --- a/Tests/TestModifiersTypes/FixtureModifiersTypes.swift +++ b/Tests/TestModifiersTypes/FixtureModifiersTypes.swift @@ -17,8 +17,7 @@ class FooMock: Foo { } - private(set) var listenerSetCallCount = 0 - weak var listener: AnyObject? = nil { didSet { listenerSetCallCount += 1 } } + weak var listener: AnyObject? = nil private(set) var barFuncCallCount = 0 var barFuncHandler: (([Int]) -> ())? @@ -60,8 +59,8 @@ class FooMock: Foo { } - private(set) var listenerSetCallCount = 0 - dynamic var listener: AnyObject? = nil { didSet { listenerSetCallCount += 1 } } + + dynamic var listener: AnyObject? = nil private(set) var barFuncCallCount = 0 var barFuncHandler: (([Int]) -> ())? @@ -103,8 +102,8 @@ class FooMock: Foo { } - private(set) var listenerSetCallCount = 0 - var listener: AnyObject? = nil { didSet { listenerSetCallCount += 1 } } + + var listener: AnyObject? = nil private(set) var barFuncCallCount = 0 var barFuncHandler: (([Int]) -> ())? diff --git a/Tests/TestModuleNames/FixtureModuleOverrides.swift b/Tests/TestModuleNames/FixtureModuleOverrides.swift index 3f29d1c5..af3d13f5 100644 --- a/Tests/TestModuleNames/FixtureModuleOverrides.swift +++ b/Tests/TestModuleNames/FixtureModuleOverrides.swift @@ -17,8 +17,8 @@ class TaskRoutingMock: Foo.TaskRouting { init(bar: String = "") { self.bar = bar } - private(set) var barSetCallCount = 0 - var bar: String = "" { didSet { barSetCallCount += 1 } } + + var bar: String = "" private(set) var bazCallCount = 0 var bazHandler: (() -> (Double))? func baz() -> Double { diff --git a/Tests/TestOverloads/FixtureDuplicates4.swift b/Tests/TestOverloads/FixtureDuplicates4.swift index b92b9099..cfb8ccbd 100644 --- a/Tests/TestOverloads/FixtureDuplicates4.swift +++ b/Tests/TestOverloads/FixtureDuplicates4.swift @@ -110,7 +110,7 @@ public class FooMock: Foo { let sameNameVarFunc = """ /// \(String.mockAnnotation) -public protocol Bar: class { +public protocol Bar: AnyObject { var talk: Int { get } } @@ -131,8 +131,8 @@ public class BarMock: Bar { self.talk = talk } - public private(set) var talkSetCallCount = 0 - public var talk: Int = 0 { didSet { talkSetCallCount += 1 } } + + public var talk: Int = 0 } public class FooMock: Foo { @@ -144,8 +144,8 @@ public class FooMock: Foo { self.talk = talk } - public private(set) var talkSetCallCount = 0 - public var talk: Int = 0 { didSet { talkSetCallCount += 1 } } + + public var talk: Int = 0 public private(set) var talkDismissCallCount = 0 public var talkDismissHandler: ((Bool) -> ())? public func talk(_ dismiss: Bool) { diff --git a/Tests/TestOverloads/FixtureInheritance.swift b/Tests/TestOverloads/FixtureInheritance.swift index 4ca87227..df98dc43 100644 --- a/Tests/TestOverloads/FixtureInheritance.swift +++ b/Tests/TestOverloads/FixtureInheritance.swift @@ -111,8 +111,8 @@ class TestProtocolMock: TestProtocol { } - private(set) var someBoolSetCallCount = 0 - var someBool: Bool = false { didSet { someBoolSetCallCount += 1 } } + + var someBool: Bool = false private(set) var doSomethingCallCount = 0 var doSomethingHandler: (() -> ())? diff --git a/Tests/TestPATs/FixturePAT.swift b/Tests/TestPATs/FixturePAT.swift index 12f155f3..90bbb151 100644 --- a/Tests/TestPATs/FixturePAT.swift +++ b/Tests/TestPATs/FixturePAT.swift @@ -152,8 +152,8 @@ public class SomeTypeMock: SomeType { self._key = key } public typealias Key = String - public private(set) var keySetCallCount = 0 - private var _key: Key! { didSet { keySetCallCount += 1 } } + + private var _key: Key! public var key: Key { get { return _key } set { _key = newValue } diff --git a/Tests/TestRx/FixtureRxVars.swift b/Tests/TestRx/FixtureRxVars.swift index 2f3e2daa..40aa6f63 100644 --- a/Tests/TestRx/FixtureRxVars.swift +++ b/Tests/TestRx/FixtureRxVars.swift @@ -27,25 +27,25 @@ public class FooMock: Foo { self.someBar = someBar } - public private(set) var someBehaviorSetCallCount = 0 - private var _someBehavior: BehaviorSubject! { didSet { someBehaviorSetCallCount += 1 } } + + private var _someBehavior: BehaviorSubject! public var someBehavior: BehaviorSubject { get { return _someBehavior } set { _someBehavior = newValue } } - public private(set) var someReplySetCallCount = 0 - public var someReply: ReplaySubject = ReplaySubject.create(bufferSize: 1) { didSet { someReplySetCallCount += 1 } } - public private(set) var someVariableSetCallCount = 0 - private var _someVariable: Variable! { didSet { someVariableSetCallCount += 1 } } + public var someReply: ReplaySubject = ReplaySubject.create(bufferSize: 1) + + + private var _someVariable: Variable! public var someVariable: Variable { get { return _someVariable } set { _someVariable = newValue } } - public private(set) var someBarSetCallCount = 0 - public var someBar: Bar = BarMock() { didSet { someBarSetCallCount += 1 } } + + public var someBar: Bar = BarMock() } """ diff --git a/Tests/TestVars/FixtureNonSimpleVars.swift b/Tests/TestVars/FixtureNonSimpleVars.swift index c69ae23f..0f6dec3f 100644 --- a/Tests/TestVars/FixtureNonSimpleVars.swift +++ b/Tests/TestVars/FixtureNonSimpleVars.swift @@ -29,19 +29,19 @@ public class NonSimpleVarsMock: NonSimpleVars { } public private(set) var dictSetCallCount = 0 public var dict: Dictionary = Dictionary() { didSet { dictSetCallCount += 1 } } - public private(set) var closureVarSetCallCount = 0 - public var closureVar: ((_ arg: String) -> Void)? = nil { didSet { closureVarSetCallCount += 1 } } - public private(set) var voidHandlerSetCallCount = 0 - private var _voidHandler: ((() -> ()))! { didSet { voidHandlerSetCallCount += 1 } } + + public var closureVar: ((_ arg: String) -> Void)? = nil + + private var _voidHandler: ((() -> ()))! public var voidHandler: (() -> ()) { get { return _voidHandler } set { _voidHandler = newValue } } - public private(set) var hasDotSetCallCount = 0 - public var hasDot: ModuleX.SomeType? = nil { didSet { hasDotSetCallCount += 1 } } - public static private(set) var someValSetCallCount = 0 - static private var _someVal: String = "" { didSet { someValSetCallCount += 1 } } + public var hasDot: ModuleX.SomeType? = nil + + + static private var _someVal: String = "" public static var someVal: String { get { return _someVal } set { _someVal = newValue } From 2a7a57d52ce98c5fd729a82996e51286125e82c0 Mon Sep 17 00:00:00 2001 From: Iceman Date: Fri, 25 Oct 2024 12:57:45 +0900 Subject: [PATCH 07/27] Remove unnecessary static var handling --- Sources/MockoloFramework/Models/VariableModel.swift | 2 +- .../MockoloFramework/Templates/VariableTemplate.swift | 7 +++---- Tests/TestVars/FixtureNonSimpleVars.swift | 10 +++++----- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/Sources/MockoloFramework/Models/VariableModel.swift b/Sources/MockoloFramework/Models/VariableModel.swift index 84b83189..25afd199 100644 --- a/Sources/MockoloFramework/Models/VariableModel.swift +++ b/Sources/MockoloFramework/Models/VariableModel.swift @@ -36,7 +36,7 @@ final class VariableModel: Model { } var underlyingName: String { - if isStatic || type.defaultVal() == nil { + if type.defaultVal() == nil { return "_\(name)" } return name diff --git a/Sources/MockoloFramework/Templates/VariableTemplate.swift b/Sources/MockoloFramework/Templates/VariableTemplate.swift index 522f0afa..cb4de311 100644 --- a/Sources/MockoloFramework/Templates/VariableTemplate.swift +++ b/Sources/MockoloFramework/Templates/VariableTemplate.swift @@ -48,7 +48,6 @@ extension VariableModel { } let privateSetSpace = allowSetCallCount ? "" : "\(String.privateSet) " - let setCallCountStmt = "\(underlyingSetCallCount) += 1" let modifierTypeStr: String if let customModifiers = self.customModifiers, @@ -65,7 +64,7 @@ extension VariableModel { var accessorBlockItems: [String] = [] if hasSetter { let didSetBlock = """ - didSet { \(setCallCountStmt) } + didSet { \(underlyingSetCallCount) += 1 } """ accessorBlockItems.append(didSetBlock) } @@ -82,7 +81,7 @@ extension VariableModel { } let template: String - if isStatic || underlyingVarDefaultVal == nil { + if underlyingVarDefaultVal == nil { template = """ \(setCallCountVarDecl) @@ -96,7 +95,7 @@ extension VariableModel { template = """ \(setCallCountVarDecl) - \(1.tab)\(propertyWrapper)\(acl)\(overrideStr)\(modifierTypeStr)var \(name): \(type.typeName) \(assignVal)\(accessorBlock) + \(1.tab)\(propertyWrapper)\(acl)\(staticSpace)\(overrideStr)\(modifierTypeStr)var \(name): \(type.typeName) \(assignVal)\(accessorBlock) """ } diff --git a/Tests/TestVars/FixtureNonSimpleVars.swift b/Tests/TestVars/FixtureNonSimpleVars.swift index 0f6dec3f..4298229a 100644 --- a/Tests/TestVars/FixtureNonSimpleVars.swift +++ b/Tests/TestVars/FixtureNonSimpleVars.swift @@ -13,6 +13,7 @@ public protocol NonSimpleVars { var voidHandler: (() -> ()) { get } var hasDot: ModuleX.SomeType? { get } static var someVal: String { get } + static var someVal2: String { get set } } """ @@ -41,11 +42,10 @@ public class NonSimpleVarsMock: NonSimpleVars { public var hasDot: ModuleX.SomeType? = nil - static private var _someVal: String = "" - public static var someVal: String { - get { return _someVal } - set { _someVal = newValue } - } + public static var someVal: String = "" + + public static private(set) var someVal2SetCallCount = 0 + public static var someVal2: String = "" { didSet { someVal2SetCallCount += 1 } } } """ From 96add24cbddc51e2d54b8462824bcc2485241024 Mon Sep 17 00:00:00 2001 From: Iceman Date: Fri, 25 Oct 2024 16:59:31 +0900 Subject: [PATCH 08/27] Support get async and get throws property accessor --- .../Models/VariableModel.swift | 25 ++-- .../Parsers/SwiftSyntaxExtensions.swift | 38 +++-- .../Templates/VariableTemplate.swift | 141 +++++++++++++----- Tests/TestVars/FixtureAsyncThrowsVars.swift | 43 ++++++ Tests/TestVars/VarTests.swift | 5 + 5 files changed, 194 insertions(+), 58 deletions(-) create mode 100644 Tests/TestVars/FixtureAsyncThrowsVars.swift diff --git a/Sources/MockoloFramework/Models/VariableModel.swift b/Sources/MockoloFramework/Models/VariableModel.swift index 25afd199..f7d7b9cb 100644 --- a/Sources/MockoloFramework/Models/VariableModel.swift +++ b/Sources/MockoloFramework/Models/VariableModel.swift @@ -1,9 +1,19 @@ import Foundation final class VariableModel: Model { - enum GetterEffect: Hashable { - case async - case `throws`(errorType: String?) + struct GetterEffects: Equatable { + var isAsync: Bool + enum ThrowsType: Equatable { + case throwing(errorType: String?) + case none + } + var `throws`: ThrowsType + static let empty: GetterEffects = .init(isAsync: false, throws: .none) + } + + enum /* Mock */StorageType { + case stored(needsSetCount: Bool) + case computed(GetterEffects) } var name: String @@ -18,8 +28,7 @@ final class VariableModel: Model { var filePath: String = "" var isStatic = false var shouldOverride = false - let getterEffects: Set - let hasSetter: Bool + let storageType: StorageType var rxTypes: [String: String]? var customModifiers: [String: Modifier]? var modelDescription: String? = nil @@ -47,8 +56,7 @@ final class VariableModel: Model { acl: String?, encloserType: DeclType, isStatic: Bool, - getterEffects: Set, - hasSetter: Bool, + storageType: StorageType, canBeInitParam: Bool, offset: Int64, rxTypes: [String: String]?, @@ -60,8 +68,7 @@ final class VariableModel: Model { self.type = SwiftType(typeName.trimmingCharacters(in: .whitespaces)) self.offset = offset self.isStatic = isStatic - self.getterEffects = getterEffects - self.hasSetter = hasSetter + self.storageType = storageType self.shouldOverride = encloserType == .classType self.canBeInitParam = canBeInitParam self.processed = processed diff --git a/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift b/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift index 2b39a8c9..d1c3df98 100644 --- a/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift +++ b/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift @@ -368,23 +368,36 @@ extension VariableDeclSyntax { typeName = vtype } - let hasSetter: Bool - var getterEffects = Set() + let storageType: VariableModel.StorageType switch v.accessorBlock?.accessors { case .accessors(let accessorDecls): - if let getterDecl = accessorDecls.first(where: { $0.accessorSpecifier.tokenKind == .keyword(.get) }) { - if getterDecl.effectSpecifiers?.asyncSpecifier != nil { - getterEffects.insert(.async) - } - if let `throws` = getterDecl.effectSpecifiers?.throwsClause { - getterEffects.insert(.throws(errorType: `throws`.type?.trimmedDescription)) + if accessorDecls.contains(where: { $0.accessorSpecifier.tokenKind == .keyword(.set) }) { + storageType = .stored(needsSetCount: true) + } else if let getterDecl = accessorDecls.first(where: { $0.accessorSpecifier.tokenKind == .keyword(.get) }) { + if getterDecl.body == nil { // is protoccol + var getterEffects = VariableModel.GetterEffects.empty + if getterDecl.effectSpecifiers?.asyncSpecifier != nil { + getterEffects.isAsync = true + } + if let `throws` = getterDecl.effectSpecifiers?.throwsClause { + getterEffects.throws = .throwing(errorType: `throws`.type?.trimmedDescription) + } + if getterEffects == .empty { + storageType = .stored(needsSetCount: false) + } else { + storageType = .computed(getterEffects) + } + } else { // is class + storageType = .computed(.empty) } + } else { + // will never happens + storageType = .stored(needsSetCount: false) // fallback } - hasSetter = accessorDecls.contains(where: { $0.accessorSpecifier.tokenKind == .keyword(.set) }) case .getter: - hasSetter = false + storageType = .computed(.empty) case nil: - hasSetter = true + storageType = .stored(needsSetCount: true) } let varmodel = VariableModel(name: name, @@ -392,8 +405,7 @@ extension VariableDeclSyntax { acl: acl, encloserType: declType, isStatic: isStatic, - getterEffects: getterEffects, - hasSetter: hasSetter, + storageType: storageType, canBeInitParam: potentialInitParam, offset: v.offset, rxTypes: metadata?.varTypes, diff --git a/Sources/MockoloFramework/Templates/VariableTemplate.swift b/Sources/MockoloFramework/Templates/VariableTemplate.swift index cb4de311..c4bf1c73 100644 --- a/Sources/MockoloFramework/Templates/VariableTemplate.swift +++ b/Sources/MockoloFramework/Templates/VariableTemplate.swift @@ -58,48 +58,62 @@ extension VariableModel { } let staticSpace = isStatic ? "\(String.static) " : "" - let setCallCountVarDecl = hasSetter ? """ - \(1.tab)\(acl)\(staticSpace)\(privateSetSpace)var \(underlyingSetCallCount) = 0 - """ : "" - var accessorBlockItems: [String] = [] - if hasSetter { - let didSetBlock = """ - didSet { \(underlyingSetCallCount) += 1 } - """ - accessorBlockItems.append(didSetBlock) - } - let accessorBlock: String - switch accessorBlockItems.count { - case 0: accessorBlock = "" - case 1: accessorBlock = " { \(accessorBlockItems[0]) }" - default: - accessorBlock = """ - { - \(accessorBlockItems.map { "\(2.tab)\($0)" }.joined(separator: "\n")) - \(1.tab)} - """ - } - let template: String - if underlyingVarDefaultVal == nil { - template = """ + switch storageType { + case .stored(let needSetCount): + let setCallCountVarDecl = needSetCount ? """ + \(1.tab)\(acl)\(staticSpace)\(privateSetSpace)var \(underlyingSetCallCount) = 0 + """ : "" + + var accessorBlockItems: [String] = [] + if needSetCount { + let didSetBlock = """ + didSet { \(underlyingSetCallCount) += 1 } + """ + accessorBlockItems.append(didSetBlock) + } + + let accessorBlock: String + switch accessorBlockItems.count { + case 0: accessorBlock = "" + case 1: accessorBlock = " { \(accessorBlockItems[0]) }" + default: + accessorBlock = """ + { + \(accessorBlockItems.map { "\(2.tab)\($0)" }.joined(separator: "\n")) + \(1.tab)} + """ + } + + let template: String + if underlyingVarDefaultVal == nil { + template = """ + + \(setCallCountVarDecl) + \(1.tab)\(propertyWrapper)\(staticSpace)private var \(underlyingName): \(underlyingType) \(assignVal)\(accessorBlock) + \(1.tab)\(acl)\(staticSpace)\(overrideStr)\(modifierTypeStr)var \(name): \(type.typeName) { + \(2.tab)get { return \(underlyingName) } + \(2.tab)set { \(underlyingName) = newValue } + \(1.tab)} + """ + } else { + template = """ + + \(setCallCountVarDecl) + \(1.tab)\(propertyWrapper)\(acl)\(staticSpace)\(overrideStr)\(modifierTypeStr)var \(name): \(type.typeName) \(assignVal)\(accessorBlock) + """ + } + + return template - \(setCallCountVarDecl) - \(1.tab)\(propertyWrapper)\(staticSpace)private var \(underlyingName): \(underlyingType) \(assignVal)\(accessorBlock) + case .computed(let effects): + return """ + \(1.tab)\(acl)\(staticSpace)var \(name)Handler: (() \(effects.syntax)-> \(type.typeName))? \(1.tab)\(acl)\(staticSpace)\(overrideStr)\(modifierTypeStr)var \(name): \(type.typeName) { - \(2.tab)get { return \(underlyingName) } - \(2.tab)set { \(underlyingName) = newValue } + \(2.tab)get \(effects.syntax){ \(effects.callerMarkers)\(name)Handler!() } \(1.tab)} """ - } else { - template = """ - - \(setCallCountVarDecl) - \(1.tab)\(propertyWrapper)\(acl)\(staticSpace)\(overrideStr)\(modifierTypeStr)var \(name): \(type.typeName) \(assignVal)\(accessorBlock) - """ } - - return template } func applyCombineVariableTemplate(name: String, @@ -325,4 +339,59 @@ extension VariableModel { } } +extension VariableModel.GetterEffects { + fileprivate var syntax: String { + var clauses: [String] = [] + if isAsync { + clauses.append(.async) + } + switch `throws` { + case .throwing(let errorType): + if let errorType { + clauses.append("\(String.throws)(\(errorType))") + } else { + clauses.append(.throws) + } + case .none: + break + } + return clauses.map { "\($0) " }.joined() + } + fileprivate var callerMarkers: String { + var clauses: [String] = [] + switch `throws` { + case .throwing: + clauses.append(.try) + case .none: + break + } + if isAsync { + clauses.append(.await) + } + return clauses.map { "\($0) " }.joined() + } + + fileprivate func handlerType(typeName: String) -> String { +// if self == .empty { +// return nil +// } + + var clauses: [String] = [] + if isAsync { + clauses.append(.async) + } + switch `throws` { + case .throwing(let errorType): + if let errorType { + clauses.append("\(String.throws)(\(errorType))") + } else { + clauses.append(.throws) + } + case .none: + break + } + + return "(() \(clauses.map { "\($0) " }.joined())-> \(typeName))?" + } +} diff --git a/Tests/TestVars/FixtureAsyncThrowsVars.swift b/Tests/TestVars/FixtureAsyncThrowsVars.swift new file mode 100644 index 00000000..86376a41 --- /dev/null +++ b/Tests/TestVars/FixtureAsyncThrowsVars.swift @@ -0,0 +1,43 @@ +import MockoloFramework + +let asyncThrowsVars = """ +/// \(String.mockAnnotation) +public protocol AsyncThrowsVars { + var getOnly: Int { get } + static var getAndSet: Int { get set } + var getAndThrows: Int { get throws } + static var getAndAsync: Int { get async } + var getAndAsyncAndThrows: Int { get async throws(any Error) } +} +""" + +let asyncThrowsVarsMock = """ +public class AsyncThrowsVarsMock: AsyncThrowsVars { + public init() { } + public init(getOnly: Int = 0, getAndThrows: Int = 0, getAndAsyncAndThrows: Int = 0) { + self.getOnly = getOnly + self.getAndThrowsHandler = { getAndThrows } + self.getAndAsyncAndThrowsHandler = { getAndAsyncAndThrows } + } + + + + public var getOnly: Int = 0 + + public static private(set) var getAndSetSetCallCount = 0 + public static var getAndSet: Int = 0 { didSet { getAndSetSetCallCount += 1 } } + public var getAndThrowsHandler: (() throws -> Int)? + public var getAndThrows: Int { + get throws { try getAndThrowsHandler!() } + } + public static var getAndAsyncHandler: (() async -> Int)? + public static var getAndAsync: Int { + get async { await getAndAsyncHandler!() } + } + public var getAndAsyncAndThrowsHandler: (() async throws(any Error) -> Int)? + public var getAndAsyncAndThrows: Int { + get async throws(any Error) { try await getAndAsyncAndThrowsHandler!() } + } +} +""" + diff --git a/Tests/TestVars/VarTests.swift b/Tests/TestVars/VarTests.swift index dbd272c0..44614725 100644 --- a/Tests/TestVars/VarTests.swift +++ b/Tests/TestVars/VarTests.swift @@ -23,4 +23,9 @@ class VarTests: MockoloTestCase { dstContent: simpleVarsAllowCallCountMock, allowSetCallCount: true) } + + func testAsyncThrows() { + verify(srcContent: asyncThrowsVars, + dstContent: asyncThrowsVarsMock) + } } From 72a125d60b1b5ae8da3c417705a513c31273a475 Mon Sep 17 00:00:00 2001 From: Iceman Date: Fri, 25 Oct 2024 17:00:14 +0900 Subject: [PATCH 09/27] small rename and remove unused code --- .../Models/VariableModel.swift | 6 ++--- .../Parsers/SwiftSyntaxExtensions.swift | 2 +- .../Templates/VariableTemplate.swift | 23 ------------------- 3 files changed, 4 insertions(+), 27 deletions(-) diff --git a/Sources/MockoloFramework/Models/VariableModel.swift b/Sources/MockoloFramework/Models/VariableModel.swift index f7d7b9cb..8f4db1d9 100644 --- a/Sources/MockoloFramework/Models/VariableModel.swift +++ b/Sources/MockoloFramework/Models/VariableModel.swift @@ -11,7 +11,7 @@ final class VariableModel: Model { static let empty: GetterEffects = .init(isAsync: false, throws: .none) } - enum /* Mock */StorageType { + enum MockStorageType { case stored(needsSetCount: Bool) case computed(GetterEffects) } @@ -28,7 +28,7 @@ final class VariableModel: Model { var filePath: String = "" var isStatic = false var shouldOverride = false - let storageType: StorageType + let storageType: MockStorageType var rxTypes: [String: String]? var customModifiers: [String: Modifier]? var modelDescription: String? = nil @@ -56,7 +56,7 @@ final class VariableModel: Model { acl: String?, encloserType: DeclType, isStatic: Bool, - storageType: StorageType, + storageType: MockStorageType, canBeInitParam: Bool, offset: Int64, rxTypes: [String: String]?, diff --git a/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift b/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift index d1c3df98..0dead101 100644 --- a/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift +++ b/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift @@ -368,7 +368,7 @@ extension VariableDeclSyntax { typeName = vtype } - let storageType: VariableModel.StorageType + let storageType: VariableModel.MockStorageType switch v.accessorBlock?.accessors { case .accessors(let accessorDecls): if accessorDecls.contains(where: { $0.accessorSpecifier.tokenKind == .keyword(.set) }) { diff --git a/Sources/MockoloFramework/Templates/VariableTemplate.swift b/Sources/MockoloFramework/Templates/VariableTemplate.swift index c4bf1c73..e8c628db 100644 --- a/Sources/MockoloFramework/Templates/VariableTemplate.swift +++ b/Sources/MockoloFramework/Templates/VariableTemplate.swift @@ -371,27 +371,4 @@ extension VariableModel.GetterEffects { } return clauses.map { "\($0) " }.joined() } - - fileprivate func handlerType(typeName: String) -> String { -// if self == .empty { -// return nil -// } - - var clauses: [String] = [] - if isAsync { - clauses.append(.async) - } - switch `throws` { - case .throwing(let errorType): - if let errorType { - clauses.append("\(String.throws)(\(errorType))") - } else { - clauses.append(.throws) - } - case .none: - break - } - - return "(() \(clauses.map { "\($0) " }.joined())-> \(typeName))?" - } } From 0194530bf2f8791712392a17dc24849491a31cbc Mon Sep 17 00:00:00 2001 From: Iceman Date: Fri, 25 Oct 2024 17:06:32 +0900 Subject: [PATCH 10/27] generate initialize for computed var --- .../Templates/NominalTemplate.swift | 19 +++++++++++-------- .../Templates/VariableTemplate.swift | 1 + Tests/TestVars/FixtureAsyncThrowsVars.swift | 3 +++ 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/Sources/MockoloFramework/Templates/NominalTemplate.swift b/Sources/MockoloFramework/Templates/NominalTemplate.swift index 50a2ee23..cce48583 100644 --- a/Sources/MockoloFramework/Templates/NominalTemplate.swift +++ b/Sources/MockoloFramework/Templates/NominalTemplate.swift @@ -147,8 +147,8 @@ extension NominalModel { if needParamedInit { var paramsAssign = "" let params = initParamCandidates - .map { (element: Model) -> String in - if let val = element.type.defaultVal(with: overrides, overrideKey: element.name, isInitParam: true) { + .map { (element: VariableModel) -> String in + if let val = element.type.defaultVal(with: overrides, overrideKey: element.name, isInitParam: true) { return "\(element.name): \(element.type.typeName) = \(val)" } var prefix = "" @@ -158,13 +158,16 @@ extension NominalModel { } } return "\(element.name): \(prefix)\(element.type.typeName)" - } - .joined(separator: ", ") + } + .joined(separator: ", ") - - paramsAssign = initParamCandidates.map { p in - return "\(2.tab)self.\(p.underlyingName) = \(p.name.safeName)" - + paramsAssign = initParamCandidates.map { (element: VariableModel) in + switch element.storageType { + case .stored: + return "\(2.tab)self.\(element.underlyingName) = \(element.name.safeName)" + case .computed: + return "\(2.tab)self.\(element.name)Handler = { \(element.name.safeName) }" + } }.joined(separator: "\n") initTemplate = """ diff --git a/Sources/MockoloFramework/Templates/VariableTemplate.swift b/Sources/MockoloFramework/Templates/VariableTemplate.swift index e8c628db..ed854ce0 100644 --- a/Sources/MockoloFramework/Templates/VariableTemplate.swift +++ b/Sources/MockoloFramework/Templates/VariableTemplate.swift @@ -108,6 +108,7 @@ extension VariableModel { case .computed(let effects): return """ + \(1.tab)\(acl)\(staticSpace)var \(name)Handler: (() \(effects.syntax)-> \(type.typeName))? \(1.tab)\(acl)\(staticSpace)\(overrideStr)\(modifierTypeStr)var \(name): \(type.typeName) { \(2.tab)get \(effects.syntax){ \(effects.callerMarkers)\(name)Handler!() } diff --git a/Tests/TestVars/FixtureAsyncThrowsVars.swift b/Tests/TestVars/FixtureAsyncThrowsVars.swift index 86376a41..650db40d 100644 --- a/Tests/TestVars/FixtureAsyncThrowsVars.swift +++ b/Tests/TestVars/FixtureAsyncThrowsVars.swift @@ -26,14 +26,17 @@ public class AsyncThrowsVarsMock: AsyncThrowsVars { public static private(set) var getAndSetSetCallCount = 0 public static var getAndSet: Int = 0 { didSet { getAndSetSetCallCount += 1 } } + public var getAndThrowsHandler: (() throws -> Int)? public var getAndThrows: Int { get throws { try getAndThrowsHandler!() } } + public static var getAndAsyncHandler: (() async -> Int)? public static var getAndAsync: Int { get async { await getAndAsyncHandler!() } } + public var getAndAsyncAndThrowsHandler: (() async throws(any Error) -> Int)? public var getAndAsyncAndThrows: Int { get async throws(any Error) { try await getAndAsyncAndThrowsHandler!() } From 3acc4a79aa1889eb1b919dc92230ce532d901d52 Mon Sep 17 00:00:00 2001 From: Iceman Date: Fri, 25 Oct 2024 18:32:23 +0900 Subject: [PATCH 11/27] computed getter handles with the same way of method --- .../Models/ArgumentsHistoryModel.swift | 4 +- .../Models/ClosureModel.swift | 21 ++++++---- .../MockoloFramework/Models/MethodModel.swift | 17 ++++---- .../Models/ThrowingKind.swift | 32 +++++++++++++++ .../Models/VariableModel.swift | 8 +--- .../Parsers/SwiftSyntaxExtensions.swift | 36 +++++++++++++---- .../Templates/ClosureTemplate.swift | 7 +--- .../Templates/MethodTemplate.swift | 8 ++-- .../Templates/NominalTemplate.swift | 5 ++- .../Templates/VariableTemplate.swift | 39 ++++++++++++------- .../Utils/StringExtensions.swift | 3 ++ .../MockoloFramework/Utils/TypeParser.swift | 15 ++++--- .../FixtureNonSimpleFuncs.swift | 4 +- Tests/TestVars/FixtureAsyncThrowsVars.swift | 35 ++++++++++++----- 14 files changed, 163 insertions(+), 71 deletions(-) create mode 100644 Sources/MockoloFramework/Models/ThrowingKind.swift diff --git a/Sources/MockoloFramework/Models/ArgumentsHistoryModel.swift b/Sources/MockoloFramework/Models/ArgumentsHistoryModel.swift index 758e7902..a613c80b 100644 --- a/Sources/MockoloFramework/Models/ArgumentsHistoryModel.swift +++ b/Sources/MockoloFramework/Models/ArgumentsHistoryModel.swift @@ -4,7 +4,6 @@ final class ArgumentsHistoryModel: Model { var name: String var type: SwiftType var offset: Int64 = .max - let suffix: String let capturableParamNames: [String] let capturableParamTypes: [SwiftType] let isHistoryAnnotated: Bool @@ -13,7 +12,7 @@ final class ArgumentsHistoryModel: Model { return .argumentsHistory } - init?(name: String, genericTypeParams: [ParamModel], params: [ParamModel], isHistoryAnnotated: Bool, suffix: String) { + init?(name: String, genericTypeParams: [ParamModel], params: [ParamModel], isHistoryAnnotated: Bool) { // Value contains closure is not supported. let capturables = params.filter { !$0.type.hasClosure && !$0.type.isEscaping && !$0.type.isAutoclosure } guard !capturables.isEmpty else { @@ -21,7 +20,6 @@ final class ArgumentsHistoryModel: Model { } self.name = name + .argsHistorySuffix - self.suffix = suffix self.isHistoryAnnotated = isHistoryAnnotated self.capturableParamNames = capturables.map(\.name.safeName) diff --git a/Sources/MockoloFramework/Models/ClosureModel.swift b/Sources/MockoloFramework/Models/ClosureModel.swift index be03462c..4da33906 100644 --- a/Sources/MockoloFramework/Models/ClosureModel.swift +++ b/Sources/MockoloFramework/Models/ClosureModel.swift @@ -24,31 +24,36 @@ final class ClosureModel: Model { let genericTypeNames: [String] let paramNames: [String] let paramTypes: [SwiftType] - let suffix: String + let isAsync: Bool + let throwing: ThrowingKind var modelType: ModelType { return .closure } - - init(name: String, genericTypeParams: [ParamModel], paramNames: [String], paramTypes: [SwiftType], suffix: String, returnType: SwiftType, encloser: String) { + init(name: String, genericTypeParams: [ParamModel], paramNames: [String], paramTypes: [SwiftType], isAsync: Bool, throwing: ThrowingKind, returnType: SwiftType, encloser: String) { self.name = name + .handlerSuffix - self.suffix = suffix + self.isAsync = isAsync + self.throwing = throwing let genericTypeNameList = genericTypeParams.map(\.name) self.genericTypeNames = genericTypeNameList self.paramNames = paramNames self.paramTypes = paramTypes self.funcReturnType = returnType - self.type = SwiftType.toClosureType(with: paramTypes, typeParams: genericTypeNameList, suffix: suffix, returnType: returnType, encloser: encloser) + self.type = SwiftType.toClosureType( + params: paramTypes, + typeParams: genericTypeNameList, + isAsync: isAsync, + throwing: throwing, + returnType: returnType, + encloser: encloser + ) } func render(with identifier: String, encloser: String, useTemplateFunc: Bool = false, useMockObservable: Bool = false, allowSetCallCount: Bool = false, mockFinal: Bool = false, enableFuncArgsHistory: Bool = false, disableCombineDefaultValues: Bool = false) -> String? { return applyClosureTemplate(name: identifier + .handlerSuffix, - type: type, - genericTypeNames: genericTypeNames, paramVals: paramNames, paramTypes: paramTypes, - suffix: suffix, returnDefaultType: funcReturnType) } } diff --git a/Sources/MockoloFramework/Models/MethodModel.swift b/Sources/MockoloFramework/Models/MethodModel.swift index 390506e9..8698eeb3 100644 --- a/Sources/MockoloFramework/Models/MethodModel.swift +++ b/Sources/MockoloFramework/Models/MethodModel.swift @@ -38,7 +38,8 @@ final class MethodModel: Model { var modelDescription: String? = nil var isStatic: Bool let shouldOverride: Bool - let suffix: String + let isAsync: Bool + let throwing: ThrowingKind let kind: MethodKind let funcsWithArgsHistory: [String] let customModifiers: [String : Modifier] @@ -134,8 +135,7 @@ final class MethodModel: Model { let ret = ArgumentsHistoryModel(name: name, genericTypeParams: genericTypeParams, params: params, - isHistoryAnnotated: funcsWithArgsHistory.contains(name), - suffix: suffix) + isHistoryAnnotated: funcsWithArgsHistory.contains(name)) return ret }() @@ -151,7 +151,8 @@ final class MethodModel: Model { genericTypeParams: genericTypeParams, paramNames: paramNames, paramTypes: paramTypes, - suffix: suffix, + isAsync: isAsync, + throwing: throwing, returnType: type, encloser: encloser) @@ -167,8 +168,8 @@ final class MethodModel: Model { genericTypeParams: [ParamModel], genericWhereClause: String?, params: [ParamModel], - throwsOrRethrows: String?, - asyncOrReasync: String?, + isAsync: Bool, + throwing: ThrowingKind, isStatic: Bool, offset: Int64, length: Int64, @@ -178,7 +179,8 @@ final class MethodModel: Model { processed: Bool) { self.name = name.trimmingCharacters(in: .whitespaces) self.type = SwiftType(typeName.trimmingCharacters(in: .whitespaces)) - self.suffix = [asyncOrReasync, throwsOrRethrows].compactMap { $0 }.joined(separator: " ") + self.isAsync = isAsync + self.throwing = throwing self.offset = offset self.length = length self.kind = kind @@ -237,7 +239,6 @@ final class MethodModel: Model { params: params, returnType: type, accessLevel: accessLevel, - suffix: suffix, argsHistory: argsHistory, handler: handler(encloser: encloser)) return result diff --git a/Sources/MockoloFramework/Models/ThrowingKind.swift b/Sources/MockoloFramework/Models/ThrowingKind.swift new file mode 100644 index 00000000..397d3a4e --- /dev/null +++ b/Sources/MockoloFramework/Models/ThrowingKind.swift @@ -0,0 +1,32 @@ +enum ThrowingKind: Equatable { + case none + case any + case `rethrows` + case typed(errorType: String) + + var hasError: Bool { + switch self { + case .none: + return false + case .any: + return true + case .rethrows: + return true + case .typed(let errorType): + return errorType != .neverType && errorType != "Swift.\(String.neverType)" + } + } + + var syntax: String? { + switch self { + case .none: + return nil + case .any: + return .throws + case .rethrows: + return .rethrows + case .typed(let errorType): + return "\(String.throws)(\(errorType))" + } + } +} diff --git a/Sources/MockoloFramework/Models/VariableModel.swift b/Sources/MockoloFramework/Models/VariableModel.swift index 8f4db1d9..a3139b68 100644 --- a/Sources/MockoloFramework/Models/VariableModel.swift +++ b/Sources/MockoloFramework/Models/VariableModel.swift @@ -3,12 +3,8 @@ import Foundation final class VariableModel: Model { struct GetterEffects: Equatable { var isAsync: Bool - enum ThrowsType: Equatable { - case throwing(errorType: String?) - case none - } - var `throws`: ThrowsType - static let empty: GetterEffects = .init(isAsync: false, throws: .none) + var throwing: ThrowingKind + static let empty: GetterEffects = .init(isAsync: false, throwing: .none) } enum MockStorageType { diff --git a/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift b/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift index 0dead101..4e887719 100644 --- a/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift +++ b/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift @@ -380,7 +380,11 @@ extension VariableDeclSyntax { getterEffects.isAsync = true } if let `throws` = getterDecl.effectSpecifiers?.throwsClause { - getterEffects.throws = .throwing(errorType: `throws`.type?.trimmedDescription) + if let throwsType = `throws`.type { + getterEffects.throwing = .typed(errorType: throwsType.trimmedDescription) + } else { + getterEffects.throwing = .any + } } if getterEffects == .empty { storageType = .stored(needsSetCount: false) @@ -435,8 +439,8 @@ extension SubscriptDeclSyntax { genericTypeParams: genericTypeParams, genericWhereClause: genericWhereClause, params: params, - throwsOrRethrows: nil, - asyncOrReasync: nil, + isAsync: false, + throwing: .none, isStatic: isStatic, offset: self.offset, length: self.length, @@ -465,8 +469,8 @@ extension FunctionDeclSyntax { genericTypeParams: genericTypeParams, genericWhereClause: genericWhereClause, params: params, - throwsOrRethrows: self.signature.effectSpecifiers?.throwsClause?.throwsSpecifier.text, - asyncOrReasync: self.signature.effectSpecifiers?.asyncSpecifier?.text, + isAsync: self.signature.effectSpecifiers?.asyncSpecifier != nil, + throwing: .init(self.signature.effectSpecifiers?.throwsClause), isStatic: isStatic, offset: self.offset, length: self.length, @@ -506,8 +510,8 @@ extension InitializerDeclSyntax { genericTypeParams: genericTypeParams, genericWhereClause: genericWhereClause, params: params, - throwsOrRethrows: self.signature.effectSpecifiers?.throwsClause?.throwsSpecifier.text, - asyncOrReasync: self.signature.effectSpecifiers?.asyncSpecifier?.text, + isAsync: self.signature.effectSpecifiers?.asyncSpecifier != nil, + throwing: .init(self.signature.effectSpecifiers?.throwsClause), isStatic: false, offset: self.offset, length: self.length, @@ -829,3 +833,21 @@ extension Trivia { return nil } } + +extension ThrowingKind { + fileprivate init(_ syntax: ThrowsClauseSyntax?) { + guard let syntax else { + self = .none + return + } + if syntax.throwsSpecifier.tokenKind == .keyword(.rethrows) { + self = .rethrows + } else { + if let type = syntax.type { + self = .typed(errorType: type.trimmedDescription) + } else { + self = .any + } + } + } +} diff --git a/Sources/MockoloFramework/Templates/ClosureTemplate.swift b/Sources/MockoloFramework/Templates/ClosureTemplate.swift index 3bbc995a..75e0ffcd 100644 --- a/Sources/MockoloFramework/Templates/ClosureTemplate.swift +++ b/Sources/MockoloFramework/Templates/ClosureTemplate.swift @@ -18,11 +18,8 @@ import Foundation extension ClosureModel { func applyClosureTemplate(name: String, - type: SwiftType, - genericTypeNames: [String], paramVals: [String]?, paramTypes: [SwiftType]?, - suffix: String, returnDefaultType: SwiftType) -> String { var handlerParamValsStr = "" @@ -46,8 +43,8 @@ extension ClosureModel { let handlerReturnDefault = renderReturnDefaultStatement(name: name, type: returnDefaultType) let prefix = [ - suffix.hasThrowsOrRethrows ? String.try + " " : nil, - suffix.hasAsync ? String.await + " " : nil, + throwing.hasError ? String.try + " " : nil, + isAsync ? String.await + " " : nil, ].compactMap { $0 }.joined() let returnStr = returnDefaultType.typeName.isEmpty ? "" : "return " diff --git a/Sources/MockoloFramework/Templates/MethodTemplate.swift b/Sources/MockoloFramework/Templates/MethodTemplate.swift index 2276a93f..1ed05302 100644 --- a/Sources/MockoloFramework/Templates/MethodTemplate.swift +++ b/Sources/MockoloFramework/Templates/MethodTemplate.swift @@ -31,7 +31,6 @@ extension MethodModel { params: [ParamModel], returnType: SwiftType, accessLevel: String, - suffix: String, argsHistory: ArgumentsHistoryModel?, handler: ClosureModel?) -> String { var template = "" @@ -59,14 +58,17 @@ extension MethodModel { let handlerVarType = handler.type.typeName // ?? "Any" let handlerReturn = handler.render(with: identifier, encloser: "") ?? "" - let suffixStr = suffix.isEmpty ? "" : "\(suffix) " + let suffixStr = [ + isAsync ? String.async : nil, + throwing.syntax, + ].compactMap { $0 }.joined(separator: " ") + " " let returnStr = returnTypeName.isEmpty ? "" : "-> \(returnTypeName)" let staticStr = isStatic ? String.static + " " : "" let keyword = isSubscript ? "" : "func " var body = "" if useTemplateFunc { - let callMockFunc = !suffix.hasThrowsOrRethrows && (handler.type.cast?.isEmpty ?? false) + let callMockFunc = !throwing.hasError && (handler.type.cast?.isEmpty ?? false) if callMockFunc { let handlerParamValsStr = params.map { (arg) -> String in if arg.type.typeName.hasPrefix(String.autoclosure) { diff --git a/Sources/MockoloFramework/Templates/NominalTemplate.swift b/Sources/MockoloFramework/Templates/NominalTemplate.swift index cce48583..798a5e2a 100644 --- a/Sources/MockoloFramework/Templates/NominalTemplate.swift +++ b/Sources/MockoloFramework/Templates/NominalTemplate.swift @@ -198,7 +198,10 @@ extension NominalModel { let genericTypeDeclsStr = m.genericTypeParams.compactMap {$0.render(with: "", encloser: "")}.joined(separator: ", ") let genericTypesStr = genericTypeDeclsStr.isEmpty ? "" : "<\(genericTypeDeclsStr)>" let paramDeclsStr = m.params.compactMap{$0.render(with: "", encloser: "")}.joined(separator: ", ") - let suffixStr = m.suffix.isEmpty ? "" : "\(m.suffix) " + let suffixStr = [ + m.isAsync ? String.async : nil, + m.throwing.syntax, + ].compactMap { $0 }.joined(separator: " ") + " " if override { let paramsList = m.params.map { param in diff --git a/Sources/MockoloFramework/Templates/VariableTemplate.swift b/Sources/MockoloFramework/Templates/VariableTemplate.swift index ed854ce0..574f46ca 100644 --- a/Sources/MockoloFramework/Templates/VariableTemplate.swift +++ b/Sources/MockoloFramework/Templates/VariableTemplate.swift @@ -107,11 +107,27 @@ extension VariableModel { return template case .computed(let effects): + let body = (ClosureModel( + name: "", + genericTypeParams: [], + paramNames: [], + paramTypes: [], + isAsync: effects.isAsync, + throwing: effects.throwing, + returnType: type, + encloser: "" + ).render(with: name, encloser: "") ?? "") + .split(separator: "\n") + .map { "\(1.tab)\($0)" } + .joined(separator: "\n") + return """ - \(1.tab)\(acl)\(staticSpace)var \(name)Handler: (() \(effects.syntax)-> \(type.typeName))? + \(1.tab)\(acl)\(staticSpace)var \(name)\(String.handlerSuffix): (() \(effects.syntax)-> \(type.typeName))? \(1.tab)\(acl)\(staticSpace)\(overrideStr)\(modifierTypeStr)var \(name): \(type.typeName) { - \(2.tab)get \(effects.syntax){ \(effects.callerMarkers)\(name)Handler!() } + \(2.tab)get \(effects.syntax){ + \(body) + \(2.tab)} \(1.tab)} """ } @@ -346,26 +362,23 @@ extension VariableModel.GetterEffects { if isAsync { clauses.append(.async) } - switch `throws` { - case .throwing(let errorType): - if let errorType { - clauses.append("\(String.throws)(\(errorType))") - } else { - clauses.append(.throws) - } + switch throwing { case .none: break + case .any: + clauses.append(.throws) + case .rethrows: + clauses.append(.rethrows) + case .typed(let errorType): + clauses.append("\(String.throws)(\(errorType))") } return clauses.map { "\($0) " }.joined() } fileprivate var callerMarkers: String { var clauses: [String] = [] - switch `throws` { - case .throwing: + if throwing.hasError { clauses.append(.try) - case .none: - break } if isAsync { clauses.append(.await) diff --git a/Sources/MockoloFramework/Utils/StringExtensions.swift b/Sources/MockoloFramework/Utils/StringExtensions.swift index 3bfe3489..b96f3576 100644 --- a/Sources/MockoloFramework/Utils/StringExtensions.swift +++ b/Sources/MockoloFramework/Utils/StringExtensions.swift @@ -48,6 +48,7 @@ extension String { static let unknownVal = "Unknown" static let prefix = "prefix" static let anyType = "Any" + static let neverType = "Never" static let any = "any" static let some = "some" static let anyObject = "AnyObject" @@ -111,12 +112,14 @@ extension String { """ + @available(*, deprecated) var hasThrowsOrRethrows: Bool { return components(separatedBy: .whitespaces).contains { component in return component == .throws || component == .rethrows } } + @available(*, deprecated) var hasAsync: Bool { return components(separatedBy: .whitespaces).contains { component in return component == .async diff --git a/Sources/MockoloFramework/Utils/TypeParser.swift b/Sources/MockoloFramework/Utils/TypeParser.swift index b36d8648..7830dde6 100644 --- a/Sources/MockoloFramework/Utils/TypeParser.swift +++ b/Sources/MockoloFramework/Utils/TypeParser.swift @@ -512,9 +512,14 @@ public final class SwiftType { } - static func toClosureType(with params: [SwiftType], typeParams: [String], suffix: String, returnType: SwiftType, encloser: String) -> SwiftType { - - + static func toClosureType( + params: [SwiftType], + typeParams: [String], + isAsync: Bool, + throwing: ThrowingKind, + returnType: SwiftType, + encloser: String + ) -> SwiftType { let displayableParamTypes = params.map { (subtype: SwiftType) -> String in return subtype.processTypeParams(with: typeParams) } @@ -560,8 +565,8 @@ public final class SwiftType { } let suffixStr = [ - suffix.hasAsync ? String.async + " " : nil, - suffix.hasThrowsOrRethrows ? String.throws + " " : nil, + isAsync ? String.async + " " : nil, + throwing.hasError ? String.throws + " " : nil, ].compactMap { $0 }.joined() let typeStr = "((\(displayableParamStr)) \(suffixStr)-> \(displayableReturnType))?" diff --git a/Tests/TestFuncs/TestBasicFuncs/FixtureNonSimpleFuncs.swift b/Tests/TestFuncs/TestBasicFuncs/FixtureNonSimpleFuncs.swift index 9da2c024..7ecb7d5a 100644 --- a/Tests/TestFuncs/TestBasicFuncs/FixtureNonSimpleFuncs.swift +++ b/Tests/TestFuncs/TestBasicFuncs/FixtureNonSimpleFuncs.swift @@ -370,7 +370,7 @@ import Foundation /// \(String.mockAnnotation) protocol NonSimpleFuncs { -func pass(handler: @autoclosure () -> Int) rethrows -> T +func pass(handler: @autoclosure () -> Int) throws -> T } """ @@ -385,7 +385,7 @@ class NonSimpleFuncsMock: NonSimpleFuncs { private(set) var passCallCount = 0 var passHandler: ((@autoclosure () -> Int) throws -> (Any))? - func pass(handler: @autoclosure () -> Int) rethrows -> T { + func pass(handler: @autoclosure () -> Int) throws -> T { passCallCount += 1 if let passHandler = passHandler { return try passHandler(handler()) as! T diff --git a/Tests/TestVars/FixtureAsyncThrowsVars.swift b/Tests/TestVars/FixtureAsyncThrowsVars.swift index 650db40d..fc1630f6 100644 --- a/Tests/TestVars/FixtureAsyncThrowsVars.swift +++ b/Tests/TestVars/FixtureAsyncThrowsVars.swift @@ -5,8 +5,8 @@ let asyncThrowsVars = """ public protocol AsyncThrowsVars { var getOnly: Int { get } static var getAndSet: Int { get set } - var getAndThrows: Int { get throws } - static var getAndAsync: Int { get async } + var getAndThrows: MyValue { get throws } + static var getAndAsync: MyValue { get async } var getAndAsyncAndThrows: Int { get async throws(any Error) } } """ @@ -14,7 +14,7 @@ public protocol AsyncThrowsVars { let asyncThrowsVarsMock = """ public class AsyncThrowsVarsMock: AsyncThrowsVars { public init() { } - public init(getOnly: Int = 0, getAndThrows: Int = 0, getAndAsyncAndThrows: Int = 0) { + public init(getOnly: Int = 0, getAndThrows: MyValue, getAndAsyncAndThrows: Int = 0) { self.getOnly = getOnly self.getAndThrowsHandler = { getAndThrows } self.getAndAsyncAndThrowsHandler = { getAndAsyncAndThrows } @@ -27,19 +27,34 @@ public class AsyncThrowsVarsMock: AsyncThrowsVars { public static private(set) var getAndSetSetCallCount = 0 public static var getAndSet: Int = 0 { didSet { getAndSetSetCallCount += 1 } } - public var getAndThrowsHandler: (() throws -> Int)? - public var getAndThrows: Int { - get throws { try getAndThrowsHandler!() } + public var getAndThrowsHandler: (() throws -> MyValue)? + public var getAndThrows: MyValue { + get throws { + if let getAndThrowsHandler = getAndThrowsHandler { + return try getAndThrowsHandler() + } + fatalError("getAndThrowsHandler returns can't have a default value thus its handler must be set") + } } - public static var getAndAsyncHandler: (() async -> Int)? - public static var getAndAsync: Int { - get async { await getAndAsyncHandler!() } + public static var getAndAsyncHandler: (() async -> MyValue)? + public static var getAndAsync: MyValue { + get async { + if let getAndAsyncHandler = getAndAsyncHandler { + return await getAndAsyncHandler() + } + fatalError("getAndAsyncHandler returns can't have a default value thus its handler must be set") + } } public var getAndAsyncAndThrowsHandler: (() async throws(any Error) -> Int)? public var getAndAsyncAndThrows: Int { - get async throws(any Error) { try await getAndAsyncAndThrowsHandler!() } + get async throws(any Error) { + if let getAndAsyncAndThrowsHandler = getAndAsyncAndThrowsHandler { + return try await getAndAsyncAndThrowsHandler() + } + return 0 + } } } """ From c28c0275e96a1ddb73f12f77b531f9effa998616 Mon Sep 17 00:00:00 2001 From: Iceman Date: Fri, 25 Oct 2024 18:53:22 +0900 Subject: [PATCH 12/27] remove deprecated method --- .../MockoloFramework/Utils/StringExtensions.swift | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/Sources/MockoloFramework/Utils/StringExtensions.swift b/Sources/MockoloFramework/Utils/StringExtensions.swift index b96f3576..0bf2598e 100644 --- a/Sources/MockoloFramework/Utils/StringExtensions.swift +++ b/Sources/MockoloFramework/Utils/StringExtensions.swift @@ -111,21 +111,6 @@ extension String { /// """ - - @available(*, deprecated) - var hasThrowsOrRethrows: Bool { - return components(separatedBy: .whitespaces).contains { component in - return component == .throws || component == .rethrows - } - } - - @available(*, deprecated) - var hasAsync: Bool { - return components(separatedBy: .whitespaces).contains { component in - return component == .async - } - } - var safeName: String { var text = self if let keyword = text.withSyntaxText(Keyword.init), From 0028467802b48f6e53f4816d3283953def9ce6e7 Mon Sep 17 00:00:00 2001 From: Iceman Date: Fri, 25 Oct 2024 19:01:32 +0900 Subject: [PATCH 13/27] Add test for throwing never --- Tests/TestVars/FixtureAsyncThrowsVars.swift | 26 +++++++++++++++++++++ Tests/TestVars/VarTests.swift | 5 ++++ 2 files changed, 31 insertions(+) diff --git a/Tests/TestVars/FixtureAsyncThrowsVars.swift b/Tests/TestVars/FixtureAsyncThrowsVars.swift index fc1630f6..c73e579d 100644 --- a/Tests/TestVars/FixtureAsyncThrowsVars.swift +++ b/Tests/TestVars/FixtureAsyncThrowsVars.swift @@ -59,3 +59,29 @@ public class AsyncThrowsVarsMock: AsyncThrowsVars { } """ +let throwsNeverVars = """ +/// \(String.mockAnnotation) +protocol P { + var foo: Int { get throws(Never) } +} +""" + +let throwsNeverVarsMock = """ +class PMock: P { + init() { } + init(foo: Int = 0) { + self.fooHandler = { foo } + } + + + var fooHandler: (() throws(Never) -> Int)? + var foo: Int { + get throws(Never) { + if let fooHandler = fooHandler { + return fooHandler() + } + return 0 + } + } +} +""" diff --git a/Tests/TestVars/VarTests.swift b/Tests/TestVars/VarTests.swift index 44614725..42c8f11c 100644 --- a/Tests/TestVars/VarTests.swift +++ b/Tests/TestVars/VarTests.swift @@ -28,4 +28,9 @@ class VarTests: MockoloTestCase { verify(srcContent: asyncThrowsVars, dstContent: asyncThrowsVarsMock) } + + func testThrowsNever() { + verify(srcContent: throwsNeverVars, + dstContent: throwsNeverVarsMock) + } } From 802bed7a18006da6cdf751d941b25b755b4467dc Mon Sep 17 00:00:00 2001 From: Iceman Date: Fri, 25 Oct 2024 19:12:41 +0900 Subject: [PATCH 14/27] small formatting --- Sources/MockoloFramework/Templates/NominalTemplate.swift | 2 +- Sources/MockoloFramework/Templates/VariableTemplate.swift | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Sources/MockoloFramework/Templates/NominalTemplate.swift b/Sources/MockoloFramework/Templates/NominalTemplate.swift index 798a5e2a..62080c00 100644 --- a/Sources/MockoloFramework/Templates/NominalTemplate.swift +++ b/Sources/MockoloFramework/Templates/NominalTemplate.swift @@ -166,7 +166,7 @@ extension NominalModel { case .stored: return "\(2.tab)self.\(element.underlyingName) = \(element.name.safeName)" case .computed: - return "\(2.tab)self.\(element.name)Handler = { \(element.name.safeName) }" + return "\(2.tab)self.\(element.name)\(String.handlerSuffix) = { \(element.name.safeName) }" } }.joined(separator: "\n") diff --git a/Sources/MockoloFramework/Templates/VariableTemplate.swift b/Sources/MockoloFramework/Templates/VariableTemplate.swift index 574f46ca..a31366cd 100644 --- a/Sources/MockoloFramework/Templates/VariableTemplate.swift +++ b/Sources/MockoloFramework/Templates/VariableTemplate.swift @@ -77,8 +77,7 @@ extension VariableModel { switch accessorBlockItems.count { case 0: accessorBlock = "" case 1: accessorBlock = " { \(accessorBlockItems[0]) }" - default: - accessorBlock = """ + default: accessorBlock = """ { \(accessorBlockItems.map { "\(2.tab)\($0)" }.joined(separator: "\n")) \(1.tab)} From c011048666b65654a29ac70fa5e9b9944ffae266 Mon Sep 17 00:00:00 2001 From: fummicc1 Date: Sat, 26 Oct 2024 14:34:40 +0900 Subject: [PATCH 15/27] Define FunctionSuffixClause. --- Sources/MockoloFramework/Templates/FunctionSuffixTemplate.swift | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Sources/MockoloFramework/Templates/FunctionSuffixTemplate.swift diff --git a/Sources/MockoloFramework/Templates/FunctionSuffixTemplate.swift b/Sources/MockoloFramework/Templates/FunctionSuffixTemplate.swift new file mode 100644 index 00000000..e69de29b From 62cbd3c1c63f35954d3aab5f8b782faf04f9bcd7 Mon Sep 17 00:00:00 2001 From: fummicc1 Date: Sat, 26 Oct 2024 14:35:19 +0900 Subject: [PATCH 16/27] Change swift-syntax organization from apple to swiftlang --- Package.resolved | 2 +- Package.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Package.resolved b/Package.resolved index 20d72133..5042d9d6 100644 --- a/Package.resolved +++ b/Package.resolved @@ -12,7 +12,7 @@ { "identity" : "swift-syntax", "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-syntax.git", + "location" : "https://github.com/swiftlang/swift-syntax.git", "state" : { "revision" : "0687f71944021d616d34d922343dcef086855920", "version" : "600.0.1" diff --git a/Package.swift b/Package.swift index 10eda523..3e529900 100644 --- a/Package.swift +++ b/Package.swift @@ -11,7 +11,7 @@ let package = Package( .library(name: "MockoloFramework", targets: ["MockoloFramework"]), ], dependencies: [ - .package(url: "https://github.com/apple/swift-syntax.git", from: "600.0.1"), + .package(url: "https://github.com/swiftlang/swift-syntax.git", from: "600.0.1"), .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.5.0"), ], targets: [ From 909d26ecaf2585b3319a5841e7b0a5591d3a56e9 Mon Sep 17 00:00:00 2001 From: fummicc1 Date: Sat, 26 Oct 2024 14:35:56 +0900 Subject: [PATCH 17/27] Define FunctionSuffixClause --- .../Models/ArgumentsHistoryModel.swift | 4 +- .../Models/ClosureModel.swift | 4 +- .../MockoloFramework/Models/MethodModel.swift | 50 ++++++++++++++-- .../Parsers/SwiftSyntaxExtensions.swift | 57 ++++++++++++------- .../Templates/ClosureTemplate.swift | 6 +- .../Templates/FunctionSuffixTemplate.swift | 40 +++++++++++++ .../Templates/MethodTemplate.swift | 6 +- .../Templates/NominalTemplate.swift | 2 +- .../Utils/StringExtensions.swift | 27 ++++----- .../MockoloFramework/Utils/TypeParser.swift | 36 +----------- 10 files changed, 150 insertions(+), 82 deletions(-) diff --git a/Sources/MockoloFramework/Models/ArgumentsHistoryModel.swift b/Sources/MockoloFramework/Models/ArgumentsHistoryModel.swift index 758e7902..c93e9fda 100644 --- a/Sources/MockoloFramework/Models/ArgumentsHistoryModel.swift +++ b/Sources/MockoloFramework/Models/ArgumentsHistoryModel.swift @@ -4,7 +4,7 @@ final class ArgumentsHistoryModel: Model { var name: String var type: SwiftType var offset: Int64 = .max - let suffix: String + let suffix: FunctionSuffixClause? let capturableParamNames: [String] let capturableParamTypes: [SwiftType] let isHistoryAnnotated: Bool @@ -13,7 +13,7 @@ final class ArgumentsHistoryModel: Model { return .argumentsHistory } - init?(name: String, genericTypeParams: [ParamModel], params: [ParamModel], isHistoryAnnotated: Bool, suffix: String) { + init?(name: String, genericTypeParams: [ParamModel], params: [ParamModel], isHistoryAnnotated: Bool, suffix: FunctionSuffixClause?) { // Value contains closure is not supported. let capturables = params.filter { !$0.type.hasClosure && !$0.type.isEscaping && !$0.type.isAutoclosure } guard !capturables.isEmpty else { diff --git a/Sources/MockoloFramework/Models/ClosureModel.swift b/Sources/MockoloFramework/Models/ClosureModel.swift index be03462c..53206dc2 100644 --- a/Sources/MockoloFramework/Models/ClosureModel.swift +++ b/Sources/MockoloFramework/Models/ClosureModel.swift @@ -24,14 +24,14 @@ final class ClosureModel: Model { let genericTypeNames: [String] let paramNames: [String] let paramTypes: [SwiftType] - let suffix: String + let suffix: FunctionSuffixClause? var modelType: ModelType { return .closure } - init(name: String, genericTypeParams: [ParamModel], paramNames: [String], paramTypes: [SwiftType], suffix: String, returnType: SwiftType, encloser: String) { + init(name: String, genericTypeParams: [ParamModel], paramNames: [String], paramTypes: [SwiftType], suffix: FunctionSuffixClause?, returnType: SwiftType, encloser: String) { self.name = name + .handlerSuffix self.suffix = suffix let genericTypeNameList = genericTypeParams.map(\.name) diff --git a/Sources/MockoloFramework/Models/MethodModel.swift b/Sources/MockoloFramework/Models/MethodModel.swift index 390506e9..2d9f443d 100644 --- a/Sources/MockoloFramework/Models/MethodModel.swift +++ b/Sources/MockoloFramework/Models/MethodModel.swift @@ -38,7 +38,7 @@ final class MethodModel: Model { var modelDescription: String? = nil var isStatic: Bool let shouldOverride: Bool - let suffix: String + let suffix: FunctionSuffixClause? let kind: MethodKind let funcsWithArgsHistory: [String] let customModifiers: [String : Modifier] @@ -167,8 +167,8 @@ final class MethodModel: Model { genericTypeParams: [ParamModel], genericWhereClause: String?, params: [ParamModel], - throwsOrRethrows: String?, - asyncOrReasync: String?, + throwsOrRethrows: FunctionThrowsSuffix?, + asyncOrReasync: FunctionAsyncSuffix?, isStatic: Bool, offset: Int64, length: Int64, @@ -178,7 +178,10 @@ final class MethodModel: Model { processed: Bool) { self.name = name.trimmingCharacters(in: .whitespaces) self.type = SwiftType(typeName.trimmingCharacters(in: .whitespaces)) - self.suffix = [asyncOrReasync, throwsOrRethrows].compactMap { $0 }.joined(separator: " ") + self.suffix = FunctionSuffixClause( + throwsSuffix: throwsOrRethrows, + asyncSuffix: asyncOrReasync + ) self.offset = offset self.length = length self.kind = kind @@ -243,3 +246,42 @@ final class MethodModel: Model { return result } } + +/// throws, rethrows +/// +/// if throws clause has a type information, the associated value `type` is not `nil`. +struct FunctionThrowsSuffix { + let isRethrows: Bool + let type: String? +} + +/// async, reasync +struct FunctionAsyncSuffix { + var isReasync: Bool + + var text: String { + isReasync ? String.reasync : String.async + } + + var description: String { + text + } +} + +/// Function Suffix Clause such as async / throws. +/// +/// Since the support of typed throw, it is necessary to prepare a type that represents suffix in place of String type +/// due to the swift syntax's complexity. +struct FunctionSuffixClause { + var throwsSuffix: FunctionThrowsSuffix? + var asyncSuffix: FunctionAsyncSuffix? + + + init?(throwsSuffix: FunctionThrowsSuffix? = nil, asyncSuffix: FunctionAsyncSuffix? = nil) { + if throwsSuffix == nil, asyncSuffix == nil { + return nil + } + self.throwsSuffix = throwsSuffix + self.asyncSuffix = asyncSuffix + } +} diff --git a/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift b/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift index 1f83cb5c..107425e1 100644 --- a/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift +++ b/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift @@ -423,24 +423,33 @@ extension FunctionDeclSyntax { let params = self.signature.parameterClause.parameters.compactMap { $0.model(inInit: false, declType: declType) } let genericTypeParams = self.genericParameterClause?.parameters.compactMap { $0.model(inInit: false) } ?? [] let genericWhereClause = self.genericWhereClause?.description - - let funcmodel = MethodModel(name: self.name.description, - typeName: self.signature.returnClause?.type.description ?? "", - kind: .funcKind, - encloserType: declType, - acl: acl, - genericTypeParams: genericTypeParams, - genericWhereClause: genericWhereClause, - params: params, - throwsOrRethrows: self.signature.effectSpecifiers?.throwsClause?.throwsSpecifier.text, - asyncOrReasync: self.signature.effectSpecifiers?.asyncSpecifier?.text, - isStatic: isStatic, - offset: self.offset, - length: self.length, - funcsWithArgsHistory: funcsWithArgsHistory ?? [], - customModifiers: customModifiers ?? [:], - modelDescription: self.description, - processed: processed) + let asyncSpecifier = self.signature.effectSpecifiers?.asyncSpecifier + let throwsClause = self.signature.effectSpecifiers?.throwsClause + + let funcmodel = MethodModel( + name: self.name.description, + typeName: self.signature.returnClause?.type.description ?? "", + kind: .funcKind, + encloserType: declType, + acl: acl, + genericTypeParams: genericTypeParams, + genericWhereClause: genericWhereClause, + params: params, + throwsOrRethrows: throwsClause != nil ? FunctionThrowsSuffix( + isRethrows: throwsClause!.throwsSpecifier.text == String.rethrows, + type: throwsClause!.type?.description + ) : nil, + asyncOrReasync: asyncSpecifier != nil ? FunctionAsyncSuffix( + isReasync: asyncSpecifier!.text == String.rethrows + ) : nil, + isStatic: isStatic, + offset: self.offset, + length: self.length, + funcsWithArgsHistory: funcsWithArgsHistory ?? [], + customModifiers: customModifiers ?? [:], + modelDescription: self.description, + processed: processed + ) return funcmodel } } @@ -464,6 +473,8 @@ extension InitializerDeclSyntax { let params = self.signature.parameterClause.parameters.compactMap { $0.model(inInit: true, declType: declType) } let genericTypeParams = self.genericParameterClause?.parameters.compactMap { $0.model(inInit: true) } ?? [] let genericWhereClause = self.genericWhereClause?.description + let asyncSpecifier = self.signature.effectSpecifiers?.asyncSpecifier + let throwsClause = self.signature.effectSpecifiers?.throwsClause return MethodModel(name: "init", typeName: "", @@ -473,8 +484,14 @@ extension InitializerDeclSyntax { genericTypeParams: genericTypeParams, genericWhereClause: genericWhereClause, params: params, - throwsOrRethrows: self.signature.effectSpecifiers?.throwsClause?.throwsSpecifier.text, - asyncOrReasync: self.signature.effectSpecifiers?.asyncSpecifier?.text, + throwsOrRethrows: throwsClause != nil ? + FunctionThrowsSuffix( + isRethrows: throwsClause!.throwsSpecifier.text == String.rethrows, + type: throwsClause!.type?.description + ) : nil, + asyncOrReasync: asyncSpecifier != nil ? FunctionAsyncSuffix( + isReasync: asyncSpecifier!.text == String.rethrows + ) : nil, isStatic: false, offset: self.offset, length: self.length, diff --git a/Sources/MockoloFramework/Templates/ClosureTemplate.swift b/Sources/MockoloFramework/Templates/ClosureTemplate.swift index 3bbc995a..3b2737bc 100644 --- a/Sources/MockoloFramework/Templates/ClosureTemplate.swift +++ b/Sources/MockoloFramework/Templates/ClosureTemplate.swift @@ -22,7 +22,7 @@ extension ClosureModel { genericTypeNames: [String], paramVals: [String]?, paramTypes: [SwiftType]?, - suffix: String, + suffix: FunctionSuffixClause?, returnDefaultType: SwiftType) -> String { var handlerParamValsStr = "" @@ -46,8 +46,8 @@ extension ClosureModel { let handlerReturnDefault = renderReturnDefaultStatement(name: name, type: returnDefaultType) let prefix = [ - suffix.hasThrowsOrRethrows ? String.try + " " : nil, - suffix.hasAsync ? String.await + " " : nil, + suffix?.throwsSuffix != nil ? String.try + " " : nil, + suffix?.asyncSuffix != nil ? String.await + " " : nil, ].compactMap { $0 }.joined() let returnStr = returnDefaultType.typeName.isEmpty ? "" : "return " diff --git a/Sources/MockoloFramework/Templates/FunctionSuffixTemplate.swift b/Sources/MockoloFramework/Templates/FunctionSuffixTemplate.swift index e69de29b..f8b1ce0d 100644 --- a/Sources/MockoloFramework/Templates/FunctionSuffixTemplate.swift +++ b/Sources/MockoloFramework/Templates/FunctionSuffixTemplate.swift @@ -0,0 +1,40 @@ +// +// Copyright (c) 2018. Uber Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +extension FunctionSuffixClause { + func applyFunctionSuffixTemplate( + forClosureTemplate: Bool + ) -> String { + var ret: String = "" + if let asyncSuffix { + ret += asyncSuffix.isReasync ? String.reasync : String.async + } + if let throwsSuffix { + if asyncSuffix != nil { + ret += " " + } + if !forClosureTemplate, throwsSuffix.isRethrows { + ret += String.rethrows + } else { + ret += String.throws + if let throwType = throwsSuffix.type { + ret += "(\(throwType))" + } + } + } + return ret + } +} diff --git a/Sources/MockoloFramework/Templates/MethodTemplate.swift b/Sources/MockoloFramework/Templates/MethodTemplate.swift index 2276a93f..6bf8770e 100644 --- a/Sources/MockoloFramework/Templates/MethodTemplate.swift +++ b/Sources/MockoloFramework/Templates/MethodTemplate.swift @@ -31,7 +31,7 @@ extension MethodModel { params: [ParamModel], returnType: SwiftType, accessLevel: String, - suffix: String, + suffix: FunctionSuffixClause?, argsHistory: ArgumentsHistoryModel?, handler: ClosureModel?) -> String { var template = "" @@ -59,14 +59,14 @@ extension MethodModel { let handlerVarType = handler.type.typeName // ?? "Any" let handlerReturn = handler.render(with: identifier, encloser: "") ?? "" - let suffixStr = suffix.isEmpty ? "" : "\(suffix) " + let suffixStr = suffix == nil ? "" : "\(suffix!.applyFunctionSuffixTemplate(forClosureTemplate: false)) " let returnStr = returnTypeName.isEmpty ? "" : "-> \(returnTypeName)" let staticStr = isStatic ? String.static + " " : "" let keyword = isSubscript ? "" : "func " var body = "" if useTemplateFunc { - let callMockFunc = !suffix.hasThrowsOrRethrows && (handler.type.cast?.isEmpty ?? false) + let callMockFunc = suffix?.throwsSuffix == nil && (handler.type.cast?.isEmpty ?? false) if callMockFunc { let handlerParamValsStr = params.map { (arg) -> String in if arg.type.typeName.hasPrefix(String.autoclosure) { diff --git a/Sources/MockoloFramework/Templates/NominalTemplate.swift b/Sources/MockoloFramework/Templates/NominalTemplate.swift index 50a2ee23..5eba7086 100644 --- a/Sources/MockoloFramework/Templates/NominalTemplate.swift +++ b/Sources/MockoloFramework/Templates/NominalTemplate.swift @@ -195,7 +195,7 @@ extension NominalModel { let genericTypeDeclsStr = m.genericTypeParams.compactMap {$0.render(with: "", encloser: "")}.joined(separator: ", ") let genericTypesStr = genericTypeDeclsStr.isEmpty ? "" : "<\(genericTypeDeclsStr)>" let paramDeclsStr = m.params.compactMap{$0.render(with: "", encloser: "")}.joined(separator: ", ") - let suffixStr = m.suffix.isEmpty ? "" : "\(m.suffix) " + let suffixStr = m.suffix == nil ? "" : "\(m.suffix!.applyFunctionSuffixTemplate(forClosureTemplate: false)) " if override { let paramsList = m.params.map { param in diff --git a/Sources/MockoloFramework/Utils/StringExtensions.swift b/Sources/MockoloFramework/Utils/StringExtensions.swift index 38d861c1..63a9f031 100644 --- a/Sources/MockoloFramework/Utils/StringExtensions.swift +++ b/Sources/MockoloFramework/Utils/StringExtensions.swift @@ -32,6 +32,7 @@ extension String { static let `throws` = "throws" static let `rethrows` = "rethrows" static let async = "async" + static let reasync = "reasync" static let await = "await" static let `inout` = "inout" static let hasBlankInit = "_hasBlankInit" @@ -109,19 +110,19 @@ extension String { /// @Generated by Mockolo /// """ - - var hasThrowsOrRethrows: Bool { - return components(separatedBy: .whitespaces).contains { component in - let hasTypedThrow = hasPrefix(String.throws.withLeftParen) && hasSuffix(")") - return component == .throws || hasTypedThrow || component == .rethrows - } - } - - var hasAsync: Bool { - return components(separatedBy: .whitespaces).contains { component in - return component == .async - } - } +// +// var hasThrowsOrRethrows: Bool { +// return components(separatedBy: .whitespaces).contains { component in +// let hasTypedThrow = hasPrefix(String.throws.withLeftParen) && hasSuffix(")") +// return component == .throws || hasTypedThrow || component == .rethrows +// } +// } +// +// var hasAsync: Bool { +// return components(separatedBy: .whitespaces).contains { component in +// return component == .async +// } +// } var safeName: String { var text = self diff --git a/Sources/MockoloFramework/Utils/TypeParser.swift b/Sources/MockoloFramework/Utils/TypeParser.swift index e9009f6c..86606c1c 100644 --- a/Sources/MockoloFramework/Utils/TypeParser.swift +++ b/Sources/MockoloFramework/Utils/TypeParser.swift @@ -511,27 +511,7 @@ public final class SwiftType { return mutableArg } - /// Parse throws clause from suffix and return typed-throw's type. - /// - /// - Returns: if typed-throw is used, returns its concrete type, unless returns nil. - static func extractTypedThrow( - suffix: String - ) -> String? { - return suffix - .components(separatedBy: .whitespaces) - .compactMap { clause in - guard let prefixRange = clause.range(of: "\(String.throws)(") else { - return nil - } - let endIndex = clause.dropLast().endIndex - return String( - clause[prefixRange.upperBound.. SwiftType { + static func toClosureType(with params: [SwiftType], typeParams: [String], suffix: FunctionSuffixClause?, returnType: SwiftType, encloser: String) -> SwiftType { let displayableParamTypes = params.map { (subtype: SwiftType) -> String in @@ -578,19 +558,7 @@ public final class SwiftType { displayableReturnType = "(\(displayableReturnType))" } - let hasThrowsOrRethrows = suffix.hasThrowsOrRethrows - let typedThrowTypeName = extractTypedThrow(suffix: suffix) - - let thrownSuffix: String = if let typedThrowTypeName { - "\(String.throws)(\(typedThrowTypeName))" - } else { - String.throws - } - - let suffixStr = [ - suffix.hasAsync ? String.async + " " : nil, - hasThrowsOrRethrows ? thrownSuffix + " " : nil, - ].compactMap { $0 }.joined() + let suffixStr = suffix != nil ? "\(suffix!.applyFunctionSuffixTemplate(forClosureTemplate: true)) " : "" let typeStr = "((\(displayableParamStr)) \(suffixStr)-> \(displayableReturnType))?" return SwiftType(typeStr, cast: returnTypeCast) From 75a0f361af0e575ae4fb2a59bb99031874a286c6 Mon Sep 17 00:00:00 2001 From: fummicc1 Date: Sat, 26 Oct 2024 15:52:03 +0900 Subject: [PATCH 18/27] Update TestCase --- .../TestFuncThrow/FixtureFuncThrow.swift | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/Tests/TestFuncs/TestFuncThrow/FixtureFuncThrow.swift b/Tests/TestFuncs/TestFuncThrow/FixtureFuncThrow.swift index 587f3ccc..68bf3f29 100644 --- a/Tests/TestFuncs/TestFuncThrow/FixtureFuncThrow.swift +++ b/Tests/TestFuncs/TestFuncThrow/FixtureFuncThrow.swift @@ -10,6 +10,8 @@ protocol FuncThrow { func f2(arg: Int) throws func f3(arg: Int) throws(SomeError) func f4(arg: Int) throws(SomeError) -> String + func f5() throws (MyError) + func f6() async throws (any Error) func g1(arg: (Int) throws -> ()) throws -> String func g2(arg: (Int) throws -> ()) throws @@ -21,7 +23,6 @@ protocol FuncThrow { let funcThrowMock = """ - import Foundation @@ -56,7 +57,7 @@ class FuncThrowMock: FuncThrow { if let f3Handler = f3Handler { try f3Handler(arg) } - + } private(set) var f4CallCount = 0 @@ -69,6 +70,26 @@ class FuncThrowMock: FuncThrow { return "" } + private(set) var f5CallCount = 0 + var f5Handler: (() throws(MyError) -> ())? + func f5() throws(MyError) { + f5CallCount += 1 + if let f5Handler = f5Handler { + try f5Handler() + } + + } + + private(set) var f6CallCount = 0 + var f6Handler: (() async throws(any Error) -> ())? + func f6() async throws(any Error) { + f6CallCount += 1 + if let f6Handler = f6Handler { + try await f6Handler() + } + + } + private(set) var g1CallCount = 0 var g1Handler: (((Int) throws -> ()) throws -> (String))? func g1(arg: (Int) throws -> ()) throws -> String { From bac0c69ec4b5c3942e56d1bd470ac0311e069899 Mon Sep 17 00:00:00 2001 From: fummicc1 Date: Sat, 26 Oct 2024 15:55:57 +0900 Subject: [PATCH 19/27] refactoring --- Sources/MockoloFramework/Models/MethodModel.swift | 4 ---- .../Parsers/SwiftSyntaxExtensions.swift | 11 ----------- .../MockoloFramework/Utils/StringExtensions.swift | 13 ------------- 3 files changed, 28 deletions(-) diff --git a/Sources/MockoloFramework/Models/MethodModel.swift b/Sources/MockoloFramework/Models/MethodModel.swift index 2d9f443d..26f77f51 100644 --- a/Sources/MockoloFramework/Models/MethodModel.swift +++ b/Sources/MockoloFramework/Models/MethodModel.swift @@ -262,10 +262,6 @@ struct FunctionAsyncSuffix { var text: String { isReasync ? String.reasync : String.async } - - var description: String { - text - } } /// Function Suffix Clause such as async / throws. diff --git a/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift b/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift index 107425e1..49303a63 100644 --- a/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift +++ b/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift @@ -587,17 +587,6 @@ extension TypeAliasDeclSyntax { } } -extension ThrowsClauseSyntax { - - var text: String { - if let type { - "\(throwsSpecifier.text)(\(type))" - } else { - throwsSpecifier.text - } - } -} - final class EntityVisitor: SyntaxVisitor { var entities: [Entity] = [] var imports: [String: [String]] = [:] diff --git a/Sources/MockoloFramework/Utils/StringExtensions.swift b/Sources/MockoloFramework/Utils/StringExtensions.swift index 63a9f031..0250ce82 100644 --- a/Sources/MockoloFramework/Utils/StringExtensions.swift +++ b/Sources/MockoloFramework/Utils/StringExtensions.swift @@ -110,19 +110,6 @@ extension String { /// @Generated by Mockolo /// """ -// -// var hasThrowsOrRethrows: Bool { -// return components(separatedBy: .whitespaces).contains { component in -// let hasTypedThrow = hasPrefix(String.throws.withLeftParen) && hasSuffix(")") -// return component == .throws || hasTypedThrow || component == .rethrows -// } -// } -// -// var hasAsync: Bool { -// return components(separatedBy: .whitespaces).contains { component in -// return component == .async -// } -// } var safeName: String { var text = self From c00471693fc8fe29d0d82a8dfc2617cb555bee4b Mon Sep 17 00:00:00 2001 From: fummicc1 Date: Sat, 26 Oct 2024 20:57:41 +0900 Subject: [PATCH 20/27] fix: remove unused string token. --- Sources/MockoloFramework/Utils/StringExtensions.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Sources/MockoloFramework/Utils/StringExtensions.swift b/Sources/MockoloFramework/Utils/StringExtensions.swift index 3b6b9883..0bf2598e 100644 --- a/Sources/MockoloFramework/Utils/StringExtensions.swift +++ b/Sources/MockoloFramework/Utils/StringExtensions.swift @@ -32,7 +32,6 @@ extension String { static let `throws` = "throws" static let `rethrows` = "rethrows" static let async = "async" - static let reasync = "reasync" static let await = "await" static let `inout` = "inout" static let hasBlankInit = "_hasBlankInit" From de89895249c6cd0f352f33da1ef76893c34a174b Mon Sep 17 00:00:00 2001 From: fummicc1 Date: Sat, 26 Oct 2024 21:15:06 +0900 Subject: [PATCH 21/27] Fix for typed throws --- .../MockoloFramework/Templates/MethodTemplate.swift | 4 +++- .../MockoloFramework/Templates/NominalTemplate.swift | 4 +++- .../Templates/ThrowingKindTemplate.swift | 10 ++++++++-- .../MockoloFramework/Templates/VariableTemplate.swift | 5 +++-- Sources/MockoloFramework/Utils/TypeParser.swift | 2 +- 5 files changed, 18 insertions(+), 7 deletions(-) diff --git a/Sources/MockoloFramework/Templates/MethodTemplate.swift b/Sources/MockoloFramework/Templates/MethodTemplate.swift index b1abada3..bf0669d5 100644 --- a/Sources/MockoloFramework/Templates/MethodTemplate.swift +++ b/Sources/MockoloFramework/Templates/MethodTemplate.swift @@ -60,7 +60,9 @@ extension MethodModel { let suffixStr = [ isAsync ? String.async : nil, - throwing.applyThrowingTemplate(), + throwing.applyThrowingTemplate( + fromClosureTemplate: false + ), ].compactMap { $0 }.joined(separator: " ") + " " let returnStr = returnTypeName.isEmpty ? "" : "-> \(returnTypeName)" let staticStr = isStatic ? String.static + " " : "" diff --git a/Sources/MockoloFramework/Templates/NominalTemplate.swift b/Sources/MockoloFramework/Templates/NominalTemplate.swift index e6f5cefb..a51298eb 100644 --- a/Sources/MockoloFramework/Templates/NominalTemplate.swift +++ b/Sources/MockoloFramework/Templates/NominalTemplate.swift @@ -200,7 +200,9 @@ extension NominalModel { let paramDeclsStr = m.params.compactMap{$0.render(with: "", encloser: "")}.joined(separator: ", ") let suffixStr = [ m.isAsync ? String.async : nil, - m.throwing.applyThrowingTemplate(), + m.throwing.applyThrowingTemplate( + fromClosureTemplate: false + ), ].compactMap { $0 }.joined(separator: " ") + " " if override { diff --git a/Sources/MockoloFramework/Templates/ThrowingKindTemplate.swift b/Sources/MockoloFramework/Templates/ThrowingKindTemplate.swift index 8ec73a9b..f6ef860c 100644 --- a/Sources/MockoloFramework/Templates/ThrowingKindTemplate.swift +++ b/Sources/MockoloFramework/Templates/ThrowingKindTemplate.swift @@ -15,13 +15,19 @@ // extension ThrowingKind { - func applyThrowingTemplate() -> String? { + func applyThrowingTemplate( + fromClosureTemplate: Bool + ) -> String { switch self { case .none: - return nil + return "" case .any: return .throws case .rethrows: + if fromClosureTemplate { + // if this method is called to render closure, rethrows should be replaced with throws. + return .throws + } return .rethrows case .typed(let errorType): return "\(String.throws)(\(errorType))" diff --git a/Sources/MockoloFramework/Templates/VariableTemplate.swift b/Sources/MockoloFramework/Templates/VariableTemplate.swift index 0aaa9f97..27316850 100644 --- a/Sources/MockoloFramework/Templates/VariableTemplate.swift +++ b/Sources/MockoloFramework/Templates/VariableTemplate.swift @@ -361,8 +361,9 @@ extension VariableModel.GetterEffects { if isAsync { clauses.append(.async) } - if let throwSyntax = throwing.applyThrowingTemplate() { - clauses.append(throwSyntax) + let throwing = self.throwing.applyThrowingTemplate(fromClosureTemplate: false) + if throwing.isNotEmpty { + clauses.append(throwing) } return clauses.map { "\($0) " }.joined() } diff --git a/Sources/MockoloFramework/Utils/TypeParser.swift b/Sources/MockoloFramework/Utils/TypeParser.swift index 7830dde6..968da743 100644 --- a/Sources/MockoloFramework/Utils/TypeParser.swift +++ b/Sources/MockoloFramework/Utils/TypeParser.swift @@ -566,7 +566,7 @@ public final class SwiftType { let suffixStr = [ isAsync ? String.async + " " : nil, - throwing.hasError ? String.throws + " " : nil, + throwing.applyThrowingTemplate(fromClosureTemplate: true) + " ", ].compactMap { $0 }.joined() let typeStr = "((\(displayableParamStr)) \(suffixStr)-> \(displayableReturnType))?" From 1b1de5d4b64cf79a17378ce758618922cdef32d6 Mon Sep 17 00:00:00 2001 From: fummicc1 Date: Sat, 26 Oct 2024 21:50:20 +0900 Subject: [PATCH 22/27] rename argument for applyThrowingTemplate method. Add doc comment --- .../MockoloFramework/Templates/MethodTemplate.swift | 2 +- .../Templates/NominalTemplate.swift | 2 +- .../Templates/ThrowingKindTemplate.swift | 13 ++++++++++--- .../Templates/VariableTemplate.swift | 2 +- Sources/MockoloFramework/Utils/TypeParser.swift | 2 +- 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/Sources/MockoloFramework/Templates/MethodTemplate.swift b/Sources/MockoloFramework/Templates/MethodTemplate.swift index bf0669d5..c8968610 100644 --- a/Sources/MockoloFramework/Templates/MethodTemplate.swift +++ b/Sources/MockoloFramework/Templates/MethodTemplate.swift @@ -61,7 +61,7 @@ extension MethodModel { let suffixStr = [ isAsync ? String.async : nil, throwing.applyThrowingTemplate( - fromClosureTemplate: false + appliesforClosureHandler: false ), ].compactMap { $0 }.joined(separator: " ") + " " let returnStr = returnTypeName.isEmpty ? "" : "-> \(returnTypeName)" diff --git a/Sources/MockoloFramework/Templates/NominalTemplate.swift b/Sources/MockoloFramework/Templates/NominalTemplate.swift index a51298eb..fc1c2bf0 100644 --- a/Sources/MockoloFramework/Templates/NominalTemplate.swift +++ b/Sources/MockoloFramework/Templates/NominalTemplate.swift @@ -201,7 +201,7 @@ extension NominalModel { let suffixStr = [ m.isAsync ? String.async : nil, m.throwing.applyThrowingTemplate( - fromClosureTemplate: false + appliesforClosureHandler: false ), ].compactMap { $0 }.joined(separator: " ") + " " diff --git a/Sources/MockoloFramework/Templates/ThrowingKindTemplate.swift b/Sources/MockoloFramework/Templates/ThrowingKindTemplate.swift index f6ef860c..02ee2260 100644 --- a/Sources/MockoloFramework/Templates/ThrowingKindTemplate.swift +++ b/Sources/MockoloFramework/Templates/ThrowingKindTemplate.swift @@ -15,8 +15,16 @@ // extension ThrowingKind { + /// if this method is called to render handler for closure, rethrows should be replaced with throws. + /// + /// - example: + /// ``` + /// // handler shouldn't use rethrows, instead use throws + /// var fooHandler: (() -> throws -> Void) throws -> Void + /// func foo(bar: () throws -> Void) rethrows -> Void + /// ``` func applyThrowingTemplate( - fromClosureTemplate: Bool + appliesforClosureHandler: Bool ) -> String { switch self { case .none: @@ -24,8 +32,7 @@ extension ThrowingKind { case .any: return .throws case .rethrows: - if fromClosureTemplate { - // if this method is called to render closure, rethrows should be replaced with throws. + if appliesforClosureHandler { return .throws } return .rethrows diff --git a/Sources/MockoloFramework/Templates/VariableTemplate.swift b/Sources/MockoloFramework/Templates/VariableTemplate.swift index 27316850..8b6a4d88 100644 --- a/Sources/MockoloFramework/Templates/VariableTemplate.swift +++ b/Sources/MockoloFramework/Templates/VariableTemplate.swift @@ -361,7 +361,7 @@ extension VariableModel.GetterEffects { if isAsync { clauses.append(.async) } - let throwing = self.throwing.applyThrowingTemplate(fromClosureTemplate: false) + let throwing = self.throwing.applyThrowingTemplate(appliesforClosureHandler: false) if throwing.isNotEmpty { clauses.append(throwing) } diff --git a/Sources/MockoloFramework/Utils/TypeParser.swift b/Sources/MockoloFramework/Utils/TypeParser.swift index 968da743..ceba7bda 100644 --- a/Sources/MockoloFramework/Utils/TypeParser.swift +++ b/Sources/MockoloFramework/Utils/TypeParser.swift @@ -566,7 +566,7 @@ public final class SwiftType { let suffixStr = [ isAsync ? String.async + " " : nil, - throwing.applyThrowingTemplate(fromClosureTemplate: true) + " ", + throwing.applyThrowingTemplate(appliesforClosureHandler: true) + " ", ].compactMap { $0 }.joined() let typeStr = "((\(displayableParamStr)) \(suffixStr)-> \(displayableReturnType))?" From f1121102273819a4ff11f3c45142be10cde6abaf Mon Sep 17 00:00:00 2001 From: fummicc1 Date: Sun, 27 Oct 2024 09:27:17 +0900 Subject: [PATCH 23/27] Avoid to expose special parameter in a common method. --- .../MockoloFramework/Models/ClosureModel.swift | 2 ++ .../MockoloFramework/Models/ThrowingKind.swift | 10 ++++++++++ .../Templates/MethodTemplate.swift | 4 +--- .../Templates/NominalTemplate.swift | 4 +--- .../Templates/ThrowingKindTemplate.swift | 15 +-------------- .../Templates/VariableTemplate.swift | 5 ++--- Sources/MockoloFramework/Utils/TypeParser.swift | 2 +- 7 files changed, 18 insertions(+), 24 deletions(-) diff --git a/Sources/MockoloFramework/Models/ClosureModel.swift b/Sources/MockoloFramework/Models/ClosureModel.swift index 4da33906..b6295812 100644 --- a/Sources/MockoloFramework/Models/ClosureModel.swift +++ b/Sources/MockoloFramework/Models/ClosureModel.swift @@ -32,6 +32,8 @@ final class ClosureModel: Model { } init(name: String, genericTypeParams: [ParamModel], paramNames: [String], paramTypes: [SwiftType], isAsync: Bool, throwing: ThrowingKind, returnType: SwiftType, encloser: String) { + // In the mock's call handler, rethrows is unavailable. + let throwing = throwing.coerceRethrowsToThrows self.name = name + .handlerSuffix self.isAsync = isAsync self.throwing = throwing diff --git a/Sources/MockoloFramework/Models/ThrowingKind.swift b/Sources/MockoloFramework/Models/ThrowingKind.swift index 5277e5c8..e7766999 100644 --- a/Sources/MockoloFramework/Models/ThrowingKind.swift +++ b/Sources/MockoloFramework/Models/ThrowingKind.swift @@ -33,3 +33,13 @@ enum ThrowingKind: Equatable { } } } + +extension ThrowingKind { + /// Replace rethrows with throws. + var coerceRethrowsToThrows: ThrowingKind { + if case .rethrows = self { + return .any + } + return self + } +} diff --git a/Sources/MockoloFramework/Templates/MethodTemplate.swift b/Sources/MockoloFramework/Templates/MethodTemplate.swift index c8968610..b1abada3 100644 --- a/Sources/MockoloFramework/Templates/MethodTemplate.swift +++ b/Sources/MockoloFramework/Templates/MethodTemplate.swift @@ -60,9 +60,7 @@ extension MethodModel { let suffixStr = [ isAsync ? String.async : nil, - throwing.applyThrowingTemplate( - appliesforClosureHandler: false - ), + throwing.applyThrowingTemplate(), ].compactMap { $0 }.joined(separator: " ") + " " let returnStr = returnTypeName.isEmpty ? "" : "-> \(returnTypeName)" let staticStr = isStatic ? String.static + " " : "" diff --git a/Sources/MockoloFramework/Templates/NominalTemplate.swift b/Sources/MockoloFramework/Templates/NominalTemplate.swift index fc1c2bf0..e6f5cefb 100644 --- a/Sources/MockoloFramework/Templates/NominalTemplate.swift +++ b/Sources/MockoloFramework/Templates/NominalTemplate.swift @@ -200,9 +200,7 @@ extension NominalModel { let paramDeclsStr = m.params.compactMap{$0.render(with: "", encloser: "")}.joined(separator: ", ") let suffixStr = [ m.isAsync ? String.async : nil, - m.throwing.applyThrowingTemplate( - appliesforClosureHandler: false - ), + m.throwing.applyThrowingTemplate(), ].compactMap { $0 }.joined(separator: " ") + " " if override { diff --git a/Sources/MockoloFramework/Templates/ThrowingKindTemplate.swift b/Sources/MockoloFramework/Templates/ThrowingKindTemplate.swift index 02ee2260..d8787fe2 100644 --- a/Sources/MockoloFramework/Templates/ThrowingKindTemplate.swift +++ b/Sources/MockoloFramework/Templates/ThrowingKindTemplate.swift @@ -15,26 +15,13 @@ // extension ThrowingKind { - /// if this method is called to render handler for closure, rethrows should be replaced with throws. - /// - /// - example: - /// ``` - /// // handler shouldn't use rethrows, instead use throws - /// var fooHandler: (() -> throws -> Void) throws -> Void - /// func foo(bar: () throws -> Void) rethrows -> Void - /// ``` - func applyThrowingTemplate( - appliesforClosureHandler: Bool - ) -> String { + func applyThrowingTemplate() -> String { switch self { case .none: return "" case .any: return .throws case .rethrows: - if appliesforClosureHandler { - return .throws - } return .rethrows case .typed(let errorType): return "\(String.throws)(\(errorType))" diff --git a/Sources/MockoloFramework/Templates/VariableTemplate.swift b/Sources/MockoloFramework/Templates/VariableTemplate.swift index 8b6a4d88..79456215 100644 --- a/Sources/MockoloFramework/Templates/VariableTemplate.swift +++ b/Sources/MockoloFramework/Templates/VariableTemplate.swift @@ -361,9 +361,8 @@ extension VariableModel.GetterEffects { if isAsync { clauses.append(.async) } - let throwing = self.throwing.applyThrowingTemplate(appliesforClosureHandler: false) - if throwing.isNotEmpty { - clauses.append(throwing) + if throwing.applyThrowingTemplate().isNotEmpty { + clauses.append(throwing.applyThrowingTemplate()) } return clauses.map { "\($0) " }.joined() } diff --git a/Sources/MockoloFramework/Utils/TypeParser.swift b/Sources/MockoloFramework/Utils/TypeParser.swift index ceba7bda..ec26c190 100644 --- a/Sources/MockoloFramework/Utils/TypeParser.swift +++ b/Sources/MockoloFramework/Utils/TypeParser.swift @@ -566,7 +566,7 @@ public final class SwiftType { let suffixStr = [ isAsync ? String.async + " " : nil, - throwing.applyThrowingTemplate(appliesforClosureHandler: true) + " ", + throwing.applyThrowingTemplate() + " ", ].compactMap { $0 }.joined() let typeStr = "((\(displayableParamStr)) \(suffixStr)-> \(displayableReturnType))?" From 71d692025c10dc93e265f06c0a8c5b9284e6c864 Mon Sep 17 00:00:00 2001 From: fummicc1 Date: Sun, 27 Oct 2024 09:28:08 +0900 Subject: [PATCH 24/27] Update TestFixture --- Tests/TestFuncs/TestFuncThrow/FixtureFuncThrow.swift | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Tests/TestFuncs/TestFuncThrow/FixtureFuncThrow.swift b/Tests/TestFuncs/TestFuncThrow/FixtureFuncThrow.swift index 68bf3f29..c6cf2e46 100644 --- a/Tests/TestFuncs/TestFuncThrow/FixtureFuncThrow.swift +++ b/Tests/TestFuncs/TestFuncThrow/FixtureFuncThrow.swift @@ -16,6 +16,7 @@ protocol FuncThrow { throws -> String func g2(arg: (Int) throws -> ()) throws func h(arg: (Int) throws -> ()) rethrows -> String + func h2(arg: (Int) throws(SomeError) -> ()) rethrows -> String func update(arg1: T, arg2: @escaping (U) throws -> ()) throws -> ((T) -> (U)) func update(arg1: T, arg2: () throws -> T) rethrows -> T } @@ -120,6 +121,16 @@ class FuncThrowMock: FuncThrow { return "" } + private(set) var h2CallCount = 0 + var h2Handler: (((Int) throws(SomeError) -> ()) throws -> (String))? + func h2(arg: (Int) throws(SomeError) -> ()) rethrows -> String { + h2CallCount += 1 + if let h2Handler = h2Handler { + return try h2Handler(arg) + } + return "" + } + private(set) var updateCallCount = 0 var updateHandler: ((Any, Any) throws -> (Any))? func update(arg1: T, arg2: @escaping (U) throws -> ()) throws -> ((T) -> (U)) { From 733771e7251e52295add7ffdff121b0a9c0012ac Mon Sep 17 00:00:00 2001 From: fummicc1 Date: Sun, 27 Oct 2024 09:40:17 +0900 Subject: [PATCH 25/27] Keep applyThrowingTemplate nullable to consider trivia. review comment: https://github.com/uber/mockolo/pull/262#discussion_r1817862573 --- Sources/MockoloFramework/Templates/ThrowingKindTemplate.swift | 4 ++-- Sources/MockoloFramework/Templates/VariableTemplate.swift | 4 ++-- Sources/MockoloFramework/Utils/TypeParser.swift | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Sources/MockoloFramework/Templates/ThrowingKindTemplate.swift b/Sources/MockoloFramework/Templates/ThrowingKindTemplate.swift index d8787fe2..8ec73a9b 100644 --- a/Sources/MockoloFramework/Templates/ThrowingKindTemplate.swift +++ b/Sources/MockoloFramework/Templates/ThrowingKindTemplate.swift @@ -15,10 +15,10 @@ // extension ThrowingKind { - func applyThrowingTemplate() -> String { + func applyThrowingTemplate() -> String? { switch self { case .none: - return "" + return nil case .any: return .throws case .rethrows: diff --git a/Sources/MockoloFramework/Templates/VariableTemplate.swift b/Sources/MockoloFramework/Templates/VariableTemplate.swift index 79456215..0aaa9f97 100644 --- a/Sources/MockoloFramework/Templates/VariableTemplate.swift +++ b/Sources/MockoloFramework/Templates/VariableTemplate.swift @@ -361,8 +361,8 @@ extension VariableModel.GetterEffects { if isAsync { clauses.append(.async) } - if throwing.applyThrowingTemplate().isNotEmpty { - clauses.append(throwing.applyThrowingTemplate()) + if let throwSyntax = throwing.applyThrowingTemplate() { + clauses.append(throwSyntax) } return clauses.map { "\($0) " }.joined() } diff --git a/Sources/MockoloFramework/Utils/TypeParser.swift b/Sources/MockoloFramework/Utils/TypeParser.swift index ec26c190..f828c300 100644 --- a/Sources/MockoloFramework/Utils/TypeParser.swift +++ b/Sources/MockoloFramework/Utils/TypeParser.swift @@ -566,7 +566,7 @@ public final class SwiftType { let suffixStr = [ isAsync ? String.async + " " : nil, - throwing.applyThrowingTemplate() + " ", + throwing != .none ? throwing.applyThrowingTemplate()! + " " : nil, ].compactMap { $0 }.joined() let typeStr = "((\(displayableParamStr)) \(suffixStr)-> \(displayableReturnType))?" From be998e110e8c09a71d1917b5a01102675db78f74 Mon Sep 17 00:00:00 2001 From: fummicc1 Date: Sun, 27 Oct 2024 22:47:38 +0900 Subject: [PATCH 26/27] Refactor template logic for closure's suffix. --- Sources/MockoloFramework/Utils/TypeParser.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/MockoloFramework/Utils/TypeParser.swift b/Sources/MockoloFramework/Utils/TypeParser.swift index f828c300..db9f62e9 100644 --- a/Sources/MockoloFramework/Utils/TypeParser.swift +++ b/Sources/MockoloFramework/Utils/TypeParser.swift @@ -565,9 +565,9 @@ public final class SwiftType { } let suffixStr = [ - isAsync ? String.async + " " : nil, - throwing != .none ? throwing.applyThrowingTemplate()! + " " : nil, - ].compactMap { $0 }.joined() + isAsync ? String.async : nil, + throwing.applyThrowingTemplate(), + ].compactMap { $0 }.joined(separator: " ") + " " let typeStr = "((\(displayableParamStr)) \(suffixStr)-> \(displayableReturnType))?" return SwiftType(typeStr, cast: returnTypeCast) From 4a3a54a530f677409ac34d1d2e2e19006324f35b Mon Sep 17 00:00:00 2001 From: fummicc1 Date: Sun, 27 Oct 2024 22:56:02 +0900 Subject: [PATCH 27/27] Refactor function's suffix logic. --- .../Templates/MethodTemplate.swift | 8 +++--- .../Templates/NominalTemplate.swift | 8 +++--- .../Utils/FunctionSuffixTemplate.swift | 25 +++++++++++++++++++ .../MockoloFramework/Utils/TypeParser.swift | 8 +++--- 4 files changed, 37 insertions(+), 12 deletions(-) create mode 100644 Sources/MockoloFramework/Utils/FunctionSuffixTemplate.swift diff --git a/Sources/MockoloFramework/Templates/MethodTemplate.swift b/Sources/MockoloFramework/Templates/MethodTemplate.swift index b1abada3..a5a5cc0c 100644 --- a/Sources/MockoloFramework/Templates/MethodTemplate.swift +++ b/Sources/MockoloFramework/Templates/MethodTemplate.swift @@ -58,10 +58,10 @@ extension MethodModel { let handlerVarType = handler.type.typeName // ?? "Any" let handlerReturn = handler.render(with: identifier, encloser: "") ?? "" - let suffixStr = [ - isAsync ? String.async : nil, - throwing.applyThrowingTemplate(), - ].compactMap { $0 }.joined(separator: " ") + " " + let suffixStr = applyFunctionSuffixTemplate( + isAsync: isAsync, + throwing: throwing + ) let returnStr = returnTypeName.isEmpty ? "" : "-> \(returnTypeName)" let staticStr = isStatic ? String.static + " " : "" let keyword = isSubscript ? "" : "func " diff --git a/Sources/MockoloFramework/Templates/NominalTemplate.swift b/Sources/MockoloFramework/Templates/NominalTemplate.swift index e6f5cefb..63d84e9a 100644 --- a/Sources/MockoloFramework/Templates/NominalTemplate.swift +++ b/Sources/MockoloFramework/Templates/NominalTemplate.swift @@ -198,10 +198,10 @@ extension NominalModel { let genericTypeDeclsStr = m.genericTypeParams.compactMap {$0.render(with: "", encloser: "")}.joined(separator: ", ") let genericTypesStr = genericTypeDeclsStr.isEmpty ? "" : "<\(genericTypeDeclsStr)>" let paramDeclsStr = m.params.compactMap{$0.render(with: "", encloser: "")}.joined(separator: ", ") - let suffixStr = [ - m.isAsync ? String.async : nil, - m.throwing.applyThrowingTemplate(), - ].compactMap { $0 }.joined(separator: " ") + " " + let suffixStr = applyFunctionSuffixTemplate( + isAsync: m.isAsync, + throwing: m.throwing + ) if override { let paramsList = m.params.map { param in diff --git a/Sources/MockoloFramework/Utils/FunctionSuffixTemplate.swift b/Sources/MockoloFramework/Utils/FunctionSuffixTemplate.swift new file mode 100644 index 00000000..9629e3e1 --- /dev/null +++ b/Sources/MockoloFramework/Utils/FunctionSuffixTemplate.swift @@ -0,0 +1,25 @@ +// +// Copyright (c) 2018. Uber Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// Apply template for function suffix +/// +/// Suffix consists of async and throws. +func applyFunctionSuffixTemplate(isAsync: Bool, throwing: ThrowingKind) -> String { + [ + isAsync ? String.async : nil, + throwing.applyThrowingTemplate(), + ].compactMap { $0 }.joined(separator: " ") + " " +} diff --git a/Sources/MockoloFramework/Utils/TypeParser.swift b/Sources/MockoloFramework/Utils/TypeParser.swift index db9f62e9..60d102b1 100644 --- a/Sources/MockoloFramework/Utils/TypeParser.swift +++ b/Sources/MockoloFramework/Utils/TypeParser.swift @@ -564,10 +564,10 @@ public final class SwiftType { displayableReturnType = "(\(displayableReturnType))" } - let suffixStr = [ - isAsync ? String.async : nil, - throwing.applyThrowingTemplate(), - ].compactMap { $0 }.joined(separator: " ") + " " + let suffixStr = applyFunctionSuffixTemplate( + isAsync: isAsync, + throwing: throwing + ) let typeStr = "((\(displayableParamStr)) \(suffixStr)-> \(displayableReturnType))?" return SwiftType(typeStr, cast: returnTypeCast)