Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add trailing semicolon rule #203

Merged
merged 2 commits into from
Nov 17, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
rules. Remove length checks on `VariableNameRule`.
[Mickael Morier](https://github.com/mmorier)

* Add trailing semicolon rule.
[JP Simard](https://github.com/jpsim)

##### Bug Fixes

* All rules now print their identifiers in reports.
Expand Down
4 changes: 2 additions & 2 deletions Source/SwiftLintFramework/Extensions/File+SwiftLint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ extension File {
}

public func matchPattern(pattern: String) -> [(NSRange, [SyntaxKind])] {
let regex = try! NSRegularExpression(pattern: pattern, options: [])
let regex = try! NSRegularExpression(pattern: pattern, options: [.AnchorsMatchLines])
let range = NSRange(location: 0, length: contents.utf16.count)
let syntax = syntaxMap
let matches = regex.matchesInString(contents, options: [], range: range)
Expand Down Expand Up @@ -78,7 +78,7 @@ extension File {
*/
public func matchPattern(pattern: String,
excludingSyntaxKinds syntaxKinds: [SyntaxKind]) -> [NSRange] {
let regex = try! NSRegularExpression(pattern: pattern, options: [])
let regex = try! NSRegularExpression(pattern: pattern, options: [.AnchorsMatchLines])
let range = NSRange(location: 0, length: contents.utf16.count)
let syntax = syntaxMap
let matches = regex.matchesInString(contents, options: [], range: range)
Expand Down
1 change: 1 addition & 0 deletions Source/SwiftLintFramework/Models/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ public struct Configuration {
StatementPositionRule(),
TodoRule(),
TrailingNewlineRule(),
TrailingSemicolonRule(),
TrailingWhitespaceRule(),
TypeNameRule(),
VariableNameRule(),
Expand Down
10 changes: 3 additions & 7 deletions Source/SwiftLintFramework/Models/Linter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,11 @@ public struct Linter {
public var styleViolations: [StyleViolation] {
let regions = file.regions()
return rules.flatMap { rule in
return rule.validateFile(self.file).filter { styleViolation in
guard let violationRegion = regions.filter({
$0.start <= styleViolation.location && $0.end >= styleViolation.location
}).first else {
return rule.validateFile(self.file).filter { violation in
guard let violationRegion = regions.filter({ $0.contains(violation) }).first else {
return true
}
return !violationRegion.disabledRuleIdentifiers.contains(
rule.dynamicType.description.identifier
)
return violationRegion.isRuleEnabled(rule)
}
}
}
Expand Down
12 changes: 12 additions & 0 deletions Source/SwiftLintFramework/Models/Region.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,16 @@ public struct Region {
self.end = end
self.disabledRuleIdentifiers = disabledRuleIdentifiers
}

public func contains(violation: StyleViolation) -> Bool {
return start <= violation.location && end >= violation.location
}

public func isRuleEnabled(rule: Rule) -> Bool {
return !isRuleDisabled(rule)
}

public func isRuleDisabled(rule: Rule) -> Bool {
return disabledRuleIdentifiers.contains(rule.dynamicType.description.identifier)
}
}
32 changes: 32 additions & 0 deletions Source/SwiftLintFramework/Rules/TrailingSemicolonRule.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// TrailingSemiColonRule.swift
// SwiftLint
//
// Created by JP Simard on 2015-11-17.
// Copyright (c) 2015 Realm. All rights reserved.
//

import SourceKittenFramework

public struct TrailingSemicolonRule: Rule {
public init() {}

public static let description = RuleDescription(
identifier: "trailing_semicolon",
name: "Trailing Semicolon",
description: "Lines should not have trailing semicolons.",
nonTriggeringExamples: [ "let a = 0\n" ],
triggeringExamples: [
"let a = 0;\n",
"let a = 0;\nlet b = 1\n"
]
)

public func validateFile(file: File) -> [StyleViolation] {
let excludingKinds = SyntaxKind.commentAndStringKinds()
return file.matchPattern(";$", excludingSyntaxKinds: excludingKinds).flatMap {
StyleViolation(ruleDescription: self.dynamicType.description,
location: Location(file: file, offset: $0.location))
}
}
}
4 changes: 4 additions & 0 deletions Source/SwiftLintFrameworkTests/StringRuleTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,8 @@ class StringRuleTests: XCTestCase {
func testTrailingNewline() {
verifyRule(TrailingNewlineRule.description, commentDoesntViolate: false)
}

func testTrailingSemicolon() {
verifyRule(TrailingSemicolonRule.description)
}
}
16 changes: 7 additions & 9 deletions Source/SwiftLintFrameworkTests/TestHelpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ private func violations(string: String, _ description: RuleDescription) -> [Styl
}

extension XCTestCase {
func verifyRule(ruleDescription: RuleDescription,
commentDoesntViolate: Bool = true) {
func verifyRule(ruleDescription: RuleDescription, commentDoesntViolate: Bool = true) {
XCTAssertEqual(
ruleDescription.nonTriggeringExamples.flatMap({violations($0, ruleDescription)}),
[]
Expand All @@ -29,15 +28,14 @@ extension XCTestCase {
ruleDescription.triggeringExamples.flatMap({
violations($0, ruleDescription).map({$0.ruleDescription})
}),
Array(count: ruleDescription.triggeringExamples.count, repeatedValue: ruleDescription))
Array(count: ruleDescription.triggeringExamples.count, repeatedValue: ruleDescription)
)

if commentDoesntViolate {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe should also test that, if a comment does violate, the violation still happens inside a comment?

XCTAssertEqual(
ruleDescription.triggeringExamples.flatMap({
violations("/** " + $0, ruleDescription)
}),
[]
)
let commentedViolations = ruleDescription.triggeringExamples.flatMap {
violations("/** " + $0, ruleDescription)
}
XCTAssertEqual(commentedViolations, [])
}

let command = "// swiftlint:disable \(ruleDescription.identifier)\n"
Expand Down
4 changes: 4 additions & 0 deletions SwiftLint.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
E876BFBE1B07828500114ED5 /* SourceKittenFramework.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E876BFBD1B07828500114ED5 /* SourceKittenFramework.framework */; };
E876BFBF1B0782AA00114ED5 /* SourceKittenFramework.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = E876BFBD1B07828500114ED5 /* SourceKittenFramework.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
E87E4A091BFB9CAE00FCFE46 /* SyntaxKind+SwiftLint.swift in Sources */ = {isa = PBXBuildFile; fileRef = E87E4A081BFB9CAE00FCFE46 /* SyntaxKind+SwiftLint.swift */; };
E87E4A051BFB927C00FCFE46 /* TrailingSemicolonRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = E87E4A041BFB927C00FCFE46 /* TrailingSemicolonRule.swift */; };
E88198421BEA929F00333A11 /* NestingRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = E88DEA951B099CF200A66CB0 /* NestingRule.swift */; };
E88198441BEA93D200333A11 /* ColonRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = E88DEA831B0990F500A66CB0 /* ColonRule.swift */; };
E88198521BEA941300333A11 /* TodoRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = E88DEA811B0990A700A66CB0 /* TodoRule.swift */; };
Expand Down Expand Up @@ -181,6 +182,7 @@
E868473B1A587C6E0043DC65 /* sourcekitd.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = sourcekitd.framework; path = Toolchains/XcodeDefault.xctoolchain/usr/lib/sourcekitd.framework; sourceTree = DEVELOPER_DIR; };
E876BFBD1B07828500114ED5 /* SourceKittenFramework.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = SourceKittenFramework.framework; sourceTree = BUILT_PRODUCTS_DIR; };
E87E4A081BFB9CAE00FCFE46 /* SyntaxKind+SwiftLint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SyntaxKind+SwiftLint.swift"; sourceTree = "<group>"; };
E87E4A041BFB927C00FCFE46 /* TrailingSemicolonRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrailingSemicolonRule.swift; sourceTree = "<group>"; };
E88DEA6A1B0983FE00A66CB0 /* StyleViolation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StyleViolation.swift; sourceTree = "<group>"; };
E88DEA6E1B09843F00A66CB0 /* Location.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Location.swift; sourceTree = "<group>"; };
E88DEA701B09847500A66CB0 /* ViolationSeverity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViolationSeverity.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -437,6 +439,7 @@
692B60AB1BD8F2E700C7AA22 /* StatementPositionRule.swift */,
E88DEA811B0990A700A66CB0 /* TodoRule.swift */,
E88DEA871B09924C00A66CB0 /* TrailingNewlineRule.swift */,
E87E4A041BFB927C00FCFE46 /* TrailingSemicolonRule.swift */,
E88DEA851B0991BF00A66CB0 /* TrailingWhitespaceRule.swift */,
E88DEA8D1B0999CD00A66CB0 /* TypeBodyLengthRule.swift */,
E88DEA911B099B1F00A66CB0 /* TypeNameRule.swift */,
Expand Down Expand Up @@ -678,6 +681,7 @@
E88DEA6F1B09843F00A66CB0 /* Location.swift in Sources */,
E88DEA771B098D0C00A66CB0 /* Rule.swift in Sources */,
CAF900141BED8B17006A371D /* VariableNameMaxLengthRule.swift in Sources */,
E87E4A051BFB927C00FCFE46 /* TrailingSemicolonRule.swift in Sources */,
E88198421BEA929F00333A11 /* NestingRule.swift in Sources */,
E881985B1BEA974E00333A11 /* StatementPositionRule.swift in Sources */,
E88DEA711B09847500A66CB0 /* ViolationSeverity.swift in Sources */,
Expand Down