Skip to content

Support typed throws#148

Open
Simon-F-AMS wants to merge 6 commits intoMatejkob:mainfrom
Simon-F-AMS:support-typed-throws
Open

Support typed throws#148
Simon-F-AMS wants to merge 6 commits intoMatejkob:mainfrom
Simon-F-AMS:support-typed-throws

Conversation

@Simon-F-AMS
Copy link
Copy Markdown

@Simon-F-AMS Simon-F-AMS commented Sep 11, 2025

Enhancement: Support Typed Throws

Summary

SpyFactory would use throwableErrorFactory.variableDeclaration(variablePrefix: variablePrefix) to create the throwable error to always be of type (any Error)?.

Main Objective: Support typed throws

Protocol functions with typed throws should result in a throwable error that has the appropriate type in the Spy:

@Spyable
protocol ServiceProtocol {
            func foo(_ added: ((text: String) -> Void)?) throws(ExampleError) -> (() -> Int)?
        }

Should result in a spy:

class ServiceProtocolSpy: ServiceProtocol, @unchecked Sendable {
            init() {
            }
            var fooCallsCount = 0
            var fooCalled: Bool {
                return fooCallsCount > 0
            }
            var fooReceivedAdded: ((text: String) -> Void)?
            var fooReceivedInvocations: [((text: String) -> Void)?] = []
            var fooThrowableError: ExampleError? // Note that this is not (any Error)? anymore
            var fooReturnValue: (() -> Int)?
            var fooClosure: ((((text: String) -> Void)?) throws(ExampleError) -> (() -> Int)?)?
            func foo(_ added: ((text: String) -> Void)?) throws(ExampleError) -> (() -> Int)? {
                fooCallsCount += 1
                fooReceivedAdded = (added)
                fooReceivedInvocations.append((added))
                if let fooThrowableError {
                    throw fooThrowableError // As fooThrowableError now has the correct type, this compiles
                }
                if fooClosure != nil {
                    return try fooClosure!(added)
                } else {
                    return fooReturnValue
                }
            }
        }

Implemented Solution

An additional helper function typedVariableDeclaration(variablePrefix: String, typeSpecifier: String) throws -> VariableDeclSyntax was added to the ThrowableErrorFactory. It distinguishes which function to use by the optional typeSpecifier Parameter that can be passed to variableDeclaration(variablePrefix:, typeSpecifier:)

SpyFactory forwardsfunctionDeclaration.signature.effectSpecifiers?.throwsClause?.type which results in the correct type being set for the error in case the throwing clause is typed

Happy to hear your feedback on this 😊

@codecov
Copy link
Copy Markdown

codecov bot commented Sep 21, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.81%. Comparing base (c08b0ed) to head (672206c).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #148      +/-   ##
==========================================
+ Coverage   95.58%   95.81%   +0.23%     
==========================================
  Files          22       22              
  Lines        1337     1411      +74     
==========================================
+ Hits         1278     1352      +74     
  Misses         59       59              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@idolize
Copy link
Copy Markdown

idolize commented Mar 26, 2026

Any updates on this? I'd love to make use of this myself in my codebase. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants