Skip to content

Commit

Permalink
[OperatorFunctionWhitespaceRule] refactored rule. Fixes #60.
Browse files Browse the repository at this point in the history
  • Loading branch information
jpsim committed Jun 17, 2015
1 parent 752d970 commit 90dfe36
Show file tree
Hide file tree
Showing 7 changed files with 32 additions and 70 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
##### Enhancements

* Added `OperatorFunctionWhitespaceRule` to make sure that
you use whitespace around operators when defining them.
you use whitespace around operators when defining them.
[Akira Hirakawa](https://github.com/akirahrkw)
[#60](https://github.com/realm/SwiftLint/issues/60)

Expand Down
8 changes: 4 additions & 4 deletions Source/SwiftLintFramework/File+SwiftLint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import SwiftXPC
typealias Line = (index: Int, content: String)

extension File {
public func matchPattern(pattern: String, withSyntaxKinds syntaxKinds: [SyntaxKind] = []) ->
public func matchPattern(pattern: String, withSyntaxKinds syntaxKinds: [SyntaxKind]) ->
[NSRange] {
return matchPattern(pattern).filter { _, kindsInRange in
return kindsInRange.count == syntaxKinds.count &&
Expand All @@ -22,9 +22,9 @@ extension File {

public func matchPattern(pattern: String) -> [(NSRange, [SyntaxKind])] {
return flatMap(NSRegularExpression(pattern: pattern, options: nil, error: nil)) { regex in
let range = NSRange(location: 0, length: count(self.contents.utf16))
let syntax = self.syntaxMap
let matches = regex.matchesInString(self.contents, options: nil, range: range)
let range = NSRange(location: 0, length: count(contents.utf16))
let syntax = syntaxMap
let matches = regex.matchesInString(contents, options: nil, range: range)
return map(matches as? [NSTextCheckingResult]) { matches in
return matches.map { match in
let tokensInRange = syntax.tokens.filter {
Expand Down
3 changes: 1 addition & 2 deletions Source/SwiftLintFramework/Rules/ForceCastRule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ public struct ForceCastRule: Rule {
ruleName: "Force Cast Rule",
ruleDescription: "This rule checks whether you don't do force casts.",
nonTriggeringExamples: [
"NSNumber() as? Int\n",
"// NSNumber() as! Int\n",
"NSNumber() as? Int\n"
],
triggeringExamples: [ "NSNumber() as! Int\n" ]
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,81 +7,44 @@
//

import SourceKittenFramework
import SwiftXPC

public struct OperatorFunctionWhitespaceRule: ASTRule {
public struct OperatorFunctionWhitespaceRule: Rule {
public init() {}

public let identifier = "operator_whitespace"

public func validateFile(file: File) -> [StyleViolation] {
return validateFile(file, dictionary: file.structure.dictionary)
}

public func validateFile(file: File, dictionary: XPCDictionary) -> [StyleViolation] {
return (dictionary["key.substructure"] as? XPCArray ?? []).flatMap { subItem in
var violations = [StyleViolation]()
if let subDict = subItem as? XPCDictionary,
let kindString = subDict["key.kind"] as? String,
let kind = flatMap(kindString, { SwiftDeclarationKind(rawValue: $0) }) {
violations.extend(validateFile(file, dictionary: subDict))
violations.extend(validateFile(file, kind: kind, dictionary: subDict))
}
return violations
}
}

public func validateFile(file: File,
kind: SwiftDeclarationKind,
dictionary: XPCDictionary) -> [StyleViolation] {
let functionKinds: [SwiftDeclarationKind] = [
.FunctionFree,
]
if !contains(functionKinds, kind) {
return []
}
var violations = [StyleViolation]()
if let nameOffset = flatMap(dictionary["key.nameoffset"] as? Int64, { Int($0) }),
let nameLength = flatMap(dictionary["key.namelength"] as? Int64, { Int($0) }),
let offset = flatMap(dictionary["key.offset"] as? Int64, { Int($0) }) {

let location = Location(file: file, offset: offset)
let startAdvance = advance(file.contents.startIndex, nameOffset)
let endAdvance = advance(startAdvance, nameLength)
let range = Range(start: startAdvance, end: endAdvance)
let definition = file.contents.substringWithRange(range)

let ope1 = ["/", "=", "-", "+", "!", "*", "|", "^", "~", "?", "."].map({"\\\($0)"})
let ope2 = ["%", "<", ">", "&"]
let ope = "".join(ope1 + ope2)
let pattern = "^[\(ope)]+(<[A-Z]+>)?\\("

if let regex = NSRegularExpression(pattern: pattern, options: nil, error: nil) {
let matchRange = NSRange(location: 0, length: count(definition.utf16))
let matches = regex.matchesInString(definition, options: nil, range: matchRange)

if matches.count > 0 {
violations.append(StyleViolation(type: .OperatorFunctionWhitespace,
location: location,
severity: .Medium,
reason: "Use whitespace around operators when defining them"))
}
}
let operators = ["/", "=", "-", "+", "!", "*", "|", "^", "~", "?", "."].map({"\\\($0)"}) +
["%", "<", ">", "&"]
let zeroOrManySpaces = "(\\s{0}|\\s{2,})"
let pattern1 = "func\\s+[" + "".join(operators) + "]+\(zeroOrManySpaces)(<[A-Z]+>)?\\("
let pattern2 = "func\(zeroOrManySpaces)[" + "".join(operators) + "]+\\s+(<[A-Z]+>)?\\("
return file.matchPattern("(\(pattern1)|\(pattern2))").filter { _, syntaxKinds in
return syntaxKinds.first == .Keyword
}.map { range, _ in
return StyleViolation(type: .OperatorFunctionWhitespace,
location: Location(file: file, offset: range.location),
severity: .Medium,
reason: self.example.ruleDescription)
}
return violations
}

public let example = RuleExample(
ruleName: "Operator Function Whitespace Rule",
ruleDescription: "Use whitespace around operators when defining them.",
ruleDescription: "Use a single whitespace around operators when " +
"defining them.",
nonTriggeringExamples: [
"func <| (lhs: Int, rhs: Int) -> Int {}\n",
"func <|< <A>(lhs: A, rhs: A) -> A {}\n",
"func abc(lhs: Int, rhs: Int) -> Int {}\n"
],
triggeringExamples: [
"func <|(lhs: Int, rhs: Int) -> Int {}\n",
"func <|<<A>(lhs: A, rhs: A) -> A {}\n"
"func <|(lhs: Int, rhs: Int) -> Int {}\n", // no spaces after
"func <|<<A>(lhs: A, rhs: A) -> A {}\n", // no spaces after
"func <| (lhs: Int, rhs: Int) -> Int {}\n", // 2 spaces after
"func <|< <A>(lhs: A, rhs: A) -> A {}\n", // 2 spaces after
"func <| (lhs: Int, rhs: Int) -> Int {}\n", // 2 spaces before
"func <|< <A>(lhs: A, rhs: A) -> A {}\n" // 2 spaces before
]
)
}
4 changes: 0 additions & 4 deletions Source/SwiftLintFrameworkTests/ASTRuleTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,6 @@ class ASTRuleTests: XCTestCase {
}
}

func testOperatorFunctionWhitespace() {
verifyRule(OperatorFunctionWhitespaceRule().example, type: .OperatorFunctionWhitespace)
}

func testNesting() {
verifyRule(NestingRule().example, type: .Nesting, commentDoesntViolate: false)
}
Expand Down
4 changes: 4 additions & 0 deletions Source/SwiftLintFrameworkTests/StringRuleTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ class StringRuleTests: XCTestCase {
verifyRule(ForceCastRule().example, type: .ForceCast)
}

func testOperatorFunctionWhitespace() {
verifyRule(OperatorFunctionWhitespaceRule().example, type: .OperatorFunctionWhitespace)
}

func testTodoOrFIXME() {
verifyRule(TodoRule().example, type: .TODO)
}
Expand Down
2 changes: 1 addition & 1 deletion SwiftLint.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -410,14 +410,14 @@
E88DEA7D1B098F2A00A66CB0 /* LeadingWhitespaceRule.swift */,
E88DEA7B1B098D7D00A66CB0 /* LineLengthRule.swift */,
E88DEA951B099CF200A66CB0 /* NestingRule.swift */,
E5A167C81B25A0B000CF2D03 /* OperatorFunctionWhitespaceRule.swift */,
E88DEA811B0990A700A66CB0 /* TodoRule.swift */,
E88DEA871B09924C00A66CB0 /* TrailingNewlineRule.swift */,
E88DEA851B0991BF00A66CB0 /* TrailingWhitespaceRule.swift */,
E88DEA8D1B0999CD00A66CB0 /* TypeBodyLengthRule.swift */,
E88DEA911B099B1F00A66CB0 /* TypeNameRule.swift */,
E88DEA931B099C0900A66CB0 /* VariableNameRule.swift */,
E57B23C01B1D8BF000DEA512 /* ReturnArrowWhitespaceRule.swift */,
E5A167C81B25A0B000CF2D03 /* OperatorFunctionWhitespaceRule.swift */,
);
path = Rules;
sourceTree = "<group>";
Expand Down

0 comments on commit 90dfe36

Please sign in to comment.