Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add async_without_await rule #5869

Open
wants to merge 46 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
4f4288b
Create NoAsyncFuncWithoutAwaitRule.swift
jkolarik-paylocity Nov 25, 2024
ca41922
Implement
jkolarik-paylocity Nov 25, 2024
509e407
Generate
jkolarik-paylocity Nov 25, 2024
08751f1
Update CHANGELOG.md
jkolarik-paylocity Nov 25, 2024
6bb0de6
Cleanup
jkolarik-paylocity Nov 25, 2024
f6a329c
Rename
jkolarik-paylocity Nov 25, 2024
533cca7
Fix build
jkolarik-paylocity Nov 25, 2024
4ce9836
Update CHANGELOG.md
jkolarik-paylocity Nov 25, 2024
360049b
Update CHANGELOG.md
jkolarik-paylocity Nov 25, 2024
9a319a3
Fix lint
jkolarik-paylocity Nov 25, 2024
1928151
Fix rule
jkolarik-paylocity Nov 25, 2024
cd875d0
Fix lint in rule
jkolarik-paylocity Nov 25, 2024
cbb3add
Suppress warning
jkolarik-paylocity Nov 25, 2024
f89a375
Update default_rule_configurations.yml
jkolarik-paylocity Nov 25, 2024
b72a27a
Exclude by default
jkolarik-paylocity Nov 25, 2024
286335a
Remove suppressing
jkolarik-paylocity Nov 25, 2024
6c7a915
Update Source/SwiftLintBuiltInRules/Rules/Idiomatic/NoAsyncWithoutAwa…
jkolarik-paylocity Nov 25, 2024
14c0f91
Update Source/SwiftLintBuiltInRules/Rules/Idiomatic/NoAsyncWithoutAwa…
jkolarik-paylocity Nov 25, 2024
2a12f42
Update Source/SwiftLintBuiltInRules/Rules/Idiomatic/NoAsyncWithoutAwa…
jkolarik-paylocity Nov 25, 2024
aa83675
Update CHANGELOG.md
jkolarik-paylocity Nov 25, 2024
d2e3715
Update Source/SwiftLintBuiltInRules/Rules/Idiomatic/NoAsyncWithoutAwa…
jkolarik-paylocity Nov 25, 2024
cf27631
Update category
jkolarik-paylocity Nov 25, 2024
56a5629
Refactor
jkolarik-paylocity Nov 25, 2024
3b7fc03
Add Correctable
jkolarik-paylocity Nov 26, 2024
2b2c7ec
Fix closure
jkolarik-paylocity Nov 26, 2024
9acba61
Rename
jkolarik-paylocity Nov 26, 2024
bb65b2f
Fix closure
jkolarik-paylocity Nov 26, 2024
e004d54
Fix typo
jkolarik-paylocity Nov 26, 2024
c06a9ac
Fix typo
jkolarik-paylocity Nov 26, 2024
254cf8e
Add support for properties
jkolarik-paylocity Nov 26, 2024
f7998f1
Move to proper directory
jkolarik-paylocity Nov 26, 2024
9f133de
Add init async
jkolarik-paylocity Nov 26, 2024
78db870
Add subscript example
jkolarik-paylocity Nov 26, 2024
a25e4de
Refactor
jkolarik-paylocity Nov 26, 2024
a047d9e
Fix declarations
jkolarik-paylocity Nov 26, 2024
fcd47ae
Update CHANGELOG.md
jkolarik-paylocity Nov 26, 2024
d865b7b
Update Source/SwiftLintBuiltInRules/Rules/Lint/AsyncWithoutAwaitRule.…
jkolarik-paylocity Nov 26, 2024
e0e028f
Refactor
jkolarik-paylocity Nov 26, 2024
0a01cf0
Extra examples
jkolarik-paylocity Nov 26, 2024
1ebcdb7
Fix for
jkolarik-paylocity Nov 26, 2024
d6b5e9b
Update CHANGELOG.md
jkolarik-paylocity Nov 26, 2024
053939a
Fix async let
jkolarik-paylocity Nov 26, 2024
0be59db
Fix async in FunctionParameterClauseSyntax
jkolarik-paylocity Nov 26, 2024
5993523
Use FunctionParameterSyntax
jkolarik-paylocity Nov 26, 2024
7bc1df5
Fix return
jkolarik-paylocity Nov 26, 2024
6763e90
Ignore parameters
jkolarik-paylocity Nov 27, 2024
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
1 change: 1 addition & 0 deletions .swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ opt_in_rules:
- all
disabled_rules:
- anonymous_argument_in_multiline_closure
- async_without_await
- conditional_returns_on_newline
- contrasted_opening_brace
- convenience_type
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@

