mirror of
https://github.com/realm/SwiftLint.git
synced 2026-06-06 20:18:40 +00:00
@@ -24,6 +24,11 @@
|
||||
[Marcelo Fabri](https://github.com/marcelofabri)
|
||||
[#2365](https://github.com/realm/SwiftLint/pull/2365)
|
||||
|
||||
* Add `inert_defer` rule to validate that `defer` is not used at the end of a
|
||||
scope.
|
||||
[Marcelo Fabri](https://github.com/marcelofabri)
|
||||
[#2123](https://github.com/realm/SwiftLint/issues/2123)
|
||||
|
||||
#### Bug Fixes
|
||||
|
||||
* Fix `comma` rule false positives on object literals (for example, images).
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
* [Implicit Getter](#implicit-getter)
|
||||
* [Implicit Return](#implicit-return)
|
||||
* [Implicitly Unwrapped Optional](#implicitly-unwrapped-optional)
|
||||
* [Inert Defer](#inert-defer)
|
||||
* [Is Disjoint](#is-disjoint)
|
||||
* [Joined Default Parameter](#joined-default-parameter)
|
||||
* [Large Tuple](#large-tuple)
|
||||
@@ -8213,6 +8214,66 @@ func foo(int: Int!) {}
|
||||
|
||||
|
||||
|
||||
## Inert Defer
|
||||
|
||||
Identifier | Enabled by default | Supports autocorrection | Kind | Minimum Swift Compiler Version
|
||||
--- | --- | --- | --- | ---
|
||||
`inert_defer` | Enabled | No | lint | 3.0.0
|
||||
|
||||
If defer is at the end of its parent scope, it will be executed right where it is anyway.
|
||||
|
||||
### Examples
|
||||
|
||||
<details>
|
||||
<summary>Non Triggering Examples</summary>
|
||||
|
||||
```swift
|
||||
func example3() {
|
||||
defer { /* deferred code */ }
|
||||
|
||||
print("other code")
|
||||
}
|
||||
```
|
||||
|
||||
```swift
|
||||
func example4() {
|
||||
if condition {
|
||||
defer { /* deferred code */ }
|
||||
print("other code")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>Triggering Examples</summary>
|
||||
|
||||
```swift
|
||||
func example0() {
|
||||
↓defer { /* deferred code */ }
|
||||
}
|
||||
```
|
||||
|
||||
```swift
|
||||
func example1() {
|
||||
↓defer { /* deferred code */ }
|
||||
// comment
|
||||
}
|
||||
```
|
||||
|
||||
```swift
|
||||
func example2() {
|
||||
if condition {
|
||||
↓defer { /* deferred code */ }
|
||||
// comment
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
|
||||
## Is Disjoint
|
||||
|
||||
Identifier | Enabled by default | Supports autocorrection | Kind | Minimum Swift Compiler Version
|
||||
|
||||
@@ -57,6 +57,7 @@ public let masterRuleList = RuleList(rules: [
|
||||
ImplicitGetterRule.self,
|
||||
ImplicitReturnRule.self,
|
||||
ImplicitlyUnwrappedOptionalRule.self,
|
||||
InertDeferRule.self,
|
||||
IsDisjointRule.self,
|
||||
JoinedDefaultParameterRule.self,
|
||||
LargeTupleRule.self,
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
import Foundation
|
||||
import SourceKittenFramework
|
||||
|
||||
public struct InertDeferRule: ConfigurationProviderRule, AutomaticTestableRule {
|
||||
public var configuration = SeverityConfiguration(.warning)
|
||||
|
||||
public init() {}
|
||||
|
||||
public static let description = RuleDescription(
|
||||
identifier: "inert_defer",
|
||||
name: "Inert Defer",
|
||||
description: "If defer is at the end of its parent scope, it will be executed right where it is anyway.",
|
||||
kind: .lint,
|
||||
nonTriggeringExamples: [
|
||||
"""
|
||||
func example3() {
|
||||
defer { /* deferred code */ }
|
||||
|
||||
print("other code")
|
||||
}
|
||||
""",
|
||||
"""
|
||||
func example4() {
|
||||
if condition {
|
||||
defer { /* deferred code */ }
|
||||
print("other code")
|
||||
}
|
||||
}
|
||||
"""
|
||||
],
|
||||
triggeringExamples: [
|
||||
"""
|
||||
func example0() {
|
||||
↓defer { /* deferred code */ }
|
||||
}
|
||||
""",
|
||||
"""
|
||||
func example1() {
|
||||
↓defer { /* deferred code */ }
|
||||
// comment
|
||||
}
|
||||
""",
|
||||
"""
|
||||
func example2() {
|
||||
if condition {
|
||||
↓defer { /* deferred code */ }
|
||||
// comment
|
||||
}
|
||||
}
|
||||
"""
|
||||
]
|
||||
)
|
||||
|
||||
public func validate(file: File) -> [StyleViolation] {
|
||||
let defers = file.match(pattern: "defer\\s*\\{", with: [.keyword])
|
||||
|
||||
return defers.compactMap { range -> StyleViolation? in
|
||||
let contents = file.contents.bridge()
|
||||
guard let byteRange = contents.NSRangeToByteRange(start: range.location, length: range.length),
|
||||
case let kinds = file.structure.kinds(forByteOffset: byteRange.upperBound),
|
||||
let brace = kinds.enumerated().lazy.reversed().first(where: isBrace),
|
||||
brace.offset > kinds.startIndex,
|
||||
case let outerKindIndex = kinds.index(before: brace.offset),
|
||||
case let outerKind = kinds[outerKindIndex],
|
||||
case let braceEnd = brace.element.byteRange.upperBound,
|
||||
case let tokensRange = NSRange(location: braceEnd, length: outerKind.byteRange.upperBound - braceEnd),
|
||||
case let tokens = file.syntaxMap.tokens(inByteRange: tokensRange),
|
||||
!tokens.contains(where: isNotComment) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return StyleViolation(ruleDescription: type(of: self).description,
|
||||
severity: configuration.severity,
|
||||
location: Location(file: file, characterOffset: range.location))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func isBrace(offset: Int, element: (kind: String, byteRange: NSRange)) -> Bool {
|
||||
return StatementKind(rawValue: element.kind) == .brace
|
||||
}
|
||||
|
||||
private func isNotComment(token: SyntaxToken) -> Bool {
|
||||
guard let kind = SyntaxKind(rawValue: token.type) else {
|
||||
return false
|
||||
}
|
||||
|
||||
return !SyntaxKind.commentKinds.contains(kind)
|
||||
}
|
||||
@@ -213,6 +213,7 @@
|
||||
D44037972132730000FDA77B /* ProhibitedInterfaceBuilderRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D44037962132730000FDA77B /* ProhibitedInterfaceBuilderRule.swift */; };
|
||||
D44254201DB87CA200492EA4 /* ValidIBInspectableRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D442541E1DB87C3D00492EA4 /* ValidIBInspectableRule.swift */; };
|
||||
D44254271DB9C15C00492EA4 /* SyntacticSugarRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D44254251DB9C12300492EA4 /* SyntacticSugarRule.swift */; };
|
||||
D4441A28213279950020896F /* InertDeferRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4441A27213279950020896F /* InertDeferRule.swift */; };
|
||||
D4470D571EB69225008A1B2E /* ImplicitReturnRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4470D561EB69225008A1B2E /* ImplicitReturnRule.swift */; };
|
||||
D4470D591EB6B4D1008A1B2E /* EmptyEnumArgumentsRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4470D581EB6B4D1008A1B2E /* EmptyEnumArgumentsRule.swift */; };
|
||||
D4470D5B1EB76F44008A1B2E /* UnusedOptionalBindingRuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4470D5A1EB76F44008A1B2E /* UnusedOptionalBindingRuleTests.swift */; };
|
||||
@@ -637,6 +638,7 @@
|
||||
D44037962132730000FDA77B /* ProhibitedInterfaceBuilderRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProhibitedInterfaceBuilderRule.swift; sourceTree = "<group>"; };
|
||||
D442541E1DB87C3D00492EA4 /* ValidIBInspectableRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidIBInspectableRule.swift; sourceTree = "<group>"; };
|
||||
D44254251DB9C12300492EA4 /* SyntacticSugarRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyntacticSugarRule.swift; sourceTree = "<group>"; };
|
||||
D4441A27213279950020896F /* InertDeferRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InertDeferRule.swift; sourceTree = "<group>"; };
|
||||
D4470D561EB69225008A1B2E /* ImplicitReturnRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImplicitReturnRule.swift; sourceTree = "<group>"; };
|
||||
D4470D581EB6B4D1008A1B2E /* EmptyEnumArgumentsRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmptyEnumArgumentsRule.swift; sourceTree = "<group>"; };
|
||||
D4470D5A1EB76F44008A1B2E /* UnusedOptionalBindingRuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnusedOptionalBindingRuleTests.swift; sourceTree = "<group>"; };
|
||||
@@ -947,6 +949,7 @@
|
||||
E315B83B1DFA4BC500621B44 /* DynamicInlineRule.swift */,
|
||||
62A3E95B209E078000547A86 /* EmptyXCTestMethodRule.swift */,
|
||||
626B01B420A1735900D2C42F /* EmptyXCTestMethodRuleExamples.swift */,
|
||||
D4441A27213279950020896F /* InertDeferRule.swift */,
|
||||
C26330352073DAA200D7B4FD /* LowerACLThanParentRule.swift */,
|
||||
856651A61D6B395F005E6B29 /* MarkRule.swift */,
|
||||
F9E691272091952E0085B53E /* MissingDocsRule.swift */,
|
||||
@@ -1942,6 +1945,7 @@
|
||||
D4D5A5FF1E1F3A1C00D15E0C /* ShorthandOperatorRule.swift in Sources */,
|
||||
C3DE5DAC1E7DF9CA00761483 /* FatalErrorMessageRule.swift in Sources */,
|
||||
626C16E21F948EBC00BB7475 /* QuickDiscouragedFocusedTestRuleExamples.swift in Sources */,
|
||||
D4441A28213279950020896F /* InertDeferRule.swift in Sources */,
|
||||
B89F3BCF1FD5EE1400931E59 /* RequiredEnumCaseRuleConfiguration.swift in Sources */,
|
||||
D48B51211F4F5DEF0068AB98 /* RuleList+Documentation.swift in Sources */,
|
||||
8FC9F5111F4B8E48006826C1 /* IsDisjointRule.swift in Sources */,
|
||||
|
||||
@@ -531,6 +531,12 @@ extension ImplicitlyUnwrappedOptionalRuleTests {
|
||||
]
|
||||
}
|
||||
|
||||
extension InertDeferRuleTests {
|
||||
static var allTests: [(String, (InertDeferRuleTests) -> () throws -> Void)] = [
|
||||
("testWithDefaultConfiguration", testWithDefaultConfiguration)
|
||||
]
|
||||
}
|
||||
|
||||
extension IntegrationTests {
|
||||
static var allTests: [(String, (IntegrationTests) -> () throws -> Void)] = [
|
||||
("testSwiftLintLints", testSwiftLintLints),
|
||||
@@ -1306,6 +1312,7 @@ XCTMain([
|
||||
testCase(ImplicitReturnRuleTests.allTests),
|
||||
testCase(ImplicitlyUnwrappedOptionalConfigurationTests.allTests),
|
||||
testCase(ImplicitlyUnwrappedOptionalRuleTests.allTests),
|
||||
testCase(InertDeferRuleTests.allTests),
|
||||
testCase(IntegrationTests.allTests),
|
||||
testCase(IsDisjointRuleTests.allTests),
|
||||
testCase(JoinedDefaultParameterRuleTests.allTests),
|
||||
|
||||
@@ -246,6 +246,12 @@ class ImplicitReturnRuleTests: XCTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
class InertDeferRuleTests: XCTestCase {
|
||||
func testWithDefaultConfiguration() {
|
||||
verifyRule(InertDeferRule.description)
|
||||
}
|
||||
}
|
||||
|
||||
class IsDisjointRuleTests: XCTestCase {
|
||||
func testWithDefaultConfiguration() {
|
||||
verifyRule(IsDisjointRule.description)
|
||||
|
||||
Reference in New Issue
Block a user