From e365fd342c198374be4bfecfa6599ff030bdbb32 Mon Sep 17 00:00:00 2001 From: Ornithologist Coder Date: Thu, 3 Aug 2017 11:36:07 +0200 Subject: [PATCH] Add joined_default_parameter opt-in rule to discourage explicit usage of the default separator. Implements #1093. --- CHANGELOG.md | 5 ++ Rules.md | 38 ++++++++++++ .../Models/MasterRuleList.swift | 1 + .../Rules/JoinedDefaultRule.swift | 58 +++++++++++++++++++ SwiftLint.xcodeproj/project.pbxproj | 4 ++ Tests/LinuxMain.swift | 1 + .../SwiftLintFrameworkTests/RulesTests.swift | 4 ++ 7 files changed, 111 insertions(+) create mode 100644 Source/SwiftLintFramework/Rules/JoinedDefaultRule.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index c55768229dc..5de5fc63410 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,11 @@ [Ornithologist Coder](https://github.com/ornithocoder) [#1370](https://github.com/realm/SwiftLint/issues/1370) +* Add `joined_default_parameter` opt-in rule to discourage + explicit use of default separator. + [Ornithologist Coder](https://github.com/ornithocoder) + [#1093](https://github.com/realm/SwiftLint/issues/1093) + ##### Bug Fixes * Fix false positive on `force_unwrapping` rule when declaring diff --git a/Rules.md b/Rules.md index d7f6ee118a6..3676d7b4865 100644 --- a/Rules.md +++ b/Rules.md @@ -41,6 +41,7 @@ * [Implicit Getter](#implicit-getter) * [Implicit Return](#implicit-return) * [Implicitly Unwrapped Optional](#implicitly-unwrapped-optional) +* [Joined Default Parameter](#joined-default-parameter) * [Large Tuple](#large-tuple) * [Leading Whitespace](#leading-whitespace) * [Legacy CGGeometry Functions](#legacy-cggeometry-functions) @@ -4819,6 +4820,43 @@ func foo(int: Int!) {} +## Joined Default Parameter + +Identifier | Enabled by default | Supports autocorrection | Kind +--- | --- | --- | --- +`joined_default_parameter` | Disabled | No | idiomatic + +Discouraged explicit use of default separator. + +### Examples + +
+Non Triggering Examples + +```swift +let foo = bar.joined() +``` + +```swift +let foo = bar.joined(separator: ",") +``` + +```swift +let foo = bar.joined(separator: toto) +``` + +
+
+Triggering Examples + +```swift +let foo = bar.joined(separator: "") +``` + +
+ + + ## Large Tuple Identifier | Enabled by default | Supports autocorrection | Kind diff --git a/Source/SwiftLintFramework/Models/MasterRuleList.swift b/Source/SwiftLintFramework/Models/MasterRuleList.swift index 3b20b6f1fb6..b1ce8c90ce6 100644 --- a/Source/SwiftLintFramework/Models/MasterRuleList.swift +++ b/Source/SwiftLintFramework/Models/MasterRuleList.swift @@ -49,6 +49,7 @@ public let masterRuleList = RuleList(rules: [ ImplicitGetterRule.self, ImplicitReturnRule.self, ImplicitlyUnwrappedOptionalRule.self, + JoinedDefaultParameterRule.self, LargeTupleRule.self, LeadingWhitespaceRule.self, LegacyCGGeometryFunctionsRule.self, diff --git a/Source/SwiftLintFramework/Rules/JoinedDefaultRule.swift b/Source/SwiftLintFramework/Rules/JoinedDefaultRule.swift new file mode 100644 index 00000000000..7d78f868703 --- /dev/null +++ b/Source/SwiftLintFramework/Rules/JoinedDefaultRule.swift @@ -0,0 +1,58 @@ +// +// JoinedDefaultRule.swift +// SwiftLint +// +// Created by Ornithologist Coder on 8/3/17. +// Copyright © 2017 Realm. All rights reserved. +// + +import Foundation +import SourceKittenFramework + +public struct JoinedDefaultParameterRule: ASTRule, ConfigurationProviderRule, OptInRule { + public var configuration = SeverityConfiguration(.warning) + + public init() {} + + public static let description = RuleDescription( + identifier: "joined_default_parameter", + name: "Joined Default Parameter", + description: "Discouraged explicit usage of the default separator.", + kind: .idiomatic, + nonTriggeringExamples: [ + "let foo = bar.joined()", + "let foo = bar.joined(separator: \",\")", + "let foo = bar.joined(separator: toto)" + ], + triggeringExamples: [ + "let foo = bar.joined(separator: \"\")" + ] + ) + + public func validate(file: File, + kind: SwiftExpressionKind, + dictionary: [String: SourceKitRepresentable]) -> [StyleViolation] { + guard + kind == .call, + let offset = dictionary.offset, + let name = dictionary.name, + name.hasSuffix(".joined"), + passesDefaultSeparator(dictionary: dictionary, file: file) + else { + return [] + } + + return [StyleViolation(ruleDescription: type(of: self).description, + severity: configuration.severity, + location: Location(file: file, byteOffset: offset))] + } + + private func passesDefaultSeparator(dictionary: [String: SourceKitRepresentable], file: File) -> Bool { + guard + let bodyOffset = dictionary.bodyOffset, + let bodyLength = dictionary.bodyLength else { return false } + + let body = file.contents.bridge().substringWithByteRange(start: bodyOffset, length: bodyLength) + return body == "separator: \"\"" + } +} diff --git a/SwiftLint.xcodeproj/project.pbxproj b/SwiftLint.xcodeproj/project.pbxproj index e3bd2817625..b9e25b3a1e4 100644 --- a/SwiftLint.xcodeproj/project.pbxproj +++ b/SwiftLint.xcodeproj/project.pbxproj @@ -70,6 +70,7 @@ 62622F6B1F2F2E3500D5D099 /* DiscouragedDirectInitRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62622F6A1F2F2E3500D5D099 /* DiscouragedDirectInitRule.swift */; }; 626D02971F31CBCC0054788D /* XCTFailMessageRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 626D02961F31CBCC0054788D /* XCTFailMessageRule.swift */; }; 62A498561F306A7700D766E4 /* DiscouragedDirectInitConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62A498551F306A7700D766E4 /* DiscouragedDirectInitConfiguration.swift */; }; + 62A6E7931F3317E3003A0479 /* JoinedDefaultRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62A6E7911F3317E3003A0479 /* JoinedDefaultRule.swift */; }; 67932E2D1E54AF4B00CB0629 /* CyclomaticComplexityConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67932E2C1E54AF4B00CB0629 /* CyclomaticComplexityConfigurationTests.swift */; }; 67EB4DFA1E4CC111004E9ACD /* CyclomaticComplexityConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB4DF81E4CC101004E9ACD /* CyclomaticComplexityConfiguration.swift */; }; 67EB4DFC1E4CD7F5004E9ACD /* CyclomaticComplexityRuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB4DFB1E4CD7F5004E9ACD /* CyclomaticComplexityRuleTests.swift */; }; @@ -377,6 +378,7 @@ 62622F6A1F2F2E3500D5D099 /* DiscouragedDirectInitRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiscouragedDirectInitRule.swift; sourceTree = ""; }; 626D02961F31CBCC0054788D /* XCTFailMessageRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCTFailMessageRule.swift; sourceTree = ""; }; 62A498551F306A7700D766E4 /* DiscouragedDirectInitConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiscouragedDirectInitConfiguration.swift; sourceTree = ""; }; + 62A6E7911F3317E3003A0479 /* JoinedDefaultRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinedDefaultRule.swift; sourceTree = ""; }; 62AF35D71F30B183009B11EE /* DiscouragedDirectInitRuleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiscouragedDirectInitRuleTests.swift; sourceTree = ""; }; 65454F451B14D73800319A6C /* ControlStatementRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlStatementRule.swift; sourceTree = ""; }; 67932E2C1E54AF4B00CB0629 /* CyclomaticComplexityConfigurationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CyclomaticComplexityConfigurationTests.swift; sourceTree = ""; }; @@ -964,6 +966,7 @@ D43DB1071DC573DA00281215 /* ImplicitGetterRule.swift */, 47FF3BDF1E7C745100187E6D /* ImplicitlyUnwrappedOptionalRule.swift */, D4470D561EB69225008A1B2E /* ImplicitReturnRule.swift */, + 62A6E7911F3317E3003A0479 /* JoinedDefaultRule.swift */, D4DA1DF91E18D6200037413D /* LargeTupleRule.swift */, E88DEA7D1B098F2A00A66CB0 /* LeadingWhitespaceRule.swift */, 4DB7815C1CAD690100BC4723 /* LegacyCGGeometryFunctionsRule.swift */, @@ -1440,6 +1443,7 @@ C328A2F71E6759AE00A9E4D7 /* ExplicitTypeInterfaceRule.swift in Sources */, 93E0C3CE1D67BD7F007FA25D /* ConditionalReturnsOnNewlineRule.swift in Sources */, D43DB1081DC573DA00281215 /* ImplicitGetterRule.swift in Sources */, + 62A6E7931F3317E3003A0479 /* JoinedDefaultRule.swift in Sources */, D4FD4C851F2A260A00DD8AA8 /* BlockBasedKVORule.swift in Sources */, 7C0C2E7A1D2866CB0076435A /* ExplicitInitRule.swift in Sources */, E88DEA771B098D0C00A66CB0 /* Rule.swift in Sources */, diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift index a72c043f30f..40c9e004fec 100644 --- a/Tests/LinuxMain.swift +++ b/Tests/LinuxMain.swift @@ -367,6 +367,7 @@ extension RulesTests { ("testImplicitGetter", testImplicitGetter), ("testImplicitlyUnwrappedOptional", testImplicitlyUnwrappedOptional), ("testImplicitReturn", testImplicitReturn), + ("testJoinedDefaultParameter", testJoinedDefaultParameter), ("testLargeTuple", testLargeTuple), ("testLeadingWhitespace", testLeadingWhitespace), ("testLegacyCGGeometryFunctions", testLegacyCGGeometryFunctions), diff --git a/Tests/SwiftLintFrameworkTests/RulesTests.swift b/Tests/SwiftLintFrameworkTests/RulesTests.swift index 24330993762..3c0713b3d46 100644 --- a/Tests/SwiftLintFrameworkTests/RulesTests.swift +++ b/Tests/SwiftLintFrameworkTests/RulesTests.swift @@ -142,6 +142,10 @@ class RulesTests: XCTestCase { verifyRule(ImplicitReturnRule.description) } + func testJoinedDefaultParameter() { + verifyRule(JoinedDefaultParameterRule.description) + } + func testLargeTuple() { verifyRule(LargeTupleRule.description) }