mirror of
https://github.com/realm/SwiftLint.git
synced 2026-06-06 20:18:40 +00:00
Merge pull request #958 from marcelofabri/empty-parentheses-closure
Add empty_parentheses_with_trailing_closure rule
This commit is contained in:
@@ -31,6 +31,11 @@
|
||||
with redundant value assignments.
|
||||
[Marcelo Fabri](https://github.com/marcelofabri)
|
||||
[#946](https://github.com/realm/SwiftLint/issues/946)
|
||||
|
||||
* Add `empty_parentheses_with_trailing_closure` rule that checks for
|
||||
empty parentheses after method call when using trailing closures.
|
||||
[Marcelo Fabri](https://github.com/marcelofabri)
|
||||
[#885](https://github.com/realm/SwiftLint/issues/885)
|
||||
|
||||
##### Bug Fixes
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ public let masterRuleList = RuleList(rules:
|
||||
CustomRules.self,
|
||||
CyclomaticComplexityRule.self,
|
||||
EmptyCountRule.self,
|
||||
EmptyParenthesesWithTrailingClosureRule.self,
|
||||
ExplicitInitRule.self,
|
||||
FileHeaderRule.self,
|
||||
FileLengthRule.self,
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
//
|
||||
// EmptyParenthesesWithTrailingClosureRule.swift
|
||||
// SwiftLint
|
||||
//
|
||||
// Created by Marcelo Fabri on 11/12/16.
|
||||
// Copyright © 2016 Realm. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SourceKittenFramework
|
||||
|
||||
public struct EmptyParenthesesWithTrailingClosureRule: ASTRule, ConfigurationProviderRule {
|
||||
public var configuration = SeverityConfiguration(.warning)
|
||||
|
||||
public init() {}
|
||||
|
||||
public static let description = RuleDescription(
|
||||
identifier: "empty_parentheses_with_trailing_closure",
|
||||
name: "Empty Parentheses with Trailing Closure",
|
||||
description: "When using trailing closures, empty parentheses should be avoided " +
|
||||
"after the method call.",
|
||||
nonTriggeringExamples: [
|
||||
"[1, 2].map { $0 + 1 }\n",
|
||||
"[1, 2].map({ $0 + 1 })\n",
|
||||
"[1, 2].reduce(0) { $0 + $1 }",
|
||||
"[1, 2].map { number in\n number + 1 \n}\n",
|
||||
"let isEmpty = [1, 2].map.isEmpty()\n"
|
||||
],
|
||||
triggeringExamples: [
|
||||
"[1, 2].map↓() { $0 + 1 }",
|
||||
"[1, 2].map↓( ) { $0 + 1 }\n",
|
||||
"[1, 2].map↓() { number in\n number + 1 \n}\n",
|
||||
"[1, 2].map↓( ) { number in\n number + 1 \n}\n"
|
||||
]
|
||||
)
|
||||
|
||||
public enum Kind: String {
|
||||
case exprCall = "source.lang.swift.expr.call"
|
||||
case other
|
||||
public init?(rawValue: String) {
|
||||
switch rawValue {
|
||||
case Kind.exprCall.rawValue:
|
||||
self = .exprCall
|
||||
default:
|
||||
self = .other
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static let emptyParenthesesRegex = regex("^\\s*\\(\\s*\\)")
|
||||
|
||||
public func validateFile(_ file: File,
|
||||
kind: Kind,
|
||||
dictionary: [String: SourceKitRepresentable]) -> [StyleViolation] {
|
||||
guard kind == .exprCall else {
|
||||
return []
|
||||
}
|
||||
|
||||
guard let offset = (dictionary["key.offset"] as? Int64).flatMap({ Int($0) }),
|
||||
let length = (dictionary["key.length"] as? Int64).flatMap({ Int($0) }),
|
||||
let nameOffset = (dictionary["key.nameoffset"] as? Int64).flatMap({ Int($0) }),
|
||||
let nameLength = (dictionary["key.namelength"] as? Int64).flatMap({ Int($0) }),
|
||||
let bodyLength = (dictionary["key.bodylength"] as? Int64).flatMap({ Int($0) }),
|
||||
bodyLength > 0 else {
|
||||
return []
|
||||
}
|
||||
|
||||
let rangeStart = nameOffset + nameLength
|
||||
let rangeLength = (offset + length) - (nameOffset + nameLength)
|
||||
let regex = EmptyParenthesesWithTrailingClosureRule.emptyParenthesesRegex
|
||||
|
||||
guard let range = file.contents.byteRangeToNSRange(start: rangeStart, length: rangeLength),
|
||||
let match = regex.firstMatch(in: file.contents, options: [], range: range),
|
||||
match.range.location != NSNotFound else {
|
||||
return []
|
||||
}
|
||||
|
||||
return [
|
||||
StyleViolation(ruleDescription: type(of: self).description,
|
||||
severity: configuration.severity,
|
||||
location: Location(file: file, byteOffset: rangeStart))
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -81,6 +81,7 @@
|
||||
D44AD2761C0AA5350048F7B0 /* LegacyConstructorRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D44AD2741C0AA3730048F7B0 /* LegacyConstructorRule.swift */; };
|
||||
D46252541DF63FB200BE2CA1 /* NumberSeparatorRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D46252531DF63FB200BE2CA1 /* NumberSeparatorRule.swift */; };
|
||||
D46E041D1DE3712C00728374 /* TrailingCommaRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D46E041C1DE3712C00728374 /* TrailingCommaRule.swift */; };
|
||||
D47079A71DFCEB2D00027086 /* EmptyParenthesesWithTrailingClosureRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47079A61DFCEB2D00027086 /* EmptyParenthesesWithTrailingClosureRule.swift */; };
|
||||
D47A510E1DB29EEB00A4CC21 /* SwitchCaseOnNewlineRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47A510D1DB29EEB00A4CC21 /* SwitchCaseOnNewlineRule.swift */; };
|
||||
D4998DE91DF194F20006E05D /* FileHeaderRuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4998DE81DF194F20006E05D /* FileHeaderRuleTests.swift */; };
|
||||
D4C4A34E1DEA877200E0E04C /* FileHeaderRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4C4A34D1DEA877200E0E04C /* FileHeaderRule.swift */; };
|
||||
@@ -289,6 +290,7 @@
|
||||
D44AD2741C0AA3730048F7B0 /* LegacyConstructorRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyConstructorRule.swift; sourceTree = "<group>"; };
|
||||
D46252531DF63FB200BE2CA1 /* NumberSeparatorRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NumberSeparatorRule.swift; sourceTree = "<group>"; };
|
||||
D46E041C1DE3712C00728374 /* TrailingCommaRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrailingCommaRule.swift; sourceTree = "<group>"; };
|
||||
D47079A61DFCEB2D00027086 /* EmptyParenthesesWithTrailingClosureRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmptyParenthesesWithTrailingClosureRule.swift; sourceTree = "<group>"; };
|
||||
D47A510D1DB29EEB00A4CC21 /* SwitchCaseOnNewlineRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwitchCaseOnNewlineRule.swift; sourceTree = "<group>"; };
|
||||
D4998DE81DF194F20006E05D /* FileHeaderRuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileHeaderRuleTests.swift; sourceTree = "<group>"; };
|
||||
D4C4A34D1DEA877200E0E04C /* FileHeaderRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileHeaderRule.swift; sourceTree = "<group>"; };
|
||||
@@ -649,6 +651,7 @@
|
||||
3B1DF0111C5148140011BCED /* CustomRules.swift */,
|
||||
2E02005E1C54BF680024D09D /* CyclomaticComplexityRule.swift */,
|
||||
E847F0A81BFBBABD00EA9363 /* EmptyCountRule.swift */,
|
||||
D47079A61DFCEB2D00027086 /* EmptyParenthesesWithTrailingClosureRule.swift */,
|
||||
7C0C2E791D2866CB0076435A /* ExplicitInitRule.swift */,
|
||||
D4C4A34D1DEA877200E0E04C /* FileHeaderRule.swift */,
|
||||
E88DEA891B0992B300A66CB0 /* FileLengthRule.swift */,
|
||||
@@ -955,6 +958,7 @@
|
||||
6CC4259B1C77046200AEA885 /* SyntaxMap+SwiftLint.swift in Sources */,
|
||||
E881985C1BEA978500333A11 /* TrailingNewlineRule.swift in Sources */,
|
||||
78F032481D7D614300BE709A /* OverridenSuperCallConfiguration.swift in Sources */,
|
||||
D47079A71DFCEB2D00027086 /* EmptyParenthesesWithTrailingClosureRule.swift in Sources */,
|
||||
E881985E1BEA982100333A11 /* TypeBodyLengthRule.swift in Sources */,
|
||||
69F88BF71BDA38A6005E7CAE /* OpeningBraceRule.swift in Sources */,
|
||||
78F032461D7C877E00BE709A /* OverriddenSuperCallRule.swift in Sources */,
|
||||
|
||||
@@ -113,6 +113,10 @@ class RulesTests: XCTestCase {
|
||||
verifyRule(EmptyCountRule.description)
|
||||
}
|
||||
|
||||
func testEmptyParenthesesWithTrailingClosure() {
|
||||
verifyRule(EmptyParenthesesWithTrailingClosureRule.description)
|
||||
}
|
||||
|
||||
func testExplicitInit() {
|
||||
verifyRule(ExplicitInitRule.description)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user