diff --git a/CHANGELOG.md b/CHANGELOG.md index a3df538cf3..a19859df87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/AsyncWithoutAwaitRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/AsyncWithoutAwaitRule.swift index 7b8b0755b6..b1be636fcf 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/AsyncWithoutAwaitRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/AsyncWithoutAwaitRule.swift @@ -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 } @@ -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)) @@ -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 } @@ -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 } @@ -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 + } +} diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/AsyncWithoutAwaitRuleExamples.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/AsyncWithoutAwaitRuleExamples.swift index 91b4a0112c..f1bdd32449 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/AsyncWithoutAwaitRuleExamples.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/AsyncWithoutAwaitRuleExamples.swift @@ -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 = [