Merge pull request #3191 from MortyMerr/feature/preferZeroRule

Add prefer_zero_over_explicit_init rule
This commit is contained in:
Marcelo Fabri
2020-08-05 15:49:22 -07:00
committed by GitHub
6 changed files with 95 additions and 0 deletions
+6
View File
@@ -33,6 +33,12 @@
[Zsolt Kovács](https://github.com/lordzsolt)
[#3103](https://github.com/realm/SwiftLint/issues/3103)
* Add `prefer_zero_over_explicit_init` opt-in rule to enforce using
`.zero` instead of calling constructor with zero arguments
(e.g. `CGPoint(x: 0, y: 0)`) when using CoreGraphics types.
[Anton Nazarov](https://github.com/MortyMerr)
[#3190](https://github.com/realm/SwiftLint/issues/3190)
#### Bug Fixes
* Fix UnusedImportRule breaking transitive imports.
@@ -124,6 +124,7 @@ public let masterRuleList = RuleList(rules: [
OverrideInExtensionRule.self,
PatternMatchingKeywordsRule.self,
PreferSelfTypeOverTypeOfSelfRule.self,
PreferZeroOverExplicitInitRule.self,
PrefixedTopLevelConstantRule.self,
PrivateActionRule.self,
PrivateOutletRule.self,
@@ -0,0 +1,71 @@
import Foundation
import SourceKittenFramework
public struct PreferZeroOverExplicitInitRule: OptInRule, ConfigurationProviderRule, SubstitutionCorrectableRule,
AutomaticTestableRule {
public var configuration = SeverityConfiguration(.warning)
private var pattern: String {
let zero = "\\s*:\\s*0(.0*)?\\s*"
let type = "(\(["CGPoint", "CGSize", "CGVector", "CGRect"].joined(separator: "|")))"
let firstArg = "(\(["x", "dx", "width"].joined(separator: "|")))"
let secondArg = "(\(["y", "dy", "height"].joined(separator: "|")))"
let thirdAndFourthArg = "(\\,\\s*width\(zero)\\,\\s*height\(zero))?"
return "\(type)\\(\\s*\(firstArg)\(zero)\\,\\s*\(secondArg)\(zero)\(thirdAndFourthArg)\\)"
}
public static let description = RuleDescription(
identifier: "prefer_zero_over_explicit_init",
name: "Prefer Zero Over Explicit Init",
description: "Prefer `.zero` over explicit init with zero parameters (e.g. `CGPoint(x: 0, y: 0)`)",
kind: .idiomatic,
nonTriggeringExamples: [
Example("CGRect(x: 0, y: 0, width: 0, height: 1)"),
Example("CGPoint(x: 0, y: -1"),
Example("CGSize(width: 2, height: 4)"),
Example("CGVector(dx: -5, dy: 0)")
],
triggeringExamples: [
Example("↓CGPoint(x: 0, y: 0)"),
Example("↓CGPoint(x: 0.000000, y: 0)"),
Example("↓CGPoint(x: 0.000000, y: 0.000)"),
Example("↓CGRect(x: 0, y: 0, width: 0, height: 0)"),
Example("↓CGSize(width: 0, height: 0)"),
Example("↓CGVector(dx: 0, dy: 0)")
],
corrections: [
Example("↓CGPoint(x: 0, y: 0)"): Example("CGPoint.zero"),
Example("↓CGRect(x: 0, y: 0, width: 0, height: 0)"): Example("CGRect.zero"),
Example("↓CGSize(width: 0, height: 0.000)"): Example("CGSize.zero"),
Example("↓CGVector(dx: 0, dy: 0)"): Example("CGVector.zero")
]
)
public init() {}
public func validate(file: SwiftLintFile) -> [StyleViolation] {
violationRanges(in: file).map {
StyleViolation(
ruleDescription: Self.description,
severity: configuration.severity,
location: Location(file: file, characterOffset: $0.location)
)
}
}
public func violationRanges(in file: SwiftLintFile) -> [NSRange] {
return file.matchesAndSyntaxKinds(matching: pattern)
.filter {
$0.1 == [.identifier, .identifier, .number, .identifier, .number] ||
$0.1 == [
.identifier, .identifier, .number, .identifier, .number, .identifier, .number, .identifier, .number
]
}
.map { $0.0.range(at: 0) }
}
public func substitution(for violationRange: NSRange, in file: SwiftLintFile) -> (NSRange, String)? {
let declaration = file.stringView.substring(with: violationRange)
guard let typeEndIndex = declaration.firstIndex(of: "(") else { return nil }
return (violationRange, "\(declaration.prefix(upTo: typeEndIndex)).zero")
}
}
+4
View File
@@ -164,6 +164,7 @@
77DFF0E923442DE30041EEB4 /* RawValueForCamelCasedCodableEnumRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7723A4DE23442D7100F38590 /* RawValueForCamelCasedCodableEnumRule.swift */; };
787CDE39208E7D41005F3D2F /* SwitchCaseAlignmentConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 787CDE38208E7D41005F3D2F /* SwitchCaseAlignmentConfiguration.swift */; };
787CDE3B208F9C34005F3D2F /* SwitchCaseAlignmentRuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 787CDE3A208F9C34005F3D2F /* SwitchCaseAlignmentRuleTests.swift */; };
78B9051D24564AF600C81B0A /* PreferZeroOverExplicitInitRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78B9051A245646CD00C81B0A /* PreferZeroOverExplicitInitRule.swift */; };
78F032461D7C877E00BE709A /* OverriddenSuperCallRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78F032441D7C877800BE709A /* OverriddenSuperCallRule.swift */; };
78F032481D7D614300BE709A /* OverridenSuperCallConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78F032471D7D614300BE709A /* OverridenSuperCallConfiguration.swift */; };
7C0C2E7A1D2866CB0076435A /* ExplicitInitRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C0C2E791D2866CB0076435A /* ExplicitInitRule.swift */; };
@@ -683,6 +684,7 @@
7723A4DE23442D7100F38590 /* RawValueForCamelCasedCodableEnumRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RawValueForCamelCasedCodableEnumRule.swift; sourceTree = "<group>"; };
787CDE38208E7D41005F3D2F /* SwitchCaseAlignmentConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitchCaseAlignmentConfiguration.swift; sourceTree = "<group>"; };
787CDE3A208F9C34005F3D2F /* SwitchCaseAlignmentRuleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitchCaseAlignmentRuleTests.swift; sourceTree = "<group>"; };
78B9051A245646CD00C81B0A /* PreferZeroOverExplicitInitRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferZeroOverExplicitInitRule.swift; sourceTree = "<group>"; };
78F032441D7C877800BE709A /* OverriddenSuperCallRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OverriddenSuperCallRule.swift; sourceTree = "<group>"; };
78F032471D7D614300BE709A /* OverridenSuperCallConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OverridenSuperCallConfiguration.swift; sourceTree = "<group>"; };
7C0C2E791D2866CB0076435A /* ExplicitInitRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExplicitInitRule.swift; sourceTree = "<group>"; };
@@ -1381,6 +1383,7 @@
827169B41F48D712003FB9AF /* NoGroupingExtensionRule.swift */,
D4B022971E102EE8007E5297 /* ObjectLiteralRule.swift */,
D403A4A21F4DB5510020CA02 /* PatternMatchingKeywordsRule.swift */,
78B9051A245646CD00C81B0A /* PreferZeroOverExplicitInitRule.swift */,
1E3C2D701EE36C6F00C8386D /* PrivateOverFilePrivateRule.swift */,
24B4DF0B1D6DFA370097803B /* RedundantNilCoalescingRule.swift */,
18B90B6A21ADD99800B60749 /* RedundantObjcAttributeRule.swift */,
@@ -2356,6 +2359,7 @@
6C15818D237026AC00F582A2 /* GitHubActionsLoggingReporter.swift in Sources */,
62FE5D32200CABDD00F68793 /* DiscouragedOptionalCollectionExamples.swift in Sources */,
D41C09BD23C1B99D00F105C4 /* OrphanedDocCommentRule.swift in Sources */,
78B9051D24564AF600C81B0A /* PreferZeroOverExplicitInitRule.swift in Sources */,
D49896F12026B36C00814A83 /* RedundantSetAccessControlRule.swift in Sources */,
E4A6CF752363CBFB00DD5B18 /* RandomAccessCollection+Swiftlint.swift in Sources */,
29FFC37A1F15764D007E4825 /* FileLengthRuleConfiguration.swift in Sources */,
+7
View File
@@ -1094,6 +1094,12 @@ extension PreferSelfTypeOverTypeOfSelfRuleTests {
]
}
extension PreferZeroOverExplicitInitRuleTests {
static var allTests: [(String, (PreferZeroOverExplicitInitRuleTests) -> () throws -> Void)] = [
("testWithDefaultConfiguration", testWithDefaultConfiguration)
]
}
extension PrefixedTopLevelConstantRuleTests {
static var allTests: [(String, (PrefixedTopLevelConstantRuleTests) -> () throws -> Void)] = [
("testDefaultConfiguration", testDefaultConfiguration),
@@ -1826,6 +1832,7 @@ XCTMain([
testCase(OverrideInExtensionRuleTests.allTests),
testCase(PatternMatchingKeywordsRuleTests.allTests),
testCase(PreferSelfTypeOverTypeOfSelfRuleTests.allTests),
testCase(PreferZeroOverExplicitInitRuleTests.allTests),
testCase(PrefixedTopLevelConstantRuleTests.allTests),
testCase(PrivateActionRuleTests.allTests),
testCase(PrivateOutletRuleTests.allTests),
@@ -522,6 +522,12 @@ class PreferSelfTypeOverTypeOfSelfRuleTests: XCTestCase {
}
}
class PreferZeroOverExplicitInitRuleTests: XCTestCase {
func testWithDefaultConfiguration() {
verifyRule(PreferZeroOverExplicitInitRule.description)
}
}
class PrivateActionRuleTests: XCTestCase {
func testWithDefaultConfiguration() {
verifyRule(PrivateActionRule.description)