Skip to content

Commit

Permalink
Add unavailable_condition rule
Browse files Browse the repository at this point in the history
Fixes #3897
  • Loading branch information
marcelofabri committed Apr 16, 2022
1 parent 291b500 commit f800663
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 0 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@
[Marcelo Fabri](https://github.com/marcelofabri)
[#3343](https://github.com/realm/SwiftLint/issues/3343)

* Add `unavailable_condition` rule to prefer using `if #unavailable` instead of
`if #available` with an empty body and an `else` condition when using
Swift 5.6 or later.
[Marcelo Fabri](https://github.com/marcelofabri)
[#3897](https://github.com/realm/SwiftLint/issues/3897)

#### Bug Fixes

* Fix false positives in `unused_closure_parameter` when using parameters with
Expand Down
1 change: 1 addition & 0 deletions Source/SwiftLintFramework/Models/PrimaryRuleList.swift
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ public let primaryRuleList = RuleList(rules: [
TypeContentsOrderRule.self,
TypeNameRule.self,
TypesafeArrayInitRule.self,
UnavailableConditionRule.self,
UnavailableFunctionRule.self,
UnneededBreakInSwitchRule.self,
UnneededParenthesesInClosureArgumentRule.self,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import SourceKittenFramework
import SwiftSyntax

public struct UnavailableConditionRule: ConfigurationProviderRule, AutomaticTestableRule {
public var configuration = SeverityConfiguration(.warning)

public init() {}

public static let description = RuleDescription(
identifier: "unavailable_condition",
name: "Unavailable Condition",
description: "Use #unavailable instead of #available with an empty body.",
kind: .idiomatic,
minSwiftVersion: .fiveDotSix,
nonTriggeringExamples: [
Example("""
if #unavailable(iOS 13) {
loadMainWindow()
}
"""),
Example("""
if #available(iOS 9.0, *) {
doSomething()
} else {
legacyDoSomething()
}
""")
],
triggeringExamples: [
Example("""
if ↓#available(iOS 14.0) {
} else {
oldIos13TrackingLogic(isEnabled: ASIdentifierManager.shared().isAdvertisingTrackingEnabled)
}
"""),
Example("""
if ↓#available(iOS 14.0) {
// we don't need to do anything here
} else {
oldIos13TrackingLogic(isEnabled: ASIdentifierManager.shared().isAdvertisingTrackingEnabled)
}
"""),
Example("""
if ↓#available(iOS 13, *) {} else {
loadMainWindow()
}
""")
]
)

public func validate(file: SwiftLintFile) -> [StyleViolation] {
guard let tree = file.syntaxTree else {
return []
}

let visitor = UnavailableConditionRuleVisitor()
visitor.walk(tree)
return visitor.positions.map { position in
StyleViolation(ruleDescription: Self.description,
severity: configuration.severity,
location: Location(file: file, byteOffset: ByteCount(position.utf8Offset)))
}
}
}

private final class UnavailableConditionRuleVisitor: SyntaxVisitor {
private(set) var positions: [AbsolutePosition] = []

override func visitPost(_ node: IfStmtSyntax) {
guard node.body.statements.withoutTrivia().isEmpty else {
return
}

guard node.conditions.count == 1, let condition = node.conditions.first,
let availability = condition.condition.as(AvailabilityConditionSyntax.self) else {
return
}

positions.append(availability.positionAfterSkippingLeadingTrivia)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -791,6 +791,12 @@ class TypesafeArrayInitRuleTests: XCTestCase {
}
}

class UnavailableConditionRuleTests: XCTestCase {
func testWithDefaultConfiguration() {
verifyRule(UnavailableConditionRule.description)
}
}

class UnavailableFunctionRuleTests: XCTestCase {
func testWithDefaultConfiguration() {
verifyRule(UnavailableFunctionRule.description)
Expand Down

0 comments on commit f800663

Please sign in to comment.