Skip to content

Commit

Permalink
Add legacy_multiple opt-in rule (#2771)
Browse files Browse the repository at this point in the history
Fixes #2612.
  • Loading branch information
marcelofabri authored Jun 2, 2019
1 parent 90cb134 commit 6be5bf7
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 0 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
[Marcelo Fabri](https://github.com/marcelofabri)
[#2676](https://github.com/realm/SwiftLint/issues/2676)

* Add `legacy_multiple` opt-in rule to warn against using the remainder operator
(`%`) checking for a remainder of zero when using Swift 5.
[Marcelo Fabri](https://github.com/marcelofabri)
[#2612](https://github.com/realm/SwiftLint/issues/2612)

#### Bug Fixes

* Don't trigger `redundant_void_return` violations when using `subscript` as the
Expand Down
73 changes: 73 additions & 0 deletions Rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
* [Legacy Constant](#legacy-constant)
* [Legacy Constructor](#legacy-constructor)
* [Legacy Hashing](#legacy-hashing)
* [Legacy Multiple](#legacy-multiple)
* [Legacy NSGeometry Functions](#legacy-nsgeometry-functions)
* [Legacy Random](#legacy-random)
* [Variable Declaration Whitespace](#variable-declaration-whitespace)
Expand Down Expand Up @@ -10828,6 +10829,78 @@ class Foo: Hashable {



## Legacy Multiple

Identifier | Enabled by default | Supports autocorrection | Kind | Analyzer | Minimum Swift Compiler Version
--- | --- | --- | --- | --- | ---
`legacy_multiple` | Disabled | No | idiomatic | No | 5.0.0

Prefer using the `isMultiple(of:)` function instead of using the remainder operator (`%`).

### Examples

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

```swift
cell.contentView.backgroundColor = indexPath.row.isMultiple(of: 2) ? .gray : .white
```

```swift
guard count.isMultiple(of: 2) else { throw DecodingError.dataCorrupted(...) }
```

```swift
sanityCheck(bytes > 0 && bytes.isMultiple(of: 4), "capacity must be multiple of 4 bytes")
```

```swift
guard let i = reversedNumbers.firstIndex(where: { $0.isMultiple(of: 2) }) else { return }
```

```swift
let constant = 56
let isMultiple = value.isMultiple(of: constant)
```

```swift
let constant = 56
let secret = value % constant == 5
```

```swift
let secretValue = (value % 3) + 2
```

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

```swift
cell.contentView.backgroundColor = indexPath.row ↓% 2 == 0 ? .gray : .white
```

```swift
guard count ↓% 2 == 0 else { throw DecodingError.dataCorrupted(...) }
```

```swift
sanityCheck(bytes > 0 && bytes ↓% 4 == 0, "capacity must be multiple of 4 bytes")
```

```swift
guard let i = reversedNumbers.firstIndex(where: { $0 ↓% 2 == 0 }) else { return }
```

```swift
let constant = 56
let isMultiple = value ↓% constant == 0
```

</details>



## Legacy NSGeometry Functions

Identifier | Enabled by default | Supports autocorrection | Kind | Analyzer | Minimum Swift Compiler Version
Expand Down
1 change: 1 addition & 0 deletions Source/SwiftLintFramework/Models/MasterRuleList.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public let masterRuleList = RuleList(rules: [
LegacyConstantRule.self,
LegacyConstructorRule.self,
LegacyHashingRule.self,
LegacyMultipleRule.self,
LegacyNSGeometryFunctionsRule.self,
LegacyRandomRule.self,
LetVarWhitespaceRule.self,
Expand Down
52 changes: 52 additions & 0 deletions Source/SwiftLintFramework/Rules/Idiomatic/LegacyMultipleRule.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import SourceKittenFramework

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

public init() {}

public static let description = RuleDescription(
identifier: "legacy_multiple",
name: "Legacy Multiple",
description: "Prefer using the `isMultiple(of:)` function instead of using the remainder operator (`%`).",
kind: .idiomatic,
minSwiftVersion: .five,
nonTriggeringExamples: [
"cell.contentView.backgroundColor = indexPath.row.isMultiple(of: 2) ? .gray : .white",
"guard count.isMultiple(of: 2) else { throw DecodingError.dataCorrupted(...) }",
"sanityCheck(bytes > 0 && bytes.isMultiple(of: 4), \"capacity must be multiple of 4 bytes\")",
"guard let i = reversedNumbers.firstIndex(where: { $0.isMultiple(of: 2) }) else { return }",
"""
let constant = 56
let isMultiple = value.isMultiple(of: constant)
""",
"""
let constant = 56
let secret = value % constant == 5
""",
"let secretValue = (value % 3) + 2"
],
triggeringExamples: [
"cell.contentView.backgroundColor = indexPath.row ↓% 2 == 0 ? .gray : .white",
"guard count ↓% 2 == 0 else { throw DecodingError.dataCorrupted(...) }",
"sanityCheck(bytes > 0 && bytes ↓% 4 == 0, \"capacity must be multiple of 4 bytes\")",
"guard let i = reversedNumbers.firstIndex(where: { $0 ↓% 2 == 0 }) else { return }",
"""
let constant = 56
let isMultiple = value ↓% constant == 0
"""
]
)

// MARK: - Rule

public func validate(file: File) -> [StyleViolation] {
let pattern = "(?!\\b\\s*)%\(RegexHelpers.variableOrNumber)==\\s*0\\b"
return file.match(pattern: pattern, excludingSyntaxKinds: SyntaxKind.commentAndStringKinds)
.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 @@ -341,6 +341,7 @@
D4EA77CA1F81FACC00C315FB /* LiteralExpressionEndIdentationRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4EA77C91F81FACC00C315FB /* LiteralExpressionEndIdentationRule.swift */; };
D4EAB3A420E9948E0051C09A /* AutomaticRuleTests.generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4EAB3A320E9948D0051C09A /* AutomaticRuleTests.generated.swift */; };
D4F10614229A2F5E00FDE319 /* NoFallthroughOnlyRuleExamples.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4F10613229A2F5E00FDE319 /* NoFallthroughOnlyRuleExamples.swift */; };
D4F10616229A331200FDE319 /* LegacyMultipleRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4F10615229A331200FDE319 /* LegacyMultipleRule.swift */; };
D4F5851520E99A8A0085C6D8 /* TrailingWhitespaceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4F5851320E99A720085C6D8 /* TrailingWhitespaceTests.swift */; };
D4F5851720E99B260085C6D8 /* StatementPositionRuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4F5851620E99B260085C6D8 /* StatementPositionRuleTests.swift */; };
D4F5851920E99B5A0085C6D8 /* PrivateOutletRuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4F5851820E99B5A0085C6D8 /* PrivateOutletRuleTests.swift */; };
Expand Down Expand Up @@ -829,6 +830,7 @@
D4EA77C91F81FACC00C315FB /* LiteralExpressionEndIdentationRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiteralExpressionEndIdentationRule.swift; sourceTree = "<group>"; };
D4EAB3A320E9948D0051C09A /* AutomaticRuleTests.generated.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutomaticRuleTests.generated.swift; sourceTree = "<group>"; };
D4F10613229A2F5E00FDE319 /* NoFallthroughOnlyRuleExamples.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoFallthroughOnlyRuleExamples.swift; sourceTree = "<group>"; };
D4F10615229A331200FDE319 /* LegacyMultipleRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyMultipleRule.swift; sourceTree = "<group>"; };
D4F5851320E99A720085C6D8 /* TrailingWhitespaceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrailingWhitespaceTests.swift; sourceTree = "<group>"; };
D4F5851620E99B260085C6D8 /* StatementPositionRuleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatementPositionRuleTests.swift; sourceTree = "<group>"; };
D4F5851820E99B5A0085C6D8 /* PrivateOutletRuleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivateOutletRuleTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1242,6 +1244,7 @@
00B8D9771E2D0FBD004E0EEC /* LegacyConstantRuleExamples.swift */,
D44AD2741C0AA3730048F7B0 /* LegacyConstructorRule.swift */,
C3EF547521B5A2190009262F /* LegacyHashingRule.swift */,
D4F10615229A331200FDE319 /* LegacyMultipleRule.swift */,
F22314AE1D4F7C77009AD165 /* LegacyNSGeometryFunctionsRule.swift */,
A3184D55215BCEFF00621EA2 /* LegacyRandomRule.swift */,
D4DAE8BB1DE14E8F00B0AE7A /* NimbleOperatorRule.swift */,
Expand Down Expand Up @@ -2064,6 +2067,7 @@
62640152201552FD005B9C4A /* DiscouragedOptionalBooleanRule.swift in Sources */,
85DA81321D6B471000951BC4 /* MarkRule.swift in Sources */,
D4A893351E15824100BF954D /* SwiftVersion.swift in Sources */,
D4F10616229A331200FDE319 /* LegacyMultipleRule.swift in Sources */,
D4470D5D1EB8004B008A1B2E /* VerticalParameterAlignmentOnCallRule.swift in Sources */,
D4DABFD31E29B4A5009617B6 /* DiscardedNotificationCenterObserverRule.swift in Sources */,
D4AB0EA21F8993DD00CEC380 /* NamespaceCollector.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 @@ -673,6 +673,12 @@ extension LegacyHashingRuleTests {
]
}

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

extension LegacyNSGeometryFunctionsRuleTests {
static var allTests: [(String, (LegacyNSGeometryFunctionsRuleTests) -> () throws -> Void)] = [
("testWithDefaultConfiguration", testWithDefaultConfiguration)
Expand Down Expand Up @@ -1603,6 +1609,7 @@ XCTMain([
testCase(LegacyConstantRuleTests.allTests),
testCase(LegacyConstructorRuleTests.allTests),
testCase(LegacyHashingRuleTests.allTests),
testCase(LegacyMultipleRuleTests.allTests),
testCase(LegacyNSGeometryFunctionsRuleTests.allTests),
testCase(LegacyRandomRuleTests.allTests),
testCase(LetVarWhitespaceRuleTests.allTests),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,12 @@ class LegacyHashingRuleTests: XCTestCase {
}
}

class LegacyMultipleRuleTests: XCTestCase {
func testWithDefaultConfiguration() {
verifyRule(LegacyMultipleRule.description)
}
}

class LegacyNSGeometryFunctionsRuleTests: XCTestCase {
func testWithDefaultConfiguration() {
verifyRule(LegacyNSGeometryFunctionsRule.description)
Expand Down

0 comments on commit 6be5bf7

Please sign in to comment.