Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@

### Bug Fixes

* Ignore `override` functions in `async_without_await` rule.
[SimplyDanny](https://github.com/SimplyDanny)
[#6416](https://github.com/realm/SwiftLint/issues/6416)

* Fix false positive in `unneeded_escaping` rule when an escaping closure is used in
a nested closure preceded by another closure.
[SimplyDanny](https://github.com/SimplyDanny)
Expand Down
51 changes: 28 additions & 23 deletions Source/SwiftLintBuiltInRules/Rules/Lint/AsyncWithoutAwaitRule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,10 @@ private extension AsyncWithoutAwaitRule {
private var pendingAsync: TokenSyntax?

override func visit(_ node: FunctionDeclSyntax) -> SyntaxVisitorContinueKind {
guard node.body != nil else {
return .visitChildren
if node.body != nil {
let asyncToken = node.needsToKeepAsync ? nil : node.signature.effectSpecifiers?.asyncSpecifier
functionScopes.push(.init(asyncToken: asyncToken))
}

// @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
}

Expand All @@ -45,7 +40,7 @@ private extension AsyncWithoutAwaitRule {
}

override func visit(_ node: ClosureExprSyntax) -> SyntaxVisitorContinueKind {
// @concurrent closures require the async keyword even without await calls
// @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))
Expand All @@ -62,13 +57,10 @@ private extension AsyncWithoutAwaitRule {
}

override func visit(_ node: AccessorDeclSyntax) -> SyntaxVisitorContinueKind {
guard node.body != nil else {
return .visitChildren
if node.body != nil {
let asyncToken = node.needsToKeepAsync ? nil : node.effectSpecifiers?.asyncSpecifier
functionScopes.push(.init(asyncToken: asyncToken))
}

let asyncToken = node.effectSpecifiers?.asyncSpecifier
functionScopes.push(.init(asyncToken: asyncToken))

return .visitChildren
}

Expand All @@ -79,15 +71,10 @@ private extension AsyncWithoutAwaitRule {
}

override func visit(_ node: InitializerDeclSyntax) -> SyntaxVisitorContinueKind {
guard node.body != nil else {
return .visitChildren
if node.body != nil {
let asyncToken = node.needsToKeepAsync ? nil : node.signature.effectSpecifiers?.asyncSpecifier
functionScopes.push(.init(asyncToken: asyncToken))
}

// @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
}

Expand Down Expand Up @@ -163,3 +150,21 @@ private extension TypeSyntax {
return nil
}
}

private extension WithModifiersSyntax where Self: WithAttributesSyntax {
var needsToKeepAsync: Bool {
attributes.contains(attributeNamed: "concurrent") || modifiers.contains(keyword: .override)
}
}

private extension SyntaxProtocol {
var needsToKeepAsync: Bool {
if let variableDecl = `as`(VariableDeclSyntax.self) {
return variableDecl.needsToKeepAsync
}
if let subscriptDecl = `as`(SubscriptDeclSyntax.self) {
return subscriptDecl.needsToKeepAsync
}
return parent?.needsToKeepAsync ?? false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,34 @@ internal struct AsyncWithoutAwaitRuleExamples {
let c: () async -> Int = { @concurrent in 1 }
}
"""),
Example("""
class Parent {
func test() async { await foo() }
}
class Child: Parent {
override func test() async { print("Child") }
}
"""),
Example("""
class Parent {
var prop: Int {
get async { await fetchValue() }
}
}
class Child: Parent {
override var prop: Int {
get async { return 2 }
}
}
"""),
Example("""
class Base {
init() async { await setup() }
}
class Derived: Base {
override init() async { print("Derived") }
}
"""),
]

static let triggeringExamples = [
Expand Down