* Add Xcode command plugin allowing to run SwiftLint from within Xcode.
[SimplyDanny](https://github.com/SimplyDanny)

* Add new `async_without_await` opt-in rule that checks if an `async` declaration contains at least one `await`.
[Jan Kolarik](https://github.com/jkolarik-paylocity)
[#5082](https://github.com/realm/SwiftLint/issues/5082)

#### Bug Fixes

Expand Down
1 change: 1 addition & 0 deletions Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public let builtInRules: [any Rule.Type] = [
AccessibilityTraitForButtonRule.self,
AnonymousArgumentInMultilineClosureRule.self,
ArrayInitRule.self,
AsyncWithoutAwaitRule.self,
AttributeNameSpacingRule.self,
AttributesRule.self,
BalancedXCTestLifecycleRule.self,
Expand Down
158 changes: 158 additions & 0 deletions Source/SwiftLintBuiltInRules/Rules/Lint/AsyncWithoutAwaitRule.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import SwiftLintCore
import SwiftSyntax

@SwiftSyntaxRule
struct AsyncWithoutAwaitRule: SwiftSyntaxCorrectableRule, OptInRule {
var configuration = SeverityConfiguration<Self>(.warning)

static let description = RuleDescription(
identifier: "async_without_await",
name: "Async Without Await",
description: "Declaration should not be async if it doesn't use await",
kind: .lint,
nonTriggeringExamples: AsyncWithoutAwaitRuleExamples.nonTriggeringExamples,
triggeringExamples: AsyncWithoutAwaitRuleExamples.triggeringExamples,
corrections: AsyncWithoutAwaitRuleExamples.corrections
)
}
private extension AsyncWithoutAwaitRule {
private struct FuncInfo {
var containsAwait = false
let asyncToken: TokenSyntax?

init(asyncToken: TokenSyntax?) {
self.asyncToken = asyncToken
}
}

final class Visitor: ViolationsSyntaxVisitor<ConfigurationType> {
private var functionScopes = Stack<FuncInfo>()
private var pendingAsync: TokenSyntax?

override func visit(_ node: FunctionDeclSyntax) -> SyntaxVisitorContinueKind {
guard node.body != nil else {
return .visitChildren
}

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

return .visitChildren
}

override func visitPost(_ node: FunctionDeclSyntax) {
if node.body != nil {
checkViolation()
}
}

override func visit(_: ClosureExprSyntax) -> SyntaxVisitorContinueKind {
functionScopes.push(.init(asyncToken: pendingAsync))
pendingAsync = nil

return .visitChildren
}

override func visitPost(_: ClosureExprSyntax) {
checkViolation()
}

override func visitPost(_: AwaitExprSyntax) {
functionScopes.modifyLast {
$0.containsAwait = true
}
}

override func visitPost(_ node: FunctionTypeSyntax) {
if let asyncNode = node.effectSpecifiers?.asyncSpecifier {
pendingAsync = asyncNode
}
}

override func visit(_ node: AccessorDeclSyntax) -> SyntaxVisitorContinueKind {
guard node.body != nil else {
return .visitChildren
}

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

return .visitChildren
}

override func visitPost(_ node: AccessorDeclSyntax) {
if node.body != nil {
checkViolation()
}
}

override func visitPost(_: PatternBindingSyntax) {
pendingAsync = nil
}

override func visit(_ node: InitializerDeclSyntax) -> SyntaxVisitorContinueKind {
guard node.body != nil else {
return .visitChildren
}

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

return .visitChildren
}

override func visitPost(_ node: InitializerDeclSyntax) {
if node.body != nil {
checkViolation()
}
}

override func visitPost(_: TypeAliasDeclSyntax) {
pendingAsync = nil
}

override func visitPost(_ node: ForStmtSyntax) {
if node.awaitKeyword != nil {
functionScopes.modifyLast {
$0.containsAwait = true
}
}
}

override func visitPost(_ node: VariableDeclSyntax) {
if node.bindingSpecifier.tokenKind == .keyword(.let),
node.modifiers.contains(keyword: .async) {
functionScopes.modifyLast {
$0.containsAwait = true
}
}
}

override func visit(_: FunctionParameterSyntax) -> SyntaxVisitorContinueKind {
.skipChildren
}

override func visitPost(_: ReturnClauseSyntax) {
pendingAsync = nil
}

private func checkViolation() {
guard
let info = functionScopes.pop(),
let asyncToken = info.asyncToken,
!info.containsAwait
else {
return
}

violations.append(
at: asyncToken.positionAfterSkippingLeadingTrivia,
correction: .init(
start: asyncToken.positionAfterSkippingLeadingTrivia,
end: asyncToken.endPosition,
replacement: ""
)
)
}
}
}
Loading