freda
1
I have an XCTest case that produces an intriguing warning:
func testFutureValueWithTimeout() {
let expectation = XCTestExpectation(description: "This should not fail")
DispatchQueue.global().async {
let expect = FutureResult<Int>()
do {
// This expectation is never fulfilled so should timeout
XCTAssertThrowsError(try expect.valueWhenFulfilled(timeoutValue: .now() + 1.0))
} catch {
// Ignore
}
expectation.fulfill()
}
wait(for: [expectation], timeout: 2)
}
This code produces the warning:
'catch' block is unreachable because no errors are thrown in 'do' block
The solution is obvious: "Remove the catch block"
func testFutureValueWithTimeout() {
let expectation = XCTestExpectation(description: "This should not fail")
DispatchQueue.global().async {
let expect = FutureResult<Int>()
// This expectation is never fulfilled so should timeout
XCTAssertThrowsError(try expect.valueWhenFulfilled(timeoutValue: .now() + 1.0))
expectation.fulfill()
}
wait(for: [expectation], timeout: 2)
}
which now fails with the message:
Invalid conversion from throwing function of type '() throws -> Void' to non-throwing function type '@convention(block) () -> Void'
Any suggestions?
Where does FutureResult come from?
freda
3
This is a utility class around futures. I assumed it would not be relevant to explain this compiler warning.
The signature of the method valueWhenFulfilled is as follows:
public func valueWhenFulfilled(timeoutValue: DispatchTime) throws -> T
This method does throw in case of a timeout.
But the error is caught by XCTAssertThrowsError, so there's no need to surround it in a do-catch block. Just look at the function signature. It's not a throwing function:
func XCTAssertThrowsError<T>(
_ expression: @autoclosure () throws -> T,
_ message: @autoclosure () -> String = "",
file: StaticString = #filePath,
line: UInt = #line,
_ errorHandler: (Error) -> Void = { _ in }
)
Well, it is. Show me the code for it.
freda
5
I created a minimal version that still shows the compiler warning:
public enum FutureResultError: Error {
case futureEndedInError
}
public class FutureResult<T> {
public init() {
}
public func valueWhenFulfilled(timeoutValue: DispatchTime) throws -> T {
throw(FutureResultError.futureEndedInError)
}
}
With this code I get the same warning. Also the same error when I remove the catch block.
I do understand that XCTAssertThrowsError it is not supposed to throw an error. This is exactly why I am posting this question. It seems like the compiler is confused somehow.
Looks like a compiler bug. You should report it. Make sure the example you use is fully self-contained. For example:
struct SomeError: Error { }
func throwingFunction() throws {
throw SomeError()
}
class TestCase: XCTestCase {
func testMethod() {
DispatchQueue.global().async {
XCTAssertThrowsError(try throwingFunction())
}
}
}
freda
7
Will do. It seem you already wrote the code that I need to submit 
freda
8