diff --git a/CHANGELOG.md b/CHANGELOG.md index 308991c22f..854563d393 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,10 @@ [JP Simard](https://github.com/jpsim) [#256](https://github.com/realm/SwiftLint/issues/256) +* Add LegacyConstantRule. + [Aaron McTavish](https://github.com/aamctustwo) + [#319](https://github.com/realm/SwiftLint/issues/319) + * Add opt-in rule to encourage checking `isEmpty` over comparing `count` to zero. [JP Simard](https://github.com/jpsim) diff --git a/Source/SwiftLintFramework/Models/MasterRuleList.swift b/Source/SwiftLintFramework/Models/MasterRuleList.swift index 75872c3aca..65bcbf95ef 100644 --- a/Source/SwiftLintFramework/Models/MasterRuleList.swift +++ b/Source/SwiftLintFramework/Models/MasterRuleList.swift @@ -30,6 +30,7 @@ public let masterRuleList = RuleList( rules: ClosingBraceRule.self, ForceTryRule.self, FunctionBodyLengthRule.self, LeadingWhitespaceRule.self, + LegacyConstantRule.self, LegacyConstructorRule.self, LineLengthRule.self, MissingDocsRule.self, diff --git a/Source/SwiftLintFramework/Rules/LegacyConstantRule.swift b/Source/SwiftLintFramework/Rules/LegacyConstantRule.swift new file mode 100644 index 0000000000..5c9e19f264 --- /dev/null +++ b/Source/SwiftLintFramework/Rules/LegacyConstantRule.swift @@ -0,0 +1,81 @@ +// +// LegacyConstantRule.swift +// SwiftLint +// +// Created by Aaron McTavish on 12/01/2016. +// Copyright © 2016 Realm. All rights reserved. +// + +import SourceKittenFramework + +public struct LegacyConstantRule: CorrectableRule { + public init() { } + + public static let description = RuleDescription( + identifier: "legacy_constant", + name: "Legacy Constant", + description: "Struct-scoped constants are preferred over legacy global constants.", + nonTriggeringExamples: [ + "CGRect.infinite", + "CGPoint.zero", + "CGRect.zero", + "CGSize.zero", + "CGRect.null" + ], + triggeringExamples: [ + "↓CGRectInfinite", + "↓CGPointZero", + "↓CGRectZero", + "↓CGSizeZero", + "↓CGRectNull" + ], + corrections: [ + "CGRectInfinite\n": "CGRect.infinite\n", + "CGPointZero\n": "CGPoint.zero\n", + "CGRectZero\n": "CGRect.zero\n", + "CGSizeZero\n": "CGSize.zero\n", + "CGRectNull\n": "CGRect.null\n" + ] + ) + + public func validateFile(file: File) -> [StyleViolation] { + let constants = ["CGRectInfinite", "CGPointZero", "CGRectZero", "CGSizeZero", + "CGRectNull"] + + let pattern = "\\b(" + constants.joinWithSeparator("|") + ")\\b" + + return file.matchPattern(pattern, withSyntaxKinds: [.Identifier]).map { + StyleViolation(ruleDescription: self.dynamicType.description, + location: Location(file: file, characterOffset: $0.location)) + } + } + + public func correctFile(file: File) -> [Correction] { + let patterns = [ + "CGRectInfinite": "CGRect.infinite", + "CGPointZero": "CGPoint.zero", + "CGRectZero": "CGRect.zero", + "CGSizeZero": "CGSize.zero", + "CGRectNull": "CGRect.null" + ] + + let description = self.dynamicType.description + var corrections = [Correction]() + var contents = file.contents + + for (pattern, template) in patterns { + let matches = file.matchPattern(pattern, withSyntaxKinds: [.Identifier]) + + let regularExpression = regex(pattern) + for range in matches.reverse() { + contents = regularExpression.stringByReplacingMatchesInString(contents, + options: [], range: range, withTemplate: template) + let location = Location(file: file, characterOffset: range.location) + corrections.append(Correction(ruleDescription: description, location: location)) + } + } + + file.write(contents) + return corrections + } +} diff --git a/Source/SwiftLintFrameworkTests/StringRuleTests.swift b/Source/SwiftLintFrameworkTests/StringRuleTests.swift index e59421b6a1..be6dfdfc62 100644 --- a/Source/SwiftLintFrameworkTests/StringRuleTests.swift +++ b/Source/SwiftLintFrameworkTests/StringRuleTests.swift @@ -121,4 +121,8 @@ class StringRuleTests: XCTestCase { func testEmptyCount() { verifyRule(EmptyCountRule.description) } + + func testLegacyConstant() { + verifyRule(LegacyConstantRule.description) + } } diff --git a/SwiftLint.xcodeproj/project.pbxproj b/SwiftLint.xcodeproj/project.pbxproj index 3db2a15e57..c7f7c126a5 100644 --- a/SwiftLint.xcodeproj/project.pbxproj +++ b/SwiftLint.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 006ECFC41C44E99E00EF6364 /* LegacyConstantRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 006ECFC31C44E99E00EF6364 /* LegacyConstantRule.swift */; }; 00970C751C3FC3A0007C4A83 /* ConditionalBindingCascadeRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00970C741C3FC3A0007C4A83 /* ConditionalBindingCascadeRule.swift */; }; 02FD8AEF1BFC18D60014BFFB /* ExtendedNSStringTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02FD8AEE1BFC18D60014BFFB /* ExtendedNSStringTests.swift */; }; 1F11B3CF1C252F23002E8FA8 /* ClosingBraceRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F11B3CE1C252F23002E8FA8 /* ClosingBraceRule.swift */; }; @@ -152,6 +153,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 006ECFC31C44E99E00EF6364 /* LegacyConstantRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyConstantRule.swift; sourceTree = ""; }; 00970C741C3FC3A0007C4A83 /* ConditionalBindingCascadeRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConditionalBindingCascadeRule.swift; sourceTree = ""; }; 02FD8AEE1BFC18D60014BFFB /* ExtendedNSStringTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExtendedNSStringTests.swift; sourceTree = ""; }; 1F11B3CE1C252F23002E8FA8 /* ClosingBraceRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClosingBraceRule.swift; sourceTree = ""; }; @@ -511,6 +513,7 @@ E816194D1BFBFEAB00946723 /* ForceTryRule.swift */, E88DEA8F1B099A3100A66CB0 /* FunctionBodyLengthRule.swift */, E88DEA7D1B098F2A00A66CB0 /* LeadingWhitespaceRule.swift */, + 006ECFC31C44E99E00EF6364 /* LegacyConstantRule.swift */, D44AD2741C0AA3730048F7B0 /* LegacyConstructorRule.swift */, E88DEA7B1B098D7D00A66CB0 /* LineLengthRule.swift */, E849FF271BF9481A009AE999 /* MissingDocsRule.swift */, @@ -789,6 +792,7 @@ E88198441BEA93D200333A11 /* ColonRule.swift in Sources */, E809EDA11B8A71DF00399043 /* Configuration.swift in Sources */, E8EA41171C2D1DBE004F9930 /* CheckstyleReporter.swift in Sources */, + 006ECFC41C44E99E00EF6364 /* LegacyConstantRule.swift in Sources */, E88DEA731B0984C400A66CB0 /* String+SwiftLint.swift in Sources */, E88198591BEA95F100333A11 /* LeadingWhitespaceRule.swift in Sources */, 24E17F721B14BB3F008195BE /* File+Cache.swift in Sources */,