Skip to content

Commit

Permalink
added base cuddled else rule
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael Skiba committed Jun 9, 2016
1 parent 5641b9f commit 314f7db
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 4 deletions.
1 change: 1 addition & 0 deletions Source/SwiftLintFramework/Models/MasterRuleList.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public let masterRuleList = RuleList(rules:
ConditionalBindingCascadeRule.self,
ControlStatementRule.self,
CustomRules.self,
CuddledElseRule.self,
CyclomaticComplexityRule.self,
EmptyCountRule.self,
FileLengthRule.self,
Expand Down
94 changes: 94 additions & 0 deletions Source/SwiftLintFramework/Rules/CuddledElseRule.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
//
// CuddledElseRule.swift
// SwiftLint
//
// Created by Michael Skiba on 5/3/16.
// Copyright © 2016 Realm. All rights reserved.
//

import Foundation
import SourceKittenFramework

public struct CuddledElseRule: ConfigurationProviderRule, OptInRule {

public var configuration = SeverityConfiguration(.Warning)

public init() {}

public static let description = RuleDescription(
identifier: "cuddled_else",
name: "Cuddled else",
description: "Else and catch should on the next line, with equal indentation to the " +
"previous declaration.",
nonTriggeringExamples: [
" }\n else if {",
" }\n else {",
" }\n catch {",
" }\n\n catch {",
"\n\n }\n catch {",
"\"}\nelse{\"",
"struct A { let catchphrase: Int }\nlet a = A(\n catchphrase: 0\n)",
"struct A { let `catch`: Int }\nlet a = A(\n `catch`: 0\n)"
],
triggeringExamples: [
"↓ }else if {",
"↓}\n else {",
"↓ }\ncatch {",
"↓}\n\t catch {"
]
)

public func validateFile(file: File) -> [StyleViolation] {
return violationRangesInFile(file, withPattern: CuddledElseRule.pattern).flatMap { range in
return StyleViolation(ruleDescription: self.dynamicType.description,
severity: configuration.severity,
location: Location(file: file, characterOffset: range.location))
}
}

// MARK: - Private

// match literal '}'
// preceded by whitespace (or nothing)
// followed by 1) nothing, 2) two+ whitespace/newlines or 3) newlines or tabs
// followed by newline and the same amount of whitespace then 'else' or 'catch' literals
private static let pattern = "([ \t]*)\\}(\\n+)?([ \t]*)\\b(else|catch)\\b"
private static let regularExpression = (try? NSRegularExpression(pattern: pattern, options: []))
?? NSRegularExpression()

private func violationRangesInFile(file: File, withPattern pattern: String) -> [NSRange] {
let contents = file.contents
let range = NSRange(location: 0, length: contents.utf16.count)
let syntaxMap = file.syntaxMap
let matches = CuddledElseRule.regularExpression.matchesInString(contents,
options: [],
range: range)
let validMatches = matches.flatMap { match -> NSRange? in
if match.numberOfRanges != 5 {
return match.range
}
if match.rangeAtIndex(2).length == 0 {
return match.range
}
let range1 = match.rangeAtIndex(1)
let range2 = match.rangeAtIndex(3)
let whitespace1 = contents.substring(range1.location, length: range1.length)
let whitespace2 = contents.substring(range2.location, length: range2.length)
if whitespace1 == whitespace2 {
return nil
}
return match.range
}
let rangesWithValidTokens = validMatches.filter { range in
guard let matchRange = contents.NSRangeToByteRange(start: range.location,
length: range.length) else {
return false
}
let tokens = syntaxMap.tokensIn(matchRange).flatMap { SyntaxKind(rawValue: $0.type) }
return tokens == [.Keyword]
}

return rangesWithValidTokens
}

}
12 changes: 8 additions & 4 deletions SwiftLint.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
6C7045441C6ADA450003F15A /* SourceKitCrashTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C7045431C6ADA450003F15A /* SourceKitCrashTests.swift */; };
6CB514E91C760C6900FA02C4 /* Structure+SwiftLint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CB514E81C760C6900FA02C4 /* Structure+SwiftLint.swift */; };
6CC4259B1C77046200AEA885 /* SyntaxMap+SwiftLint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CC4259A1C77046200AEA885 /* SyntaxMap+SwiftLint.swift */; };
72B761331CD9193500B846D5 /* CuddledElseRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72B761311CD917DB00B846D5 /* CuddledElseRule.swift */; };
6CCFCF2A1CFEF729003239EB /* Commandant.framework in Embed Frameworks into SwiftLintFramework.framework */ = {isa = PBXBuildFile; fileRef = E8BA7E101B07A3EC003E02D0 /* Commandant.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
6CCFCF2C1CFEF72D003239EB /* Result.framework in Embed Frameworks into SwiftLintFramework.framework */ = {isa = PBXBuildFile; fileRef = E8BA7E121B07A3F3003E02D0 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
6CCFCF2D1CFEF731003239EB /* SourceKittenFramework.framework in Embed Frameworks into SwiftLintFramework.framework */ = {isa = PBXBuildFile; fileRef = E876BFBD1B07828500114ED5 /* SourceKittenFramework.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
Expand Down Expand Up @@ -207,6 +208,7 @@
6C7045431C6ADA450003F15A /* SourceKitCrashTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SourceKitCrashTests.swift; sourceTree = "<group>"; };
6CB514E81C760C6900FA02C4 /* Structure+SwiftLint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Structure+SwiftLint.swift"; sourceTree = "<group>"; };
6CC4259A1C77046200AEA885 /* SyntaxMap+SwiftLint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SyntaxMap+SwiftLint.swift"; sourceTree = "<group>"; };
72B761311CD917DB00B846D5 /* CuddledElseRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CuddledElseRule.swift; sourceTree = "<group>"; };
83894F211B0C928A006214E1 /* RulesCommand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RulesCommand.swift; sourceTree = "<group>"; };
83D71E261B131EB5000395DE /* RuleDescription.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RuleDescription.swift; sourceTree = "<group>"; };
B58AEED51C492C7B00E901FD /* ForceUnwrappingRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ForceUnwrappingRule.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -567,29 +569,32 @@
E88DEA7A1B098D7300A66CB0 /* Rules */ = {
isa = PBXGroup;
children = (
3BCC04CE1C4F56D3006073C3 /* RuleConfigurations */,
1F11B3CE1C252F23002E8FA8 /* ClosingBraceRule.swift */,
E88DEA831B0990F500A66CB0 /* ColonRule.swift */,
695BE9CE1BDFD92B0071E985 /* CommaRule.swift */,
00970C741C3FC3A0007C4A83 /* ConditionalBindingCascadeRule.swift */,
65454F451B14D73800319A6C /* ControlStatementRule.swift */,
72B761311CD917DB00B846D5 /* CuddledElseRule.swift */,
3B1DF0111C5148140011BCED /* CustomRules.swift */,
2E02005E1C54BF680024D09D /* CyclomaticComplexityRule.swift */,
E847F0A81BFBBABD00EA9363 /* EmptyCountRule.swift */,
E88DEA891B0992B300A66CB0 /* FileLengthRule.swift */,
E88DEA7F1B09903300A66CB0 /* ForceCastRule.swift */,
E816194D1BFBFEAB00946723 /* ForceTryRule.swift */,
B58AEED51C492C7B00E901FD /* ForceUnwrappingRule.swift */,
E88DEA8F1B099A3100A66CB0 /* FunctionBodyLengthRule.swift */,
2E5761A91C573B83003271AF /* FunctionParameterCountRule.swift */,
E88DEA7D1B098F2A00A66CB0 /* LeadingWhitespaceRule.swift */,
4DB7815C1CAD690100BC4723 /* LegacyCGGeometryFunctionsRule.swift */,
006ECFC31C44E99E00EF6364 /* LegacyConstantRule.swift */,
D44AD2741C0AA3730048F7B0 /* LegacyConstructorRule.swift */,
4DB7815C1CAD690100BC4723 /* LegacyCGGeometryFunctionsRule.swift */,
E88DEA7B1B098D7D00A66CB0 /* LineLengthRule.swift */,
E849FF271BF9481A009AE999 /* MissingDocsRule.swift */,
E88DEA951B099CF200A66CB0 /* NestingRule.swift */,
692B1EB11BD7E00F00EAABFF /* OpeningBraceRule.swift */,
E5A167C81B25A0B000CF2D03 /* OperatorFunctionWhitespaceRule.swift */,
E57B23C01B1D8BF000DEA512 /* ReturnArrowWhitespaceRule.swift */,
3BCC04CE1C4F56D3006073C3 /* RuleConfigurations */,
692B60AB1BD8F2E700C7AA22 /* StatementPositionRule.swift */,
E88DEA811B0990A700A66CB0 /* TodoRule.swift */,
E88DEA871B09924C00A66CB0 /* TrailingNewlineRule.swift */,
Expand All @@ -599,8 +604,6 @@
E88DEA911B099B1F00A66CB0 /* TypeNameRule.swift */,
E81CDE701C00FEAA00B430F6 /* ValidDocsRule.swift */,
E88DEA931B099C0900A66CB0 /* VariableNameRule.swift */,
2E02005E1C54BF680024D09D /* CyclomaticComplexityRule.swift */,
2E5761A91C573B83003271AF /* FunctionParameterCountRule.swift */,
);
path = Rules;
sourceTree = "<group>";
Expand Down Expand Up @@ -915,6 +918,7 @@
E88DEA711B09847500A66CB0 /* ViolationSeverity.swift in Sources */,
3BD9CD3D1C37175B009A5D25 /* YamlParser.swift in Sources */,
E88DEA8C1B0999A000A66CB0 /* ASTRule.swift in Sources */,
72B761331CD9193500B846D5 /* CuddledElseRule.swift in Sources */,
E88DEA6B1B0983FE00A66CB0 /* StyleViolation.swift in Sources */,
3BB47D831C514E8100AE6A10 /* RegexConfiguration.swift in Sources */,
3B1150CA1C31FC3F00D83B1E /* Yaml+SwiftLint.swift in Sources */,
Expand Down
4 changes: 4 additions & 0 deletions Tests/SwiftLintFramework/RulesTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ class RulesTests: XCTestCase {
verifyRule(ControlStatementRule.description)
}

func testCuddledElse() {
verifyRule(CuddledElseRule.description)
}

func testCyclomaticComplexity() {
verifyRule(CyclomaticComplexityRule.description)
}
Expand Down

0 comments on commit 314f7db

Please sign in to comment.