From 5d6c2ec3e797cc159161e7d4b9ca2feaa22e683f Mon Sep 17 00:00:00 2001 From: Marcelo Fabri Date: Fri, 6 Jan 2017 02:23:19 -0200 Subject: [PATCH] Add shorthand_operator rule Fixes #902 --- CHANGELOG.md | 5 + .../Models/MasterRuleList.swift | 1 + .../Rules/ShorthandOperatorRule.swift | 102 ++++++++++++++++++ SwiftLint.xcodeproj/project.pbxproj | 4 + .../SwiftLintFrameworkTests/RulesTests.swift | 5 + 5 files changed, 117 insertions(+) create mode 100644 Source/SwiftLintFramework/Rules/ShorthandOperatorRule.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 6782dfb97..e7c00fee6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -103,6 +103,11 @@ [Javier Hernández](https://github.com/jaherhi) [#384](https://github.com/realm/SwiftLint/issues/384) +* Add `shorthand_operator` rule that validates that shorthand operators should + be used when possible. + [Marcelo Fabri](https://github.com/marcelofabri) + [#902](https://github.com/realm/SwiftLint/issues/902) + ##### Bug Fixes * Ignore close parentheses on `vertical_parameter_alignment` rule. diff --git a/Source/SwiftLintFramework/Models/MasterRuleList.swift b/Source/SwiftLintFramework/Models/MasterRuleList.swift index 90558cca5..f39d2cbc7 100644 --- a/Source/SwiftLintFramework/Models/MasterRuleList.swift +++ b/Source/SwiftLintFramework/Models/MasterRuleList.swift @@ -122,6 +122,7 @@ public let masterRuleList = RuleList(rules: RedundantStringEnumValueRule.self, RedundantVoidReturnRule.self, ReturnArrowWhitespaceRule.self, + ShorthandOperatorRule.self, SortedImportsRule.self, StatementPositionRule.self, SwitchCaseOnNewlineRule.self, diff --git a/Source/SwiftLintFramework/Rules/ShorthandOperatorRule.swift b/Source/SwiftLintFramework/Rules/ShorthandOperatorRule.swift new file mode 100644 index 000000000..af9d1d4b9 --- /dev/null +++ b/Source/SwiftLintFramework/Rules/ShorthandOperatorRule.swift @@ -0,0 +1,102 @@ +// +// ShorthandOperatorRule.swift +// SwiftLint +// +// Created by Marcelo Fabri on 01/06/17. +// Copyright © 2017 Realm. All rights reserved. +// + +import SourceKittenFramework + +public struct ShorthandOperatorRule: ConfigurationProviderRule { + + public var configuration = SeverityConfiguration(.error) + + public init() {} + + public static let description = RuleDescription( + identifier: "shorthand_operator", + name: "Shorthand Operator", + description: "Prefer shorhand operators (+=, -=, *=, /=) over doing the operation and assigning.", + nonTriggeringExamples: allOperators.flatMap { operation in + [ + "foo \(operation)= 1", + "foo \(operation)= variable", + "foo \(operation)= bar.method()" + ] + }, + triggeringExamples: allOperators.flatMap { operation in + [ + "↓foo = foo \(operation) 1\n", + "↓foo = foo \(operation) aVariable\n", + "↓foo = foo \(operation) bar.method()\n", + "↓foo = 1 \(operation) foo\n", + "↓foo = aVariable \(operation) foo\n", + "↓foo = bar.method() \(operation) foo\n", + "↓foo = bar.method(param: 1, otherParam: 2) \(operation) foo\n" + ] + } + ) + + private static let allOperators = ["+", "-", "/", "*"] + + private static let pattern: String = { + let escapedOperators = allOperators.map { "\\\($0)" }.joined() + let operators = "[\(escapedOperators)]" + let operand = "[\\w\\d\\.]+?" + let spaces = "[^\\S\\r\\n]*?" + let otherOperand = "\(spaces).+?\(spaces)" + + let pattern1 = "\\b(\(operand))\(spaces)=\(spaces)(\\1)\(spaces)\(operators)" + let pattern2 = "\\b(\(operand))\(spaces)=\(otherOperand)\(operators)\(spaces)(\\3)" + + return "\(pattern1)|\(pattern2)" + }() + + // swiftlint:disable:next force_try + private static let regex = try! NSRegularExpression(pattern: pattern, options: []) + + public func validateFile(_ file: File) -> [StyleViolation] { + let contents = file.contents.bridge() + let range = NSRange(location: 0, length: contents.length) + + let matches = ShorthandOperatorRule.regex.matches(in: file.contents, options: [], range: range) + return matches.flatMap { match -> StyleViolation? in + + // byteRanges will have the ranges of captured groups + let byteRanges: [NSRange?] = (1..