Currently you can mark functions with something similar to:
@available(*, noasync, message: "This can block the calling thread and should not be called in an async context", renamed: "asyncAlternative()")
This will produce warnings by the compiler for anytime you call a function with this annotation in an async context. However if you call this in a new function and then call that function in an async function, you get no warning. E.g.:
@available(*, noasync, message: "This can block the calling thread and should not be called in an async context", renamed: "asyncAlternative()")
func noAsyncFunc() {
// Do something that shouldn't be done in an async context
}
func regularFunction() {
noAsyncFunc()
}
func doSomethingAsync() async {
noAsyncFunc() // This is bad and produces a warning (or error in Swift 6)
regularFunction() // This is bad but produces no warning
}
You need to manually audit your code and find all of the possible noasync calls, which is not a simple task if you have dependencies that are annotated as well. This is very error prone and seems to be a bit of a gap in the safety of Swift. I propose that noasync functions should produce transitive warnings so all these are automatically caught by the compiler.
This has come from our work in Vapor. We call a number of functions in NIO that are now marked with noasync due to the changes required by Swift Concurrency. The APIs we expose that use these functions are not marked with noasync and can cause issues. The only way for us to ensure we're doing the right thing is check every single function call to make sure it's not calling a noasync function, or wait for someone to discover a crash or issue.