Repository: Matejkob/swift-spyable (swift)

Issue:
`mutating` functions cause a compiler error
If a spyable protocol has a mutating function, the spy gets generated with the same modifier, but that's invalid for a class.

Example:

```swift
@Spyable
public protocol Client {
  mutating func logout()
}
```

Produces:

```swift
class ClientSpy: Example {
	 mutating func logout() {
	    logoutCallsCount += 1
	    logoutClosure?()
	}
}
```

Dropping `mutating` from the spy should be all that's needed since all class methods are mutating.

Pull request description:
**Context**
Fixes https://github.com/Matejkob/swift-spyable/issues/31 by stripping out `mutating` from modifiers attached to a protocol function declaration.

**Testing**
Manual check:
<img width="400" alt="Screenshot 2023-10-31 at 9 10 52 AM" src="https://github.com/Matejkob/swift-spyable/assets/10556242/51d100d0-a69b-4649-940a-468d9618157a">

Tests pass:
<img width="428" alt="Screenshot 2023-10-30 at 10 37 29 PM" src="https://github.com/Matejkob/swift-spyable/assets/10556242/abd867c1-134f-486e-aefd-4cdf3460ccda">

Patch:
diff --git a/Sources/SpyableMacro/Factories/FunctionImplementationFactory.swift b/Sources/SpyableMacro/Factories/FunctionImplementationFactory.swift
index 1fb3267..5e38ecf 100644
--- a/Sources/SpyableMacro/Factories/FunctionImplementationFactory.swift
+++ b/Sources/SpyableMacro/Factories/FunctionImplementationFactory.swift
@@ -69,7 +69,7 @@ struct FunctionImplementationFactory {
     ) -> FunctionDeclSyntax {
         FunctionDeclSyntax(
             attributes: protocolFunctionDeclaration.attributes,
-            modifiers: protocolFunctionDeclaration.modifiers,
+            modifiers: protocolFunctionDeclaration.modifiers.removingMutating,
             funcKeyword: protocolFunctionDeclaration.funcKeyword,
             name: protocolFunctionDeclaration.name,
             genericParameterClause: protocolFunctionDeclaration.genericParameterClause,
@@ -142,3 +142,11 @@ struct FunctionImplementationFactory {
         )
     }
 }
+
+private extension DeclModifierListSyntax {
+    var removingMutating: Self {
+        filter {
+            $0.name.text != TokenSyntax.keyword(.mutating).text
+        }
+    }
+}


Test patch:
diff --git a/Tests/SpyableMacroTests/Macro/UT_SpyableMacro.swift b/Tests/SpyableMacroTests/Macro/UT_SpyableMacro.swift
index a8422f2..56b7d62 100644
--- a/Tests/SpyableMacroTests/Macro/UT_SpyableMacro.swift
+++ b/Tests/SpyableMacroTests/Macro/UT_SpyableMacro.swift
@@ -28,6 +28,7 @@ final class UT_SpyableMacro: XCTestCase {
                 set
             }
 
+            mutating func logout()
             func initialize(name: String, secondName: String?)
             func fetchConfig() async throws -> [String: String]
             func fetchData(_ name: (String, count: Int)) async -> (() -> Void)
@@ -73,6 +74,15 @@ final class UT_SpyableMacro: XCTestCase {
                 }
                 var underlyingAdded: (() -> Void )!
                     var removed: (() -> Void)?
+                var logoutCallsCount = 0
+                var logoutCalled: Bool {
+                    return logoutCallsCount > 0
+                }
+                var logoutClosure: (() -> Void)?
+                func logout() {
+                    logoutCallsCount += 1
+                    logoutClosure?()
+                }
                 var initializeNameSecondNameCallsCount = 0
                 var initializeNameSecondNameCalled: Bool {
                     return initializeNameSecondNameCallsCount > 0
@@ -80,7 +90,6 @@ final class UT_SpyableMacro: XCTestCase {
                 var initializeNameSecondNameReceivedArguments: (name: String, secondName: String?)?
                 var initializeNameSecondNameReceivedInvocations: [(name: String, secondName: String?)] = []
                 var initializeNameSecondNameClosure: ((String, String?) -> Void)?
-
                     func initialize(name: String, secondName: String?) {
                     initializeNameSecondNameCallsCount += 1
                     initializeNameSecondNameReceivedArguments = (name, secondName)