From 383bd3de432c5d6248529c76890139388afcd7d9 Mon Sep 17 00:00:00 2001 From: Andrea Mazzini Date: Tue, 26 May 2015 22:04:19 +0200 Subject: [PATCH 1/5] Add ControlStatementRule --- Source/SwiftLintFramework/Linter.swift | 3 +- .../Rules/ControlStatementRule.swift | 62 +++++++++++++++++++ .../StyleViolationType.swift | 1 + .../SwiftLintFrameworkTests/LinterTests.swift | 2 +- SwiftLint.xcodeproj/project.pbxproj | 4 ++ 5 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 Source/SwiftLintFramework/Rules/ControlStatementRule.swift diff --git a/Source/SwiftLintFramework/Linter.swift b/Source/SwiftLintFramework/Linter.swift index af0a50007d..9641a3bdeb 100644 --- a/Source/SwiftLintFramework/Linter.swift +++ b/Source/SwiftLintFramework/Linter.swift @@ -26,7 +26,8 @@ public struct Linter { VariableNameRule(), TypeBodyLengthRule(), FunctionBodyLengthRule(), - NestingRule() + NestingRule(), + ControlStatementRule() ] public var styleViolations: [StyleViolation] { diff --git a/Source/SwiftLintFramework/Rules/ControlStatementRule.swift b/Source/SwiftLintFramework/Rules/ControlStatementRule.swift new file mode 100644 index 0000000000..fa05b58d14 --- /dev/null +++ b/Source/SwiftLintFramework/Rules/ControlStatementRule.swift @@ -0,0 +1,62 @@ +// +// ControlStatementRule.swift +// SwiftLint +// +// Created by Andrea Mazzini on 26/05/15. +// Copyright (c) 2015 Realm. All rights reserved. +// + +import SourceKittenFramework + +public struct ControlStatementRule: Rule { + public init() {} + + public let identifier = "control_statement" + + public func validateFile(file: File) -> [StyleViolation] { + let statement = file.matchPattern("\\s{0,}(if|for|while)\\s{0,}\\(", + withSyntaxKinds: [.Keyword]) + return statement.map { range in + return StyleViolation(type: .ControlStatement, + location: Location(file: file, offset: range.location), + severity: .Low, + reason: "if,for,while,do statements shouldn't wrap their conditionals in parentheses.") + } + } + + public let example = RuleExample( + ruleName: "Control Statement", + ruleDescription: "if,for,while,do statements shouldn't wrap their conditionals in parentheses.", + nonTriggeringExamples: [ + "if condition {\n", + "renderGif(data)\n", + "// if (hasBetterSyntax) {\n", + "// for (item in collection) {\n", + "// for (var index = 0; index < 42; index++) {\n", + "// for(item in collection) {\n", + "// for(var index = 0; index < 42; index++) {\n", + "for item in collection {\n", + "for var index = 0; index < 42; index++ {\n", + "// while (condition) {\n", + "while condition {\n", + "} while condition {\n", + "// } while (condition {\n", + "do { ; } while condition {\n" + ], + triggeringExamples: [ + "if (condition) {\n", + "if(condition) {\n", + "for (item in collection) {\n", + "for (var index = 0; index < 42; index++) {\n", + "for(item in collection) {\n", + "for(var index = 0; index < 42; index++) {\n", + "while (condition) {\n", + "while(condition) {\n", + "} while (condition) {\n", + "} while(condition) {\n", + "do { ; } while(condition) {\n", + "do { ; } while (condition) {\n" + ] + ) +} + diff --git a/Source/SwiftLintFramework/StyleViolationType.swift b/Source/SwiftLintFramework/StyleViolationType.swift index 400faaaa13..0e62e7013f 100644 --- a/Source/SwiftLintFramework/StyleViolationType.swift +++ b/Source/SwiftLintFramework/StyleViolationType.swift @@ -16,6 +16,7 @@ public enum StyleViolationType: String, Printable { case TODO = "TODO or FIXME" case Colon = "Colon" case Nesting = "Nesting" + case ControlStatement = "Control Statement" public var description: String { return rawValue } } diff --git a/Source/SwiftLintFrameworkTests/LinterTests.swift b/Source/SwiftLintFrameworkTests/LinterTests.swift index b367d07ff0..4a3696eec2 100644 --- a/Source/SwiftLintFrameworkTests/LinterTests.swift +++ b/Source/SwiftLintFrameworkTests/LinterTests.swift @@ -157,7 +157,7 @@ class LinterTests: XCTestCase { } func testControlStatements() { - // TODO: if,for,while,do statements shouldn't wrap their conditionals in parentheses. + verifyRule(ControlStatementRule().example, type: .ControlStatement, commentDoesntViolate: false) } func testNumberOfFunctionsInAType() { diff --git a/SwiftLint.xcodeproj/project.pbxproj b/SwiftLint.xcodeproj/project.pbxproj index bc05925039..7fdaab4c17 100644 --- a/SwiftLint.xcodeproj/project.pbxproj +++ b/SwiftLint.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 65454F471B14D76500319A6C /* ControlStatementRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65454F451B14D73800319A6C /* ControlStatementRule.swift */; }; 83894F221B0C928A006214E1 /* RulesCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83894F211B0C928A006214E1 /* RulesCommand.swift */; }; 83D71E281B131ECE000395DE /* RuleExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83D71E261B131EB5000395DE /* RuleExample.swift */; }; 83D71E2B1B131EED000395DE /* AnsiCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83D71E231B131C11000395DE /* AnsiCode.swift */; }; @@ -104,6 +105,7 @@ /* Begin PBXFileReference section */ 5499CA961A2394B700783309 /* Components.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Components.plist; sourceTree = ""; }; 5499CA971A2394B700783309 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 65454F451B14D73800319A6C /* ControlStatementRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlStatementRule.swift; sourceTree = ""; }; 83894F211B0C928A006214E1 /* RulesCommand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RulesCommand.swift; sourceTree = ""; }; 83894F231B0CAD41006214E1 /* StructuredText.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = StructuredText.swift; path = ../swiftlint/StructuredText.swift; sourceTree = ""; }; 83D71E231B131C11000395DE /* AnsiCode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnsiCode.swift; sourceTree = ""; }; @@ -382,6 +384,7 @@ isa = PBXGroup; children = ( E88DEA831B0990F500A66CB0 /* ColonRule.swift */, + 65454F451B14D73800319A6C /* ControlStatementRule.swift */, E88DEA891B0992B300A66CB0 /* FileLengthRule.swift */, E88DEA7F1B09903300A66CB0 /* ForceCastRule.swift */, E88DEA8F1B099A3100A66CB0 /* FunctionBodyLengthRule.swift */, @@ -569,6 +572,7 @@ E88DEA941B099C0900A66CB0 /* VariableNameRule.swift in Sources */, E88DEA8A1B0992B300A66CB0 /* FileLengthRule.swift in Sources */, E88DEA751B09852000A66CB0 /* File+SwiftLint.swift in Sources */, + 65454F471B14D76500319A6C /* ControlStatementRule.swift in Sources */, E88DEA8E1B0999CD00A66CB0 /* TypeBodyLengthRule.swift in Sources */, E88DEA6F1B09843F00A66CB0 /* Location.swift in Sources */, E88DEA881B09924C00A66CB0 /* TrailingNewlineRule.swift in Sources */, From 86f5f85458eb4c6c1912d8262f408c65dc91e2f7 Mon Sep 17 00:00:00 2001 From: Andrea Mazzini Date: Tue, 26 May 2015 22:54:11 +0200 Subject: [PATCH 2/5] Split regex for specific keywords. --- .../Rules/ControlStatementRule.swift | 14 +++++++++----- Source/SwiftLintFramework/StyleViolationType.swift | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Source/SwiftLintFramework/Rules/ControlStatementRule.swift b/Source/SwiftLintFramework/Rules/ControlStatementRule.swift index fa05b58d14..4b3418834a 100644 --- a/Source/SwiftLintFramework/Rules/ControlStatementRule.swift +++ b/Source/SwiftLintFramework/Rules/ControlStatementRule.swift @@ -14,13 +14,17 @@ public struct ControlStatementRule: Rule { public let identifier = "control_statement" public func validateFile(file: File) -> [StyleViolation] { - let statement = file.matchPattern("\\s{0,}(if|for|while)\\s{0,}\\(", - withSyntaxKinds: [.Keyword]) - return statement.map { range in + let ifStatement = file.matchPattern("\\s{0,}(if)\\s{0,}\\(", + withSyntaxKinds: [.Keyword]).map{ return ($0, "if") } + let forStatement = file.matchPattern("\\s{0,}(for)\\s{0,}\\(", + withSyntaxKinds: [.Keyword]).map{ return ($0, "for") } + let whileStatement = file.matchPattern("\\s{0,}(while)\\s{0,}\\(", + withSyntaxKinds: [.Keyword]).map{ return ($0, "while") } + return (ifStatement + forStatement + whileStatement).map { violation in return StyleViolation(type: .ControlStatement, - location: Location(file: file, offset: range.location), + location: Location(file: file, offset: violation.0.location), severity: .Low, - reason: "if,for,while,do statements shouldn't wrap their conditionals in parentheses.") + reason: "\(violation.1) statements shouldn't wrap their conditionals in parentheses.") } } diff --git a/Source/SwiftLintFramework/StyleViolationType.swift b/Source/SwiftLintFramework/StyleViolationType.swift index 0e62e7013f..c9cfac6e14 100644 --- a/Source/SwiftLintFramework/StyleViolationType.swift +++ b/Source/SwiftLintFramework/StyleViolationType.swift @@ -16,7 +16,7 @@ public enum StyleViolationType: String, Printable { case TODO = "TODO or FIXME" case Colon = "Colon" case Nesting = "Nesting" - case ControlStatement = "Control Statement" + case ControlStatement = "Control Statement Parentheses" public var description: String { return rawValue } } From 8a6c5e7f5d0af5e85733601e463e8768f7a5822d Mon Sep 17 00:00:00 2001 From: Andrea Mazzini Date: Tue, 26 May 2015 22:58:45 +0200 Subject: [PATCH 3/5] Remove unused examples --- Source/SwiftLintFramework/Rules/ControlStatementRule.swift | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Source/SwiftLintFramework/Rules/ControlStatementRule.swift b/Source/SwiftLintFramework/Rules/ControlStatementRule.swift index 4b3418834a..4e3a3fd053 100644 --- a/Source/SwiftLintFramework/Rules/ControlStatementRule.swift +++ b/Source/SwiftLintFramework/Rules/ControlStatementRule.swift @@ -34,17 +34,10 @@ public struct ControlStatementRule: Rule { nonTriggeringExamples: [ "if condition {\n", "renderGif(data)\n", - "// if (hasBetterSyntax) {\n", - "// for (item in collection) {\n", - "// for (var index = 0; index < 42; index++) {\n", - "// for(item in collection) {\n", - "// for(var index = 0; index < 42; index++) {\n", "for item in collection {\n", "for var index = 0; index < 42; index++ {\n", - "// while (condition) {\n", "while condition {\n", "} while condition {\n", - "// } while (condition {\n", "do { ; } while condition {\n" ], triggeringExamples: [ From 989685723d6b66e89acc872aed98f07c146944ef Mon Sep 17 00:00:00 2001 From: Andrea Mazzini Date: Tue, 26 May 2015 23:03:50 +0200 Subject: [PATCH 4/5] Enable commentDoesntViolate --- Source/SwiftLintFrameworkTests/LinterTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/SwiftLintFrameworkTests/LinterTests.swift b/Source/SwiftLintFrameworkTests/LinterTests.swift index 4a3696eec2..7c5dba3d47 100644 --- a/Source/SwiftLintFrameworkTests/LinterTests.swift +++ b/Source/SwiftLintFrameworkTests/LinterTests.swift @@ -157,7 +157,7 @@ class LinterTests: XCTestCase { } func testControlStatements() { - verifyRule(ControlStatementRule().example, type: .ControlStatement, commentDoesntViolate: false) + verifyRule(ControlStatementRule().example, type: .ControlStatement, commentDoesntViolate: true) } func testNumberOfFunctionsInAType() { From a425a36926be3d7397a903bce0b28d44a445deb5 Mon Sep 17 00:00:00 2001 From: Andrea Mazzini Date: Wed, 27 May 2015 00:11:13 +0200 Subject: [PATCH 5/5] Update CHANGELOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ee3d3fb69..8c9ccfa11c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,10 @@ * Cache parsing to reduce execution time by more than 50%. [Nikolaj Schumacher](https://github.com/nschum) +* Added `ControlStatementRule` to make sure that if/for/while/do statements + do not wrap their conditionals in parentheses. + [Andrea Mazzini](https://github.com/andreamazz) + ##### Bug Fixes None.