diff --git a/Sources/IssueReporting/ErrorReporting.swift b/Sources/IssueReporting/ErrorReporting.swift index f83df31..a48974c 100644 --- a/Sources/IssueReporting/ErrorReporting.swift +++ b/Sources/IssueReporting/ErrorReporting.swift @@ -56,6 +56,38 @@ public func withErrorReporting( } } +/// Evaluates a throwing closure and automatically catches and reports any error thrown. +/// +/// - Parameters: +/// - message: A message describing the expectation. +/// - reporters: Issue reporters to notify during the operation. +/// - fileID: The source `#fileID` associated with the error reporting. +/// - filePath: The source `#filePath` associated with the error reporting. +/// - line: The source `#line` associated with the error reporting. +/// - column: The source `#column` associated with the error reporting. +/// - body: A synchronous operation. +/// - Returns: The optional result of the operation, or `nil` if an error was thrown. +@_transparent +public func withErrorReporting( + _ message: @autoclosure () -> String? = nil, + to reporters: [any IssueReporter]? = nil, + fileID: StaticString = #fileID, + filePath: StaticString = #filePath, + line: UInt = #line, + column: UInt = #column, + catching body: () throws -> R? +) -> R? { + (withErrorReporting( + message(), + to: reporters, + fileID: fileID, + filePath: filePath, + line: line, + column: column, + catching: body + ) as R??) ?? nil +} + #if compiler(>=6) /// Evaluates a throwing closure and automatically catches and reports any error thrown. /// @@ -113,6 +145,42 @@ public func withErrorReporting( } } } + + /// Evaluates a throwing closure and automatically catches and reports any error thrown. + /// + /// - Parameters: + /// - message: A message describing the expectation. + /// - reporters: Issue reporters to notify during the operation. + /// - fileID: The source `#fileID` associated with the error reporting. + /// - filePath: The source `#filePath` associated with the error reporting. + /// - line: The source `#line` associated with the error reporting. + /// - column: The source `#column` associated with the error reporting. + /// - isolation: The isolation associated with the error reporting. + /// - body: An asynchronous operation. + /// - Returns: The optional result of the operation, or `nil` if an error was thrown. + @_transparent + public func withErrorReporting( + _ message: @autoclosure () -> String? = nil, + to reporters: [any IssueReporter]? = nil, + fileID: StaticString = #fileID, + filePath: StaticString = #filePath, + line: UInt = #line, + column: UInt = #column, + isolation: isolated (any Actor)? = #isolation, + // DO NOT FIX THE WHITESPACE IN THE NEXT LINE UNTIL 5.10 IS UNSUPPORTED + // https://github.com/swiftlang/swift/issues/79285 + catching body: () async throws -> sending R? + ) async -> R? { + (await withErrorReporting( + message(), + to: reporters, + fileID: fileID, + filePath: filePath, + line: line, + column: column, + catching: body + ) as R??) ?? nil + } #else @_transparent @_unsafeInheritExecutor @@ -157,4 +225,26 @@ public func withErrorReporting( } } } + + @_transparent + @_unsafeInheritExecutor + public func withErrorReporting( + _ message: @autoclosure () -> String? = nil, + to reporters: [any IssueReporter]? = nil, + fileID: StaticString = #fileID, + filePath: StaticString = #filePath, + line: UInt = #line, + column: UInt = #column, + catching body: () async throws -> R? + ) async -> R? { + (await withErrorReporting( + message(), + to: reporters, + fileID: fileID, + filePath: filePath, + line: line, + column: column, + catching: body + ) as R??) ?? nil + } #endif diff --git a/Tests/IssueReportingTests/WithErrorReportingTests.swift b/Tests/IssueReportingTests/WithErrorReportingTests.swift index c54b533..445bb9d 100644 --- a/Tests/IssueReportingTests/WithErrorReportingTests.swift +++ b/Tests/IssueReportingTests/WithErrorReportingTests.swift @@ -40,6 +40,28 @@ } } + @Test func squashOptionalSync() async { + withKnownIssue { + let _: Int? = withErrorReporting { () -> Int? in + throw SomeError() + } + } matching: { issue in + issue.description == "Caught error: SomeError()\(issueDescriptionSuffix)" + } + } + + + @Test func squashOptionalAsync() async { + await withKnownIssue { + let _: Int? = await withErrorReporting { () -> Int? in + await Task.yield() + throw SomeError() + } + } matching: { issue in + issue.description == "Caught error: SomeError()\(issueDescriptionSuffix)" + } + } + #if compiler(<6.2) @MainActor @Test func isolation() async {