Ignore @concurrent functions in async_without_await rule (#6284)

This commit is contained in:
Nandhini Subramani
2025-10-08 22:36:56 +05:30
committed by GitHub
parent 4065fc8437
commit a0414c919d
3 changed files with 44 additions and 4 deletions
+5
View File
@@ -66,6 +66,11 @@
[SimplyDanny](https://github.com/SimplyDanny)
[#6253](https://github.com/realm/SwiftLint/issues/6235)
* Exclude `@concurrent` functions from `async_without_await` rule analysis.
`@concurrent` functions requires `aysnc` in any case.
[nandhinisubbu](https://github.com/nandhinisubbu)
[#6283](https://github.com/realm/SwiftLint/issues/6283)
## 0.61.0: Even Fresher Breeze
### Breaking
@@ -30,7 +30,9 @@ private extension AsyncWithoutAwaitRule {
return .visitChildren
}
let asyncToken = node.signature.effectSpecifiers?.asyncSpecifier
// @concurrent functions require the async keyword even without await calls
let asyncToken = node.attributes.contains(attributeNamed: "concurrent")
? nil : node.signature.effectSpecifiers?.asyncSpecifier
functionScopes.push(.init(asyncToken: asyncToken))
return .visitChildren
@@ -42,8 +44,11 @@ private extension AsyncWithoutAwaitRule {
}
}
override func visit(_: ClosureExprSyntax) -> SyntaxVisitorContinueKind {
functionScopes.push(.init(asyncToken: pendingAsync))
override func visit(_ node: ClosureExprSyntax) -> SyntaxVisitorContinueKind {
// @concurrent closures require the async keyword even without await calls
let asyncToken = (node.signature?.attributes.contains(attributeNamed: "concurrent") ?? false)
? nil : pendingAsync
functionScopes.push(.init(asyncToken: asyncToken))
pendingAsync = nil
return .visitChildren
}
@@ -78,7 +83,9 @@ private extension AsyncWithoutAwaitRule {
return .visitChildren
}
let asyncToken = node.signature.effectSpecifiers?.asyncSpecifier
// @concurrent can be applied to initializers
let asyncToken = node.attributes.contains(attributeNamed: "concurrent")
? nil : node.signature.effectSpecifiers?.asyncSpecifier
functionScopes.push(.init(asyncToken: asyncToken))
return .visitChildren
@@ -160,6 +160,34 @@ internal struct AsyncWithoutAwaitRuleExamples {
}
}
""", excludeFromDocumentation: true),
Example("""
@concurrent
func concurrentFunction() async {
performWork()
}
"""),
Example("""
struct S: Sendable {
@concurrent
func alwaysSwitch() async {
// This is valid - @concurrent functions require async even without await
}
}
"""),
Example("""
struct ConcurrentInitExample {
@concurrent
init() async {
setup()
}
func setup() {}
}
"""),
Example("""
struct ConcurrentClosureExample {
let c: () async -> Int = { @concurrent in 1 }
}
"""),
]
static let triggeringExamples = [