From 2dbd06d5aa182e2fc75be77c9ddd396d522646da Mon Sep 17 00:00:00 2001 From: JP Simard Date: Mon, 18 May 2015 05:08:04 +0200 Subject: [PATCH] generalized rules and split off LineLengthRule --- .../SwiftLintFramework/File+SwiftLint.swift | 11 +----- Source/SwiftLintFramework/Rule.swift | 18 ++++++++++ Source/SwiftLintFramework/RuleParameter.swift | 12 +++++++ .../Rules/LineLengthRule.swift | 35 +++++++++++++++++++ .../SwiftLintFramework/StyleViolation.swift | 7 +++- .../SwiftLintFrameworkTests/LinterTests.swift | 20 ++++++++--- SwiftLint.xcodeproj/project.pbxproj | 20 +++++++++++ 7 files changed, 107 insertions(+), 16 deletions(-) create mode 100644 Source/SwiftLintFramework/Rule.swift create mode 100644 Source/SwiftLintFramework/RuleParameter.swift create mode 100644 Source/SwiftLintFramework/Rules/LineLengthRule.swift diff --git a/Source/SwiftLintFramework/File+SwiftLint.swift b/Source/SwiftLintFramework/File+SwiftLint.swift index 828e62882..15c99c68f 100644 --- a/Source/SwiftLintFramework/File+SwiftLint.swift +++ b/Source/SwiftLintFramework/File+SwiftLint.swift @@ -12,15 +12,6 @@ import SwiftXPC typealias Line = (index: Int, content: String) extension File { - func lineLengthViolations(lines: [Line]) -> [StyleViolation] { - return lines.filter({ count($0.content) > 100 }).map { - return StyleViolation(type: .Length, - location: Location(file: self.path, line: $0.index), - reason: "Line #\($0.index) should be 100 characters or less: " + - "currently \(count($0.content)) characters") - } - } - func leadingWhitespaceViolations(contents: String) -> [StyleViolation] { let countOfLeadingWhitespace = contents.countOfLeadingCharactersInSet( NSCharacterSet.whitespaceAndNewlineCharacterSet() @@ -323,7 +314,7 @@ extension File { let lines = contents.lines() // FIXME: Using '+' to concatenate these arrays would be nicer, // but slows the compiler to a crawl. - var violations = lineLengthViolations(lines) + var violations = LineLengthRule.validateFile(self) violations.extend(leadingWhitespaceViolations(contents)) violations.extend(trailingLineWhitespaceViolations(lines)) violations.extend(trailingNewlineViolations(contents)) diff --git a/Source/SwiftLintFramework/Rule.swift b/Source/SwiftLintFramework/Rule.swift new file mode 100644 index 000000000..788888d2a --- /dev/null +++ b/Source/SwiftLintFramework/Rule.swift @@ -0,0 +1,18 @@ +// +// Rule.swift +// SwiftLint +// +// Created by JP Simard on 2015-05-16. +// Copyright (c) 2015 Realm. All rights reserved. +// + +import SourceKittenFramework + +protocol Rule { + typealias ParameterType + + static var identifier: String { get } + static var parameters: [RuleParameter] { get } + + static func validateFile(file: File) -> [StyleViolation] +} diff --git a/Source/SwiftLintFramework/RuleParameter.swift b/Source/SwiftLintFramework/RuleParameter.swift new file mode 100644 index 000000000..1385de55a --- /dev/null +++ b/Source/SwiftLintFramework/RuleParameter.swift @@ -0,0 +1,12 @@ +// +// RuleParameter.swift +// SwiftLint +// +// Created by JP Simard on 2015-05-16. +// Copyright (c) 2015 Realm. All rights reserved. +// + +struct RuleParameter { + let severity: ViolationSeverity + let value: T +} diff --git a/Source/SwiftLintFramework/Rules/LineLengthRule.swift b/Source/SwiftLintFramework/Rules/LineLengthRule.swift new file mode 100644 index 000000000..00d2e8020 --- /dev/null +++ b/Source/SwiftLintFramework/Rules/LineLengthRule.swift @@ -0,0 +1,35 @@ +// +// LineLengthRule.swift +// SwiftLint +// +// Created by JP Simard on 2015-05-16. +// Copyright (c) 2015 Realm. All rights reserved. +// + +import SourceKittenFramework + +struct LineLengthRule: Rule { + static let identifier = "line_length" + static let parameters = [ + RuleParameter(severity: .VeryLow, value: 100), + RuleParameter(severity: .Low, value: 120), + RuleParameter(severity: .Medium, value: 150), + RuleParameter(severity: .High, value: 200), + RuleParameter(severity: .VeryHigh, value: 250) + ] + + static func validateFile(file: File) -> [StyleViolation] { + return compact(file.contents.lines().map { line in + for parameter in reverse(self.parameters) { + if count(line.content) > parameter.value { + return StyleViolation(type: .Length, + location: Location(file: file.path, line: line.index), + severity: parameter.severity, + reason: "Line should be 100 characters or less: currently " + + "\(count(line.content)) characters") + } + } + return nil + }) + } +} diff --git a/Source/SwiftLintFramework/StyleViolation.swift b/Source/SwiftLintFramework/StyleViolation.swift index 90f5a3346..f60dacc66 100644 --- a/Source/SwiftLintFramework/StyleViolation.swift +++ b/Source/SwiftLintFramework/StyleViolation.swift @@ -20,7 +20,11 @@ public struct StyleViolation: Printable, Equatable { } public init(type: StyleViolationType, location: Location, reason: String? = nil) { - severity = .Low + self.init(type: type, location: location, severity: .Low, reason: reason) + } + + public init(type: StyleViolationType, location: Location, severity: ViolationSeverity, reason: String? = nil) { + self.severity = severity self.type = type self.location = location self.reason = reason @@ -40,5 +44,6 @@ Returns true if `lhs` StyleViolation is equal to `rhs` StyleViolation. public func ==(lhs: StyleViolation, rhs: StyleViolation) -> Bool { return lhs.type == rhs.type && lhs.location == rhs.location && + lhs.severity == rhs.severity && lhs.reason == rhs.reason } diff --git a/Source/SwiftLintFrameworkTests/LinterTests.swift b/Source/SwiftLintFrameworkTests/LinterTests.swift index 9e0599ae2..cff2be7b8 100644 --- a/Source/SwiftLintFrameworkTests/LinterTests.swift +++ b/Source/SwiftLintFrameworkTests/LinterTests.swift @@ -210,11 +210,21 @@ class LinterTests: XCTestCase { // MARK: String Violations func testLineLengths() { - let longLine = join("", Array(count: 100, repeatedValue: "/")) - XCTAssertEqual(violations(longLine + "\n"), []) - XCTAssertEqual(violations(longLine + "/\n"), [StyleViolation(type: .Length, - location: Location(file: nil, line: 1), - reason: "Line #1 should be 100 characters or less: currently 101 characters")]) + let longLine = join("", Array(count: 100, repeatedValue: "/")) + "\n" + XCTAssertEqual(violations(longLine), []) + let testCases: [(String, Int, ViolationSeverity)] = [ + ("/", 101, .VeryLow), + (join("", Array(count: 21, repeatedValue: "/")), 121, .Low), + (join("", Array(count: 51, repeatedValue: "/")), 151, .Medium), + (join("", Array(count: 101, repeatedValue: "/")), 201, .High), + (join("", Array(count: 151, repeatedValue: "/")), 251, .VeryHigh) + ] + for testCase in testCases { + XCTAssertEqual(violations(testCase.0 + longLine), [StyleViolation(type: .Length, + location: Location(file: nil, line: 1), + severity: testCase.2, + reason: "Line should be 100 characters or less: currently \(testCase.1) characters")]) + } } func testTrailingNewlineAtEndOfFile() { diff --git a/SwiftLint.xcodeproj/project.pbxproj b/SwiftLint.xcodeproj/project.pbxproj index 4968233b4..00b246f45 100644 --- a/SwiftLint.xcodeproj/project.pbxproj +++ b/SwiftLint.xcodeproj/project.pbxproj @@ -26,6 +26,9 @@ E88DEA711B09847500A66CB0 /* ViolationSeverity.swift in Sources */ = {isa = PBXBuildFile; fileRef = E88DEA701B09847500A66CB0 /* ViolationSeverity.swift */; }; E88DEA731B0984C400A66CB0 /* String+SwiftLint.swift in Sources */ = {isa = PBXBuildFile; fileRef = E88DEA721B0984C400A66CB0 /* String+SwiftLint.swift */; }; E88DEA751B09852000A66CB0 /* File+SwiftLint.swift in Sources */ = {isa = PBXBuildFile; fileRef = E88DEA741B09852000A66CB0 /* File+SwiftLint.swift */; }; + E88DEA771B098D0C00A66CB0 /* Rule.swift in Sources */ = {isa = PBXBuildFile; fileRef = E88DEA761B098D0C00A66CB0 /* Rule.swift */; }; + E88DEA791B098D4400A66CB0 /* RuleParameter.swift in Sources */ = {isa = PBXBuildFile; fileRef = E88DEA781B098D4400A66CB0 /* RuleParameter.swift */; }; + E88DEA7C1B098D7D00A66CB0 /* LineLengthRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = E88DEA7B1B098D7D00A66CB0 /* LineLengthRule.swift */; }; E8AB1A2E1A649F2100452012 /* libclang.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = E8AB1A2D1A649F2100452012 /* libclang.dylib */; }; E8BA7E111B07A3EC003E02D0 /* Commandant.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E8BA7E101B07A3EC003E02D0 /* Commandant.framework */; }; E8BA7E131B07A3F3003E02D0 /* LlamaKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E8BA7E121B07A3F3003E02D0 /* LlamaKit.framework */; }; @@ -122,6 +125,9 @@ E88DEA701B09847500A66CB0 /* ViolationSeverity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViolationSeverity.swift; sourceTree = ""; }; E88DEA721B0984C400A66CB0 /* String+SwiftLint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+SwiftLint.swift"; sourceTree = ""; }; E88DEA741B09852000A66CB0 /* File+SwiftLint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "File+SwiftLint.swift"; sourceTree = ""; }; + E88DEA761B098D0C00A66CB0 /* Rule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Rule.swift; sourceTree = ""; }; + E88DEA781B098D4400A66CB0 /* RuleParameter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RuleParameter.swift; sourceTree = ""; }; + E88DEA7B1B098D7D00A66CB0 /* LineLengthRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LineLengthRule.swift; sourceTree = ""; }; E8AB1A2D1A649F2100452012 /* libclang.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libclang.dylib; path = Toolchains/XcodeDefault.xctoolchain/usr/lib/libclang.dylib; sourceTree = DEVELOPER_DIR; }; E8BA7E101B07A3EC003E02D0 /* Commandant.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Commandant.framework; sourceTree = BUILT_PRODUCTS_DIR; }; E8BA7E121B07A3F3003E02D0 /* LlamaKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = LlamaKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -274,7 +280,10 @@ D0D1216E19E87B05005E4BAA /* SwiftLintFramework */ = { isa = PBXGroup; children = ( + E88DEA7A1B098D7300A66CB0 /* Rules */, E88DEA741B09852000A66CB0 /* File+SwiftLint.swift */, + E88DEA761B098D0C00A66CB0 /* Rule.swift */, + E88DEA781B098D4400A66CB0 /* RuleParameter.swift */, E812249B1B04FADC001783D2 /* Linter.swift */, E88DEA6E1B09843F00A66CB0 /* Location.swift */, E88DEA721B0984C400A66CB0 /* String+SwiftLint.swift */, @@ -321,6 +330,14 @@ name = "Supporting Files"; sourceTree = ""; }; + E88DEA7A1B098D7300A66CB0 /* Rules */ = { + isa = PBXGroup; + children = ( + E88DEA7B1B098D7D00A66CB0 /* LineLengthRule.swift */, + ); + path = Rules; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -468,10 +485,13 @@ buildActionMask = 2147483647; files = ( E812249C1B04FADC001783D2 /* Linter.swift in Sources */, + E88DEA791B098D4400A66CB0 /* RuleParameter.swift in Sources */, E88DEA731B0984C400A66CB0 /* String+SwiftLint.swift in Sources */, E88DEA6D1B09842200A66CB0 /* StyleViolationType.swift in Sources */, E88DEA751B09852000A66CB0 /* File+SwiftLint.swift in Sources */, E88DEA6F1B09843F00A66CB0 /* Location.swift in Sources */, + E88DEA771B098D0C00A66CB0 /* Rule.swift in Sources */, + E88DEA7C1B098D7D00A66CB0 /* LineLengthRule.swift in Sources */, E88DEA711B09847500A66CB0 /* ViolationSeverity.swift in Sources */, E88DEA6B1B0983FE00A66CB0 /* StyleViolation.swift in Sources */, );