mirror of
https://github.com/realm/SwiftLint.git
synced 2026-06-06 20:18:40 +00:00
Merge pull request #3191 from MortyMerr/feature/preferZeroRule
Add prefer_zero_over_explicit_init rule
This commit is contained in:
@@ -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")
|
||||
}
|
||||
}
|
||||
@@ -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 */,
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user