Skip to content

Commit

Permalink
Add empty_collection_literal rule
Browse files Browse the repository at this point in the history
  • Loading branch information
cltnschlosser committed Aug 29, 2019
1 parent 0d18758 commit 130371b
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 1 deletion.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
expressions like `filter(where:).isEmpty` instead of `contains(where:)`.
[Marcelo Fabri](https://github.com/marcelofabri)

* Add `empty_collection_literal` opt-in rule to prefer using `isEmpty` to comparison to `[]` or `[:]`.
[Colton Schlosser](https://github.com/cltnschlosser)
[#2807](https://github.com/realm/SwiftLint/issues/2807)

#### Bug Fixes

* Fixed false positive in `colon` rule inside guard and ternary operator.
Expand Down
70 changes: 70 additions & 0 deletions Rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
* [Duplicate Enum Cases](#duplicate-enum-cases)
* [Duplicate Imports](#duplicate-imports)
* [Dynamic Inline](#dynamic-inline)
* [Empty Collection Literal](#empty-collection-literal)
* [Empty Count](#empty-count)
* [Empty Enum Arguments](#empty-enum-arguments)
* [Empty Parameters](#empty-parameters)
Expand Down Expand Up @@ -5398,6 +5399,75 @@ dynamic



## Empty Collection Literal

Identifier | Enabled by default | Supports autocorrection | Kind | Analyzer | Minimum Swift Compiler Version
--- | --- | --- | --- | --- | ---
`empty_collection_literal` | Disabled | No | performance | No | 3.0.0

Prefer checking `isEmpty` over comparing collection to an empty array or dictionary literal.

### Examples

<details>
<summary>Non Triggering Examples</summary>

```swift
myArray = []
```

```swift
myArray.isEmpty
```

```swift
!myArray.isEmpy
```

```swift
myDict = [:]
```

</details>
<details>
<summary>Triggering Examples</summary>

```swift
myArray == []
```

```swift
myArray != []
```

```swift
myArray == [ ]
```

```swift
myDict == [:]
```

```swift
myDict != [:]
```

```swift
myDict == [: ]
```

```swift
myDict == [ :]
```

```swift
myDict == [ : ]
```

</details>



## Empty Count

Identifier | Enabled by default | Supports autocorrection | Kind | Analyzer | Minimum Swift Compiler Version
Expand Down
3 changes: 2 additions & 1 deletion Source/SwiftLintFramework/Models/MasterRuleList.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Generated using Sourcery 0.16.2 — https://github.com/krzysztofzablocki/Sourcery
// Generated using Sourcery 0.16.1 — https://github.com/krzysztofzablocki/Sourcery
// DO NOT EDIT

public let masterRuleList = RuleList(rules: [
Expand Down Expand Up @@ -33,6 +33,7 @@ public let masterRuleList = RuleList(rules: [
DuplicateEnumCasesRule.self,
DuplicateImportsRule.self,
DynamicInlineRule.self,
EmptyCollectionLiteralRule.self,
EmptyCountRule.self,
EmptyEnumArgumentsRule.self,
EmptyParametersRule.self,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import SourceKittenFramework

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

public init() {}

public static let description = RuleDescription(
identifier: "empty_collection_literal",
name: "Empty Collection Literal",
description: "Prefer checking `isEmpty` over comparing collection to an empty array or dictionary literal.",
kind: .performance,
nonTriggeringExamples: [
"myArray = []",
"myArray.isEmpty",
"!myArray.isEmpy",
"myDict = [:]"
],
triggeringExamples: [
"myArray↓ == []",
"myArray↓ != []",
"myArray↓ == [ ]",
"myDict↓ == [:]",
"myDict↓ != [:]",
"myDict↓ == [: ]",
"myDict↓ == [ :]",
"myDict↓ == [ : ]"
]
)

public func validate(file: File) -> [StyleViolation] {
let pattern = "\\b\\s*(==|!=)\\s*\\[\\s*:?\\s*\\]"
let excludingKinds = SyntaxKind.commentAndStringKinds
return file.match(pattern: pattern, excludingSyntaxKinds: excludingKinds).map {
StyleViolation(ruleDescription: type(of: self).description,
severity: configuration.severity,
location: Location(file: file, characterOffset: $0.location))
}
}
}
4 changes: 4 additions & 0 deletions SwiftLint.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
4DB7815E1CAD72BA00BC4723 /* LegacyCGGeometryFunctionsRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DB7815C1CAD690100BC4723 /* LegacyCGGeometryFunctionsRule.swift */; };
4DCB8E7F1CBE494E0070FCF0 /* RegexHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DCB8E7D1CBE43640070FCF0 /* RegexHelpers.swift */; };
4E342B4C2215C793008E4EF8 /* ReduceBooleanRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E342B4A2215C6DF008E4EF8 /* ReduceBooleanRule.swift */; };
550DA0E022DB95AD00B67F03 /* EmptyCollectionLiteralRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 550DA0DE22DB958400B67F03 /* EmptyCollectionLiteralRule.swift */; };
57ED827B1CF656E3002B3513 /* JUnitReporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57ED82791CF65183002B3513 /* JUnitReporter.swift */; };
584B0D3A2112BA78002F7E25 /* SonarQubeReporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 584B0D392112BA78002F7E25 /* SonarQubeReporter.swift */; };
584B0D3C2112E8FB002F7E25 /* CannedSonarQubeReporterOutput.json in Resources */ = {isa = PBXBuildFile; fileRef = 584B0D3B2112E8FB002F7E25 /* CannedSonarQubeReporterOutput.json */; };
Expand Down Expand Up @@ -569,6 +570,7 @@
4E342B4A2215C6DF008E4EF8 /* ReduceBooleanRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReduceBooleanRule.swift; sourceTree = "<group>"; };
5499CA961A2394B700783309 /* Components.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Components.plist; sourceTree = "<group>"; };
5499CA971A2394B700783309 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
550DA0DE22DB958400B67F03 /* EmptyCollectionLiteralRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyCollectionLiteralRule.swift; sourceTree = "<group>"; };
57ED82791CF65183002B3513 /* JUnitReporter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JUnitReporter.swift; sourceTree = "<group>"; };
584B0D392112BA78002F7E25 /* SonarQubeReporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SonarQubeReporter.swift; sourceTree = "<group>"; };
584B0D3B2112E8FB002F7E25 /* CannedSonarQubeReporterOutput.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = CannedSonarQubeReporterOutput.json; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1079,6 +1081,7 @@
29AD4C641F6EA16C009B66E1 /* ContainsOverFirstNotNilRule.swift */,
D489B546231233490090BAA0 /* ContainsOverFilterCountRule.swift */,
D489B549231383350090BAA0 /* ContainsOverFilterIsEmptyRule.swift */,
550DA0DE22DB958400B67F03 /* EmptyCollectionLiteralRule.swift */,
E847F0A81BFBBABD00EA9363 /* EmptyCountRule.swift */,
740DF1AF203F5AFC0081F694 /* EmptyStringRule.swift */,
D42D2B371E09CC0D00CD7A2E /* FirstWhereRule.swift */,
Expand Down Expand Up @@ -2025,6 +2028,7 @@
125AAC78203AA82D0004BCE0 /* ExplicitTypeInterfaceConfiguration.swift in Sources */,
1E18574B1EADBA51004F89F7 /* NoExtensionAccessModifierRule.swift in Sources */,
D42D2B381E09CC0D00CD7A2E /* FirstWhereRule.swift in Sources */,
550DA0E022DB95AD00B67F03 /* EmptyCollectionLiteralRule.swift in Sources */,
D4B0226F1E0C75F9007E5297 /* VerticalParameterAlignmentRule.swift in Sources */,
E8BDE3FF1EDF91B6002EC12F /* RuleList.swift in Sources */,
E889D8C71F1D357B00058332 /* Configuration+Merging.swift in Sources */,
Expand Down
7 changes: 7 additions & 0 deletions Tests/LinuxMain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,12 @@ extension DynamicInlineRuleTests {
]
}

extension EmptyCollectionLiteralRuleTests {
static var allTests: [(String, (EmptyCollectionLiteralRuleTests) -> () throws -> Void)] = [
("testWithDefaultConfiguration", testWithDefaultConfiguration)
]
}

extension EmptyCountRuleTests {
static var allTests: [(String, (EmptyCountRuleTests) -> () throws -> Void)] = [
("testWithDefaultConfiguration", testWithDefaultConfiguration)
Expand Down Expand Up @@ -1582,6 +1588,7 @@ XCTMain([
testCase(DuplicateEnumCasesRuleTests.allTests),
testCase(DuplicateImportsRuleTests.allTests),
testCase(DynamicInlineRuleTests.allTests),
testCase(EmptyCollectionLiteralRuleTests.allTests),
testCase(EmptyCountRuleTests.allTests),
testCase(EmptyEnumArgumentsRuleTests.allTests),
testCase(EmptyParametersRuleTests.allTests),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,12 @@ class DynamicInlineRuleTests: XCTestCase {
}
}

class EmptyCollectionLiteralRuleTests: XCTestCase {
func testWithDefaultConfiguration() {
verifyRule(EmptyCollectionLiteralRule.description)
}
}

class EmptyCountRuleTests: XCTestCase {
func testWithDefaultConfiguration() {
verifyRule(EmptyCountRule.description)
Expand Down

0 comments on commit 130371b

Please sign in to comment.