diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fd28bef5f..7a2c4d89b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ ##### Enhancements +* Adds `MarkRule` rule. + [Krzysztof Rodak](https://github.com/krodak) + * Add `PrivateOutletRule` Opt-In rule to enforce `@IBOutlet` instance variables to be `private`. [Olivier Halligon](https://github.com/AliSoftware) diff --git a/Source/SwiftLintFramework/Models/MasterRuleList.swift b/Source/SwiftLintFramework/Models/MasterRuleList.swift index ab5de8cefc..fd1f5f8d17 100644 --- a/Source/SwiftLintFramework/Models/MasterRuleList.swift +++ b/Source/SwiftLintFramework/Models/MasterRuleList.swift @@ -60,6 +60,7 @@ public let masterRuleList = RuleList(rules: LegacyConstructorRule.self, LegacyNSGeometryFunctionsRule.self, LineLengthRule.self, + MarkRule.self, MissingDocsRule.self, NestingRule.self, OpeningBraceRule.self, diff --git a/Source/SwiftLintFramework/Rules/MarkRule.swift b/Source/SwiftLintFramework/Rules/MarkRule.swift new file mode 100644 index 0000000000..4efb93e38e --- /dev/null +++ b/Source/SwiftLintFramework/Rules/MarkRule.swift @@ -0,0 +1,47 @@ +// +// MarkRule.swift +// SwiftLint +// +// Created by Krzysztof Rodak on 22/08/16. +// Copyright © 2016 Realm. All rights reserved. +// + +import SourceKittenFramework + +public struct MarkRule: ConfigurationProviderRule { + + public var configuration = SeverityConfiguration(.Warning) + + public init() {} + + public static let description = RuleDescription( + identifier: "mark", + name: "Mark", + description: "MARK comment should be in valid format.", + nonTriggeringExamples: [ + "// MARK: good\n", + "// MARK: - good\n" + ], + triggeringExamples: [ + "//MARK: bad", + "//MARK:bad", + "// MARK: -bad", + "// MARK:-bad", + "//MARK:-bad" + ] + ) + + public func validateFile(file: File) -> [StyleViolation] { + let options = ["MARK:[^ ]", "[^ ]MARK: [^-]", "\\sMARK:[^ ]", "MARK:[ ][-][^ ]"] + let pattern = "(" + options.joinWithSeparator("|") + ")" + + return file.matchPattern(pattern).flatMap { range, syntaxKinds in + if syntaxKinds.filter({ $0.isCommentLike == false }).isEmpty == false { + return nil + } + return StyleViolation(ruleDescription: self.dynamicType.description, + severity: configuration.severity, + location: Location(file: file, characterOffset: range.location)) + } + } +} diff --git a/SwiftLint.xcodeproj/project.pbxproj b/SwiftLint.xcodeproj/project.pbxproj index 7b3c2315f1..c37f21a472 100644 --- a/SwiftLint.xcodeproj/project.pbxproj +++ b/SwiftLint.xcodeproj/project.pbxproj @@ -53,6 +53,7 @@ 7250948A1D0859260039B353 /* StatementPositionConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 725094881D0855760039B353 /* StatementPositionConfiguration.swift */; }; 83894F221B0C928A006214E1 /* RulesCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83894F211B0C928A006214E1 /* RulesCommand.swift */; }; 83D71E281B131ECE000395DE /* RuleDescription.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83D71E261B131EB5000395DE /* RuleDescription.swift */; }; + 85DA81321D6B471000951BC4 /* MarkRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 856651A61D6B395F005E6B29 /* MarkRule.swift */; }; B58AEED61C492C7B00E901FD /* ForceUnwrappingRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = B58AEED51C492C7B00E901FD /* ForceUnwrappingRule.swift */; }; BFF028AE1CBCF8A500B38A9D /* TrailingWhitespaceConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF48D2D61CBCCA5F0080BDAE /* TrailingWhitespaceConfiguration.swift */; }; D0AAAB5019FB0960007B24B3 /* SwiftLintFramework.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D0D1216D19E87B05005E4BAA /* SwiftLintFramework.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -215,6 +216,7 @@ 725094881D0855760039B353 /* StatementPositionConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatementPositionConfiguration.swift; sourceTree = ""; }; 83894F211B0C928A006214E1 /* RulesCommand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RulesCommand.swift; sourceTree = ""; }; 83D71E261B131EB5000395DE /* RuleDescription.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RuleDescription.swift; sourceTree = ""; }; + 856651A61D6B395F005E6B29 /* MarkRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarkRule.swift; sourceTree = ""; }; B58AEED51C492C7B00E901FD /* ForceUnwrappingRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ForceUnwrappingRule.swift; sourceTree = ""; }; BF48D2D61CBCCA5F0080BDAE /* TrailingWhitespaceConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrailingWhitespaceConfiguration.swift; sourceTree = ""; }; D0D1211B19E87861005E4BAA /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; usesTabs = 0; }; @@ -615,6 +617,7 @@ E88DEA911B099B1F00A66CB0 /* TypeNameRule.swift */, E81CDE701C00FEAA00B430F6 /* ValidDocsRule.swift */, E88DEA931B099C0900A66CB0 /* VariableNameRule.swift */, + 856651A61D6B395F005E6B29 /* MarkRule.swift */, ); path = Rules; sourceTree = ""; @@ -918,6 +921,7 @@ 2E5761AA1C573B83003271AF /* FunctionParameterCountRule.swift in Sources */, E86396C91BADB2B9002C9E88 /* JSONReporter.swift in Sources */, E881985A1BEA96EA00333A11 /* OperatorFunctionWhitespaceRule.swift in Sources */, + 85DA81321D6B471000951BC4 /* MarkRule.swift in Sources */, 3BCC04D21C4F56D3006073C3 /* NameConfiguration.swift in Sources */, E88DEA6F1B09843F00A66CB0 /* Location.swift in Sources */, E88DEA771B098D0C00A66CB0 /* Rule.swift in Sources */, diff --git a/Tests/SwiftLintFramework/RulesTests.swift b/Tests/SwiftLintFramework/RulesTests.swift index fe26d103d6..f67a1cafc4 100644 --- a/Tests/SwiftLintFramework/RulesTests.swift +++ b/Tests/SwiftLintFramework/RulesTests.swift @@ -154,6 +154,10 @@ class RulesTests: XCTestCase { stringDoesntViolate: false) } + func testMark() { + verifyRule(MarkRule.description, commentDoesntViolate: false) + } + func testMissingDocs() { verifyRule(MissingDocsRule.description) }