Compare commits
15 Commits
main
...
jp-swift-4
Author | SHA1 | Date |
---|---|---|
![]() |
ad90413304 | |
![]() |
0f9a0a3f52 | |
![]() |
f53383f88f | |
![]() |
0b8bb4cc3a | |
![]() |
3058fa3353 | |
![]() |
c5279f1891 | |
![]() |
5d56f2f96e | |
![]() |
70c30f181c | |
![]() |
892cd76bb0 | |
![]() |
d5380d5690 | |
![]() |
cf44bcf5c6 | |
![]() |
508723c595 | |
![]() |
d2e5b4b576 | |
![]() |
6d26dd2a05 | |
![]() |
aaed908a8b |
|
@ -15,7 +15,7 @@
|
||||||
url = https://github.com/jpsim/SourceKitten.git
|
url = https://github.com/jpsim/SourceKitten.git
|
||||||
[submodule "Carthage/Checkouts/SwiftyTextTable"]
|
[submodule "Carthage/Checkouts/SwiftyTextTable"]
|
||||||
path = Carthage/Checkouts/SwiftyTextTable
|
path = Carthage/Checkouts/SwiftyTextTable
|
||||||
url = https://github.com/scottrhoyt/SwiftyTextTable.git
|
url = https://github.com/jpsim/SwiftyTextTable.git
|
||||||
[submodule "Carthage/Checkouts/Yams"]
|
[submodule "Carthage/Checkouts/Yams"]
|
||||||
path = Carthage/Checkouts/Yams
|
path = Carthage/Checkouts/Yams
|
||||||
url = https://github.com/jpsim/Yams.git
|
url = https://github.com/jpsim/Yams.git
|
||||||
|
|
4
Cartfile
4
Cartfile
|
@ -1,2 +1,2 @@
|
||||||
github "jpsim/SourceKitten" ~> 0.17
|
github "jpsim/SourceKitten" "jp-swift-4"
|
||||||
github "scottrhoyt/SwiftyTextTable" ~> 0.5.0
|
github "jpsim/SwiftyTextTable" "jp-swift-4"
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
github "Carthage/Commandant" "master"
|
github "Carthage/Commandant" "master"
|
||||||
github "jspahrsummers/xcconfigs" "master"
|
github "jspahrsummers/xcconfigs" "master"
|
||||||
github "jpsim/Yams" ~> 0.3
|
github "jpsim/Yams" "master"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
github "Carthage/Commandant" "16bcee12f48564c87bb13830f5320d21257e15b8"
|
github "Carthage/Commandant" "16bcee12f48564c87bb13830f5320d21257e15b8"
|
||||||
github "antitypical/Result" "3.2.2"
|
github "antitypical/Result" "3.2.3"
|
||||||
github "drmohundro/SWXMLHash" "3.1.0"
|
github "drmohundro/SWXMLHash" "4cb1b72d406b4309aed6e6b2f11f46aa83dacbf7"
|
||||||
github "jpsim/SourceKitten" "0.17.6"
|
github "jpsim/SourceKitten" "6e60ee7d6e9885ad6e674d55a5f540b0e52def09"
|
||||||
github "jpsim/Yams" "0.3.2"
|
github "jpsim/SwiftyTextTable" "15f72002eea8e3b446d104d18225b6a828218d32"
|
||||||
|
github "jpsim/Yams" "b53284ba4c8bdece9ec8aad2e0010602dac588e9"
|
||||||
github "jspahrsummers/xcconfigs" "2055f18efbe18e77408f7f43947f7ad92b2d4ff0"
|
github "jspahrsummers/xcconfigs" "2055f18efbe18e77408f7f43947f7ad92b2d4ff0"
|
||||||
github "scottrhoyt/SwiftyTextTable" "0.5.0"
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit f460dee9c9de710463c91a505c1b4a566de7621c
|
Subproject commit c8446185238659a2b27c0261f64ff1254291d07d
|
|
@ -1 +1 @@
|
||||||
Subproject commit 10b0b412a39542d19d002d6ff9f00d1a158332d9
|
Subproject commit 4cb1b72d406b4309aed6e6b2f11f46aa83dacbf7
|
|
@ -1 +1 @@
|
||||||
Subproject commit 91d9d5b20876da2afcbf34664bccc0bfd26088c7
|
Subproject commit 6e60ee7d6e9885ad6e674d55a5f540b0e52def09
|
|
@ -1 +1 @@
|
||||||
Subproject commit 13489587b932674addd93fdfca8e627a76e6a6db
|
Subproject commit 15f72002eea8e3b446d104d18225b6a828218d32
|
|
@ -1 +1 @@
|
||||||
Subproject commit acf403cb0d950a2d58ea8c63ce001deac2fde222
|
Subproject commit b53284ba4c8bdece9ec8aad2e0010602dac588e9
|
3
Makefile
3
Makefile
|
@ -91,6 +91,9 @@ release: package archive portable_zip
|
||||||
docker_test:
|
docker_test:
|
||||||
docker run -v `pwd`:`pwd` -w `pwd` --name swiftlint --rm norionomura/sourcekit:311 swift test
|
docker run -v `pwd`:`pwd` -w `pwd` --name swiftlint --rm norionomura/sourcekit:311 swift test
|
||||||
|
|
||||||
|
docker_test_4:
|
||||||
|
docker run -v `pwd`:`pwd` -w `pwd` --name swiftlint --rm norionomura/swift:4020170524a swift test
|
||||||
|
|
||||||
docker_htop:
|
docker_htop:
|
||||||
docker run -it --rm --pid=container:swiftlint terencewestphal/htop || reset
|
docker run -it --rm --pid=container:swiftlint terencewestphal/htop || reset
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
{
|
||||||
|
"object": {
|
||||||
|
"pins": [
|
||||||
|
{
|
||||||
|
"package": "Clang_C",
|
||||||
|
"repositoryURL": "https://github.com/norio-nomura/Clang_C.git",
|
||||||
|
"state": {
|
||||||
|
"branch": null,
|
||||||
|
"revision": "90a9574276f0fd17f02f58979423c3fd4d73b59e",
|
||||||
|
"version": "1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"package": "Commandant",
|
||||||
|
"repositoryURL": "https://github.com/Carthage/Commandant.git",
|
||||||
|
"state": {
|
||||||
|
"branch": null,
|
||||||
|
"revision": "c281992c31c3f41c48b5036c5a38185eaec32626",
|
||||||
|
"version": "0.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"package": "Result",
|
||||||
|
"repositoryURL": "https://github.com/antitypical/Result.git",
|
||||||
|
"state": {
|
||||||
|
"branch": null,
|
||||||
|
"revision": "c8446185238659a2b27c0261f64ff1254291d07d",
|
||||||
|
"version": "3.2.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"package": "SourceKit",
|
||||||
|
"repositoryURL": "https://github.com/norio-nomura/SourceKit.git",
|
||||||
|
"state": {
|
||||||
|
"branch": null,
|
||||||
|
"revision": "18eaa67ca44443bbe39646916792b9f0c98dbaa1",
|
||||||
|
"version": "1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"package": "SourceKitten",
|
||||||
|
"repositoryURL": "https://github.com/jpsim/SourceKitten.git",
|
||||||
|
"state": {
|
||||||
|
"branch": "jp-swift-4",
|
||||||
|
"revision": "6632fb43284436ab77c709fb4c8c5e55de42f6f5",
|
||||||
|
"version": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"package": "SwiftyTextTable",
|
||||||
|
"repositoryURL": "https://github.com/jpsim/SwiftyTextTable.git",
|
||||||
|
"state": {
|
||||||
|
"branch": "jp-swift-4",
|
||||||
|
"revision": "15f72002eea8e3b446d104d18225b6a828218d32",
|
||||||
|
"version": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"package": "SWXMLHash",
|
||||||
|
"repositoryURL": "https://github.com/drmohundro/SWXMLHash.git",
|
||||||
|
"state": {
|
||||||
|
"branch": "version-4.0-changes",
|
||||||
|
"revision": "4cb1b72d406b4309aed6e6b2f11f46aa83dacbf7",
|
||||||
|
"version": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"package": "Yams",
|
||||||
|
"repositoryURL": "https://github.com/jpsim/Yams.git",
|
||||||
|
"state": {
|
||||||
|
"branch": "master",
|
||||||
|
"revision": "b53284ba4c8bdece9ec8aad2e0010602dac588e9",
|
||||||
|
"version": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"version": 1
|
||||||
|
}
|
|
@ -1,17 +1,39 @@
|
||||||
|
// swift-tools-version:4.0
|
||||||
import PackageDescription
|
import PackageDescription
|
||||||
|
|
||||||
let package = Package(
|
let package = Package(
|
||||||
name: "SwiftLint",
|
name: "SwiftLint",
|
||||||
targets: [
|
products: [
|
||||||
Target(name: "SwiftLintFramework"),
|
.executable(name: "swiftlint", targets: ["swiftlint"]),
|
||||||
Target(name: "swiftlint",
|
.library(name: "SwiftLintFramework", targets: ["SwiftLintFramework"])
|
||||||
dependencies: [
|
|
||||||
.Target(name: "SwiftLintFramework")
|
|
||||||
]),
|
|
||||||
],
|
],
|
||||||
dependencies: [
|
dependencies: [
|
||||||
.Package(url: "https://github.com/jpsim/SourceKitten.git", majorVersion: 0, minor: 17),
|
.package(url: "https://github.com/Carthage/Commandant.git", from: "0.12.0"),
|
||||||
.Package(url: "https://github.com/jpsim/Yams.git", majorVersion: 0, minor: 3),
|
.package(url: "https://github.com/jpsim/Yams.git", .branch("master")),
|
||||||
.Package(url: "https://github.com/scottrhoyt/SwiftyTextTable.git", majorVersion: 0, minor: 5),
|
.package(url: "https://github.com/jpsim/SourceKitten.git", .branch("jp-swift-4")),
|
||||||
]
|
.package(url: "https://github.com/jpsim/SwiftyTextTable.git", .branch("jp-swift-4")),
|
||||||
|
],
|
||||||
|
targets: [
|
||||||
|
.target(
|
||||||
|
name: "swiftlint",
|
||||||
|
dependencies: [
|
||||||
|
"Commandant",
|
||||||
|
"SwiftLintFramework",
|
||||||
|
"SwiftyTextTable"
|
||||||
|
]
|
||||||
|
),
|
||||||
|
.target(
|
||||||
|
name: "SwiftLintFramework",
|
||||||
|
dependencies: [
|
||||||
|
"SourceKittenFramework"
|
||||||
|
]
|
||||||
|
),
|
||||||
|
.testTarget(
|
||||||
|
name: "SwiftLintFrameworkTests",
|
||||||
|
dependencies: [
|
||||||
|
"SwiftLintFramework"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
],
|
||||||
|
swiftLanguageVersions: [3, 4]
|
||||||
)
|
)
|
||||||
|
|
|
@ -35,7 +35,7 @@ extension Array {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func group<U: Hashable>(by transform: (Element) -> U) -> [U: [Element]] {
|
func group<U>(by transform: (Element) -> U) -> [U: [Element]] {
|
||||||
return reduce([:]) { dictionary, element in
|
return reduce([:]) { dictionary, element in
|
||||||
var dictionary = dictionary
|
var dictionary = dictionary
|
||||||
let key = transform(element)
|
let key = transform(element)
|
||||||
|
@ -52,11 +52,33 @@ extension Array {
|
||||||
}
|
}
|
||||||
|
|
||||||
func parallelFlatMap<T>(transform: @escaping ((Element) -> [T])) -> [T] {
|
func parallelFlatMap<T>(transform: @escaping ((Element) -> [T])) -> [T] {
|
||||||
return parallelMap(transform: transform).flatMap { $0 }
|
var result = [(Int, [T])]()
|
||||||
|
result.reserveCapacity(count)
|
||||||
|
|
||||||
|
let queueLabelPrefix = "io.realm.SwiftLintFramework.map.\(NSUUID().uuidString)"
|
||||||
|
let resultAccumulatorQueue = DispatchQueue(label: "\(queueLabelPrefix).resultAccumulator")
|
||||||
|
DispatchQueue.concurrentPerform(iterations: count) { index in
|
||||||
|
let jobIndexAndResults = (index, transform(self[index]))
|
||||||
|
resultAccumulatorQueue.sync {
|
||||||
|
result.append(jobIndexAndResults)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.sorted { $0.0 < $1.0 }.flatMap { $0.1 }
|
||||||
}
|
}
|
||||||
|
|
||||||
func parallelFlatMap<T>(transform: @escaping ((Element) -> T?)) -> [T] {
|
func parallelFlatMap<T>(transform: @escaping ((Element) -> T?)) -> [T] {
|
||||||
return parallelMap(transform: transform).flatMap { $0 }
|
var result = [(Int, T?)]()
|
||||||
|
result.reserveCapacity(count)
|
||||||
|
|
||||||
|
let queueLabelPrefix = "io.realm.SwiftLintFramework.map.\(NSUUID().uuidString)"
|
||||||
|
let resultAccumulatorQueue = DispatchQueue(label: "\(queueLabelPrefix).resultAccumulator")
|
||||||
|
DispatchQueue.concurrentPerform(iterations: count) { index in
|
||||||
|
let jobIndexAndResults = (index, transform(self[index]))
|
||||||
|
resultAccumulatorQueue.sync {
|
||||||
|
result.append(jobIndexAndResults)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.sorted { $0.0 < $1.0 }.flatMap { $0.1 }
|
||||||
}
|
}
|
||||||
|
|
||||||
func parallelMap<T>(transform: @escaping ((Element) -> T)) -> [T] {
|
func parallelMap<T>(transform: @escaping ((Element) -> T)) -> [T] {
|
||||||
|
|
|
@ -100,8 +100,9 @@ extension File {
|
||||||
|
|
||||||
internal func matchesAndSyntaxKinds(matching pattern: String,
|
internal func matchesAndSyntaxKinds(matching pattern: String,
|
||||||
range: NSRange? = nil) -> [(NSTextCheckingResult, [SyntaxKind])] {
|
range: NSRange? = nil) -> [(NSTextCheckingResult, [SyntaxKind])] {
|
||||||
return matchesAndTokens(matching: pattern, range: range).map { textCheckingResult, tokens in
|
return matchesAndTokens(matching: pattern, range: range).map { textCheckingResultAndTokens in
|
||||||
(textCheckingResult, tokens.flatMap { SyntaxKind(rawValue: $0.type) })
|
let (textCheckingResult, tokens) = textCheckingResultAndTokens
|
||||||
|
return (textCheckingResult, tokens.flatMap { SyntaxKind(rawValue: $0.type) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,8 +112,9 @@ extension File {
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func match(pattern: String, range: NSRange? = nil) -> [(NSRange, [SyntaxKind])] {
|
internal func match(pattern: String, range: NSRange? = nil) -> [(NSRange, [SyntaxKind])] {
|
||||||
return matchesAndSyntaxKinds(matching: pattern, range: range).map { textCheckingResult, syntaxKinds in
|
return matchesAndSyntaxKinds(matching: pattern, range: range).map { textCheckingResultAndKinds in
|
||||||
(textCheckingResult.range, syntaxKinds)
|
let (textCheckingResult, syntaxKinds) = textCheckingResultAndKinds
|
||||||
|
return (textCheckingResult.range, syntaxKinds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,8 +281,10 @@ extension File {
|
||||||
internal func correct<R: Rule>(legacyRule: R, patterns: [String: String]) -> [Correction] {
|
internal func correct<R: Rule>(legacyRule: R, patterns: [String: String]) -> [Correction] {
|
||||||
typealias RangePatternTemplate = (NSRange, String, String)
|
typealias RangePatternTemplate = (NSRange, String, String)
|
||||||
let matches: [RangePatternTemplate]
|
let matches: [RangePatternTemplate]
|
||||||
matches = patterns.flatMap({ pattern, template -> [RangePatternTemplate] in
|
matches = patterns.flatMap({ arg -> [RangePatternTemplate] in
|
||||||
return match(pattern: pattern).filter { range, kinds in
|
let (pattern, template) = arg
|
||||||
|
return match(pattern: pattern).filter { arg -> Bool in
|
||||||
|
let (range, kinds) = arg
|
||||||
return kinds.first == .identifier &&
|
return kinds.first == .identifier &&
|
||||||
!ruleEnabled(violatingRanges: [range], for: legacyRule).isEmpty
|
!ruleEnabled(violatingRanges: [range], for: legacyRule).isEmpty
|
||||||
}.map { ($0.0, pattern, template) }
|
}.map { ($0.0, pattern, template) }
|
||||||
|
|
|
@ -12,8 +12,10 @@ import Foundation
|
||||||
#if !swift(>=3.1)
|
#if !swift(>=3.1)
|
||||||
public typealias NSRegularExpression = RegularExpression
|
public typealias NSRegularExpression = RegularExpression
|
||||||
#endif
|
#endif
|
||||||
|
#if !swift(>=4.0)
|
||||||
public typealias NSTextCheckingResult = TextCheckingResult
|
public typealias NSTextCheckingResult = TextCheckingResult
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
private var regexCache = [RegexCacheKey: NSRegularExpression]()
|
private var regexCache = [RegexCacheKey: NSRegularExpression]()
|
||||||
private let regexCacheLock = NSLock()
|
private let regexCacheLock = NSLock()
|
||||||
|
|
|
@ -294,12 +294,14 @@ private func warnAboutDeprecations(configurationDictionary dict: [String: Any],
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecation warning for rules
|
// Deprecation warning for rules
|
||||||
let deprecatedRulesIdentifiers = ruleList.list.flatMap { (identifier, rule) -> [(String, String)] in
|
let deprecatedRulesIdentifiers = ruleList.list.flatMap { arg -> [(String, String)] in
|
||||||
|
let (identifier, rule) = arg
|
||||||
return rule.description.deprecatedAliases.map { ($0, identifier) }
|
return rule.description.deprecatedAliases.map { ($0, identifier) }
|
||||||
}
|
}
|
||||||
|
|
||||||
let userProvidedRuleIDs = Set(disabledRules + optInRules + whitelistRules)
|
let userProvidedRuleIDs = Set(disabledRules + optInRules + whitelistRules)
|
||||||
let deprecatedUsages = deprecatedRulesIdentifiers.filter { deprecatedIdentifier, _ in
|
let deprecatedUsages = deprecatedRulesIdentifiers.filter { arg -> Bool in
|
||||||
|
let (deprecatedIdentifier, _) = arg
|
||||||
return dict[deprecatedIdentifier] != nil || userProvidedRuleIDs.contains(deprecatedIdentifier)
|
return dict[deprecatedIdentifier] != nil || userProvidedRuleIDs.contains(deprecatedIdentifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,12 +36,14 @@ extension Rule {
|
||||||
|
|
||||||
let (disabledViolationsAndRegions, enabledViolationsAndRegions) = violations.map { violation in
|
let (disabledViolationsAndRegions, enabledViolationsAndRegions) = violations.map { violation in
|
||||||
return (violation, regions.first(where: { $0.contains(violation.location) }))
|
return (violation, regions.first(where: { $0.contains(violation.location) }))
|
||||||
}.partitioned { _, region in
|
}.partitioned { arg -> Bool in
|
||||||
return region?.isRuleEnabled(self) ?? true
|
let (_, region) = arg
|
||||||
|
return region?.isRuleEnabled(self) ?? true
|
||||||
}
|
}
|
||||||
|
|
||||||
let enabledViolations = enabledViolationsAndRegions.map { $0.0 }
|
let enabledViolations = enabledViolationsAndRegions.map { $0.0 }
|
||||||
let deprecatedToValidIDPairs = disabledViolationsAndRegions.flatMap { _, region -> [(String, String)] in
|
let deprecatedToValidIDPairs = disabledViolationsAndRegions.flatMap { arg -> [(String, String)] in
|
||||||
|
let (_, region) = arg
|
||||||
let identifiers = region?.deprecatedAliasesDisabling(rule: self) ?? []
|
let identifiers = region?.deprecatedAliasesDisabling(rule: self) ?? []
|
||||||
return identifiers.map { ($0, type(of: self).description.identifier) }
|
return identifiers.map { ($0, type(of: self).description.identifier) }
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,8 +67,9 @@ public struct RuleList {
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func allValidIdentifiers() -> [String] {
|
internal func allValidIdentifiers() -> [String] {
|
||||||
return list.flatMap { (_, rule) -> [String] in
|
return list.flatMap { arg -> [String] in
|
||||||
rule.description.allIdentifiers
|
let (_, rule) = arg
|
||||||
|
return rule.description.allIdentifiers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,6 @@ public struct RuleParameter<T: Equatable>: Equatable {
|
||||||
|
|
||||||
// MARK: - Equatable
|
// MARK: - Equatable
|
||||||
|
|
||||||
public func ==<T: Equatable>(lhs: RuleParameter<T>, rhs: RuleParameter<T>) -> Bool {
|
public func ==<T> (lhs: RuleParameter<T>, rhs: RuleParameter<T>) -> Bool {
|
||||||
return lhs.value == rhs.value && lhs.severity == rhs.severity
|
return lhs.value == rhs.value && lhs.severity == rhs.severity
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ public extension ConfigurationProviderRule {
|
||||||
|
|
||||||
public func == (lhs: [Rule], rhs: [Rule]) -> Bool {
|
public func == (lhs: [Rule], rhs: [Rule]) -> Bool {
|
||||||
if lhs.count == rhs.count {
|
if lhs.count == rhs.count {
|
||||||
return zip(lhs, rhs).map { $0.isEqualTo($1) }.reduce(true) { $0 && $1 }
|
return zip(lhs, rhs).map { $0.0.isEqualTo($0.1) }.reduce(true) { $0 && $1 }
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -22,7 +22,7 @@ public struct CheckstyleReporter: Reporter {
|
||||||
violations
|
violations
|
||||||
.group(by: { ($0.location.file ?? "<nopath>").escapedForXML() })
|
.group(by: { ($0.location.file ?? "<nopath>").escapedForXML() })
|
||||||
.sorted(by: { $0.key < $1.key })
|
.sorted(by: { $0.key < $1.key })
|
||||||
.map(generateForViolationFile).joined(),
|
.map({ generateForViolationFile($0.0, violations: $0.1) }).joined(),
|
||||||
"\n</checkstyle>"
|
"\n</checkstyle>"
|
||||||
].joined()
|
].joined()
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,8 @@ public struct EmojiReporter: Reporter {
|
||||||
return violations
|
return violations
|
||||||
.group(by: { $0.location.file ?? "Other" })
|
.group(by: { $0.location.file ?? "Other" })
|
||||||
.sorted(by: { $0.key < $1.key })
|
.sorted(by: { $0.key < $1.key })
|
||||||
.map(report).joined(separator: "\n")
|
.map({ report(for: $0.0, with: $0.1) })
|
||||||
|
.joined(separator: "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func report(for file: String, with violations: [StyleViolation]) -> String {
|
private static func report(for file: String, with violations: [StyleViolation]) -> String {
|
||||||
|
|
|
@ -61,7 +61,8 @@ public struct AttributesRule: ASTRule, OptInRule, ConfigurationProviderRule {
|
||||||
|
|
||||||
private func validateTestableImport(file: File) -> [StyleViolation] {
|
private func validateTestableImport(file: File) -> [StyleViolation] {
|
||||||
let pattern = "@testable[\n]+\\s*import"
|
let pattern = "@testable[\n]+\\s*import"
|
||||||
return file.match(pattern: pattern).flatMap { range, kinds -> StyleViolation? in
|
return file.match(pattern: pattern).flatMap { arg -> StyleViolation? in
|
||||||
|
let (range, kinds) = arg
|
||||||
guard kinds == [.attributeBuiltin, .keyword] else {
|
guard kinds == [.attributeBuiltin, .keyword] else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -149,18 +150,20 @@ public struct AttributesRule: ASTRule, OptInRule, ConfigurationProviderRule {
|
||||||
attributesTokens: [(String, NSRange)],
|
attributesTokens: [(String, NSRange)],
|
||||||
line: Line, file: File) -> Set<String> {
|
line: Line, file: File) -> Set<String> {
|
||||||
let attributesTokensWithParameters: [(String, Bool)] = attributesTokens.map {
|
let attributesTokensWithParameters: [(String, Bool)] = attributesTokens.map {
|
||||||
let hasParameter = attributeContainsParameter(attributeRange: $1,
|
let hasParameter = attributeContainsParameter(attributeRange: $0.1,
|
||||||
line: line, file: file)
|
line: line, file: file)
|
||||||
return ($0, hasParameter)
|
return ($0.0, hasParameter)
|
||||||
}
|
}
|
||||||
let allAttributes = previousAttributes + attributesTokensWithParameters
|
let allAttributes = previousAttributes + attributesTokensWithParameters
|
||||||
|
|
||||||
return Set(allAttributes.flatMap { (token, hasParameter) -> String? in
|
return Set(allAttributes.flatMap { arg -> String? in
|
||||||
// an attribute should be on a new line if one of these is true:
|
// an attribute should be on a new line if one of these is true:
|
||||||
// 1. it's a parameterized attribute
|
// 1. it's a parameterized attribute
|
||||||
// a. the parameter is on the token (i.e. warn_unused_result)
|
// a. the parameter is on the token (i.e. warn_unused_result)
|
||||||
// b. the parameter was parsed in the `hasParameter` variable (most attributes)
|
// b. the parameter was parsed in the `hasParameter` variable (most attributes)
|
||||||
// 2. it's a whitelisted attribute, according to the current configuration
|
// 2. it's a whitelisted attribute, according to the current configuration
|
||||||
|
|
||||||
|
let (token, hasParameter) = arg
|
||||||
let isParameterized = hasParameter || token.bridge().contains("(")
|
let isParameterized = hasParameter || token.bridge().contains("(")
|
||||||
if isParameterized || configuration.alwaysOnNewLine.contains(token) {
|
if isParameterized || configuration.alwaysOnNewLine.contains(token) {
|
||||||
return token
|
return token
|
||||||
|
|
|
@ -195,14 +195,16 @@ extension ColonRule {
|
||||||
fileprivate func typeColonViolationRanges(in file: File, matching pattern: String) -> [NSRange] {
|
fileprivate func typeColonViolationRanges(in file: File, matching pattern: String) -> [NSRange] {
|
||||||
let nsstring = file.contents.bridge()
|
let nsstring = file.contents.bridge()
|
||||||
let commentAndStringKindsSet = Set(SyntaxKind.commentAndStringKinds())
|
let commentAndStringKindsSet = Set(SyntaxKind.commentAndStringKinds())
|
||||||
return file.rangesAndTokens(matching: pattern).filter { _, syntaxTokens in
|
return file.rangesAndTokens(matching: pattern).filter { arg in
|
||||||
|
let (_, syntaxTokens) = arg
|
||||||
let syntaxKinds = syntaxTokens.flatMap { SyntaxKind(rawValue: $0.type) }
|
let syntaxKinds = syntaxTokens.flatMap { SyntaxKind(rawValue: $0.type) }
|
||||||
if !syntaxKinds.starts(with: [.identifier, .typeidentifier]) {
|
if !syntaxKinds.starts(with: [.identifier, .typeidentifier]) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return Set(syntaxKinds).intersection(commentAndStringKindsSet).isEmpty
|
return Set(syntaxKinds).intersection(commentAndStringKindsSet).isEmpty
|
||||||
}.flatMap { range, syntaxTokens in
|
}.flatMap { arg in
|
||||||
let identifierRange = nsstring
|
let (range, syntaxTokens) = arg
|
||||||
|
let identifierRange = nsstring
|
||||||
.byteRangeToNSRange(start: syntaxTokens[0].offset, length: 0)
|
.byteRangeToNSRange(start: syntaxTokens[0].offset, length: 0)
|
||||||
return identifierRange.map { NSUnionRange($0, range) }
|
return identifierRange.map { NSUnionRange($0, range) }
|
||||||
}
|
}
|
||||||
|
@ -281,10 +283,11 @@ extension ColonRule {
|
||||||
return NSRange(location: offset, length: length)
|
return NSRange(location: offset, length: length)
|
||||||
}
|
}
|
||||||
|
|
||||||
let even = ranges.enumerated().flatMap { $0 % 2 == 0 ? $1 : nil }
|
let even = ranges.enumerated().flatMap { $0.0 % 2 == 0 ? $0.1 : nil }
|
||||||
let odd = ranges.enumerated().flatMap { $0 % 2 != 0 ? $1 : nil }
|
let odd = ranges.enumerated().flatMap { $0.0 % 2 != 0 ? $0.1 : nil }
|
||||||
|
|
||||||
return zip(even, odd).map { evenRange, oddRange -> NSRange in
|
return zip(even, odd).map { evenOdd -> NSRange in
|
||||||
|
let (evenRange, oddRange) = evenOdd
|
||||||
let location = NSMaxRange(evenRange)
|
let location = NSMaxRange(evenRange)
|
||||||
let length = oddRange.location - location
|
let length = oddRange.location - location
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,8 @@ public struct ConditionalReturnsOnNewlineRule: ConfigurationProviderRule, Rule,
|
||||||
|
|
||||||
public func validate(file: File) -> [StyleViolation] {
|
public func validate(file: File) -> [StyleViolation] {
|
||||||
let pattern = "(guard|if)[^\n]*return"
|
let pattern = "(guard|if)[^\n]*return"
|
||||||
return file.rangesAndTokens(matching: pattern).filter { _, tokens in
|
return file.rangesAndTokens(matching: pattern).filter { arg in
|
||||||
|
let (_, tokens) = arg
|
||||||
guard let firstToken = tokens.first, let lastToken = tokens.last,
|
guard let firstToken = tokens.first, let lastToken = tokens.last,
|
||||||
SyntaxKind(rawValue: firstToken.type) == .keyword &&
|
SyntaxKind(rawValue: firstToken.type) == .keyword &&
|
||||||
SyntaxKind(rawValue: lastToken.type) == .keyword else {
|
SyntaxKind(rawValue: lastToken.type) == .keyword else {
|
||||||
|
|
|
@ -61,7 +61,8 @@ public struct ControlStatementRule: ConfigurationProviderRule {
|
||||||
let pattern = statementKind == "guard"
|
let pattern = statementKind == "guard"
|
||||||
? "\(statementKind)\\s*\\([^,{]*\\)\\s*else\\s*\\{"
|
? "\(statementKind)\\s*\\([^,{]*\\)\\s*else\\s*\\{"
|
||||||
: "\(statementKind)\\s*\\([^,{]*\\)\\s*\\{"
|
: "\(statementKind)\\s*\\([^,{]*\\)\\s*\\{"
|
||||||
return file.match(pattern: pattern).flatMap { match, syntaxKinds in
|
return file.match(pattern: pattern).flatMap { arg in
|
||||||
|
let (match, syntaxKinds) = arg
|
||||||
let matchString = file.contents.substring(from: match.location, length: match.length)
|
let matchString = file.contents.substring(from: match.location, length: match.length)
|
||||||
if isFalsePositive(matchString, syntaxKind: syntaxKinds.first) {
|
if isFalsePositive(matchString, syntaxKind: syntaxKinds.first) {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -67,7 +67,8 @@ public struct EmptyEnumArgumentsRule: ASTRule, ConfigurationProviderRule, Correc
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
return file.match(pattern: "\\([,\\s_]*\\)", range: caseRange).flatMap { range, kinds in
|
return file.match(pattern: "\\([,\\s_]*\\)", range: caseRange).flatMap { arg in
|
||||||
|
let (range, kinds) = arg
|
||||||
guard Set(kinds).isSubset(of: [.keyword]),
|
guard Set(kinds).isSubset(of: [.keyword]),
|
||||||
case let byteRange = NSRange(location: offset, length: length),
|
case let byteRange = NSRange(location: offset, length: length),
|
||||||
Set(file.syntaxMap.kinds(inByteRange: byteRange)) != [.keyword] else {
|
Set(file.syntaxMap.kinds(inByteRange: byteRange)) != [.keyword] else {
|
||||||
|
|
|
@ -102,8 +102,14 @@ public struct FunctionParameterCountRule: ASTRule, ConfigurationProviderRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func defaultFunctionParameterCount(file: File, byteOffset: Int, byteLength: Int) -> Int {
|
fileprivate func defaultFunctionParameterCount(file: File, byteOffset: Int, byteLength: Int) -> Int {
|
||||||
return file.contents.bridge().substringWithByteRange(start: byteOffset, length: byteLength)?
|
guard let characters = file.contents.bridge().substringWithByteRange(start: byteOffset, length: byteLength)?
|
||||||
.characters.filter { $0 == "=" }.count ?? 0
|
.characters else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
let equals: [Character] = characters.filter { char -> Bool in
|
||||||
|
return char == "="
|
||||||
|
}
|
||||||
|
return equals.count
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func functionIsInitializer(file: File, byteOffset: Int, byteLength: Int) -> Bool {
|
fileprivate func functionIsInitializer(file: File, byteOffset: Int, byteLength: Int) -> Bool {
|
||||||
|
|
|
@ -77,7 +77,8 @@ public struct GenericTypeNameRule: ASTRule, ConfigurationProviderRule {
|
||||||
|
|
||||||
private func validateGenericTypeAliases(in file: File) -> [StyleViolation] {
|
private func validateGenericTypeAliases(in file: File) -> [StyleViolation] {
|
||||||
let pattern = "typealias\\s+\\w+?\\s*" + genericTypePattern + "\\s*="
|
let pattern = "typealias\\s+\\w+?\\s*" + genericTypePattern + "\\s*="
|
||||||
return file.match(pattern: pattern).flatMap { (range, tokens) -> [(String, Int)] in
|
return file.match(pattern: pattern).flatMap { arg -> [(String, Int)] in
|
||||||
|
let (range, tokens) = arg
|
||||||
guard tokens.first == .keyword,
|
guard tokens.first == .keyword,
|
||||||
Set(tokens.dropFirst()) == [.identifier],
|
Set(tokens.dropFirst()) == [.identifier],
|
||||||
let match = genericTypeRegex.firstMatch(in: file.contents, options: [],
|
let match = genericTypeRegex.firstMatch(in: file.contents, options: [],
|
||||||
|
@ -139,16 +140,19 @@ public struct GenericTypeNameRule: ASTRule, ConfigurationProviderRule {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
let namesAndRanges: [(String, NSRange)] = beforeWhere.split(separator: ",").flatMap { string, range in
|
let namesAndRanges: [(String, NSRange)] = beforeWhere.split(separator: ",")
|
||||||
return string.split(separator: ":").first.map {
|
.flatMap { arg -> (String, NSRange)? in
|
||||||
let (trimmed, trimmedRange) = $0.0.trimmingWhitespaces()
|
let (string, range) = arg
|
||||||
return (trimmed, NSRange(location: range.location + trimmedRange.location,
|
return string.split(separator: ":").first.map {
|
||||||
length: trimmedRange.length))
|
let (trimmed, trimmedRange) = $0.0.trimmingWhitespaces()
|
||||||
|
return (trimmed, NSRange(location: range.location + trimmedRange.location,
|
||||||
|
length: trimmedRange.length))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let contents = file.contents.bridge()
|
let contents = file.contents.bridge()
|
||||||
return namesAndRanges.flatMap { (name, range) -> (String, Int)? in
|
return namesAndRanges.flatMap { arg -> (String, Int)? in
|
||||||
|
let (name, range) = arg
|
||||||
guard let byteRange = contents.NSRangeToByteRange(start: range.location + offset,
|
guard let byteRange = contents.NSRangeToByteRange(start: range.location + offset,
|
||||||
length: range.length),
|
length: range.length),
|
||||||
file.syntaxMap.kinds(inByteRange: byteRange) == [.identifier] else {
|
file.syntaxMap.kinds(inByteRange: byteRange) == [.identifier] else {
|
||||||
|
|
|
@ -37,13 +37,14 @@ public struct IdentifierNameRule: ASTRule, ConfigurationProviderRule {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
return validateName(dictionary: dictionary, kind: kind).map { name, offset in
|
return validateName(dictionary: dictionary, kind: kind).map { nameAndOffset in
|
||||||
|
let (name, offset) = nameAndOffset
|
||||||
guard !configuration.excluded.contains(name) else {
|
guard !configuration.excluded.contains(name) else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
let isFunction = SwiftDeclarationKind.functionKinds().contains(kind)
|
let isFunction = SwiftDeclarationKind.functionKinds().contains(kind)
|
||||||
let description = type(of: self).description
|
let description = Swift.type(of: self).description
|
||||||
|
|
||||||
let type = self.type(for: kind)
|
let type = self.type(for: kind)
|
||||||
if !isFunction {
|
if !isFunction {
|
||||||
|
@ -64,7 +65,7 @@ public struct IdentifierNameRule: ASTRule, ConfigurationProviderRule {
|
||||||
"\(configuration.minLengthThreshold) and " +
|
"\(configuration.minLengthThreshold) and " +
|
||||||
"\(configuration.maxLengthThreshold) characters long: '\(name)'"
|
"\(configuration.maxLengthThreshold) characters long: '\(name)'"
|
||||||
return [
|
return [
|
||||||
StyleViolation(ruleDescription: type(of: self).description,
|
StyleViolation(ruleDescription: Swift.type(of: self).description,
|
||||||
severity: severity,
|
severity: severity,
|
||||||
location: Location(file: file, byteOffset: offset),
|
location: Location(file: file, byteOffset: offset),
|
||||||
reason: reason)
|
reason: reason)
|
||||||
|
|
|
@ -54,7 +54,8 @@ public struct ImplicitGetterRule: ConfigurationProviderRule {
|
||||||
|
|
||||||
public func validate(file: File) -> [StyleViolation] {
|
public func validate(file: File) -> [StyleViolation] {
|
||||||
let pattern = "\\bget\\b"
|
let pattern = "\\bget\\b"
|
||||||
let getTokens: [SyntaxToken] = file.rangesAndTokens(matching: pattern).flatMap { _, tokens in
|
let getTokens: [SyntaxToken] = file.rangesAndTokens(matching: pattern).flatMap { arg in
|
||||||
|
let (_, tokens) = arg
|
||||||
guard tokens.count == 1, let token = tokens.first,
|
guard tokens.count == 1, let token = tokens.first,
|
||||||
SyntaxKind(rawValue: token.type) == .keyword else {
|
SyntaxKind(rawValue: token.type) == .keyword else {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -68,7 +68,8 @@ public struct ImplicitReturnRule: ConfigurationProviderRule, CorrectableRule, Op
|
||||||
let pattern = "(?:\\bin|\\{)\\s+(return\\s+)"
|
let pattern = "(?:\\bin|\\{)\\s+(return\\s+)"
|
||||||
let contents = file.contents.bridge()
|
let contents = file.contents.bridge()
|
||||||
|
|
||||||
return file.matchesAndSyntaxKinds(matching: pattern).flatMap { result, kinds in
|
return file.matchesAndSyntaxKinds(matching: pattern).flatMap { arg in
|
||||||
|
let (result, kinds) = arg
|
||||||
let range = result.range
|
let range = result.range
|
||||||
guard kinds == [.keyword, .keyword] || kinds == [.keyword],
|
guard kinds == [.keyword, .keyword] || kinds == [.keyword],
|
||||||
let byteRange = contents.NSRangeToByteRange(start: range.location,
|
let byteRange = contents.NSRangeToByteRange(start: range.location,
|
||||||
|
|
|
@ -64,7 +64,8 @@ public struct LargeTupleRule: ASTRule, ConfigurationProviderRule {
|
||||||
let offsets = violationOffsetsForTypes(in: file, dictionary: dictionary, kind: kind) +
|
let offsets = violationOffsetsForTypes(in: file, dictionary: dictionary, kind: kind) +
|
||||||
violationOffsetsForFunctions(in: file, dictionary: dictionary, kind: kind)
|
violationOffsetsForFunctions(in: file, dictionary: dictionary, kind: kind)
|
||||||
|
|
||||||
return offsets.flatMap { location, size in
|
return offsets.flatMap { arg in
|
||||||
|
let (location, size) = arg
|
||||||
for parameter in configuration.params where size > parameter.value {
|
for parameter in configuration.params where size > parameter.value {
|
||||||
let reason = "Tuples should have at most \(configuration.warning) members."
|
let reason = "Tuples should have at most \(configuration.warning) members."
|
||||||
return StyleViolation(ruleDescription: type(of: self).description,
|
return StyleViolation(ruleDescription: type(of: self).description,
|
||||||
|
|
|
@ -45,7 +45,8 @@ public struct LegacyConstantRule: CorrectableRule, ConfigurationProviderRule {
|
||||||
|
|
||||||
public func correct(file: File) -> [Correction] {
|
public func correct(file: File) -> [Correction] {
|
||||||
var wordBoundPatterns: [String: String] = [:]
|
var wordBoundPatterns: [String: String] = [:]
|
||||||
LegacyConstantRule.legacyPatterns.forEach { key, value in
|
LegacyConstantRule.legacyPatterns.forEach { arg in
|
||||||
|
let (key, value) = arg
|
||||||
wordBoundPatterns["\\b" + key] = value
|
wordBoundPatterns["\\b" + key] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -150,10 +150,12 @@ public struct MarkRule: CorrectableRule, ConfigurationProviderRule {
|
||||||
|
|
||||||
private func violationRanges(in file: File, matching pattern: String) -> [NSRange] {
|
private func violationRanges(in file: File, matching pattern: String) -> [NSRange] {
|
||||||
let nsstring = file.contents.bridge()
|
let nsstring = file.contents.bridge()
|
||||||
return file.rangesAndTokens(matching: pattern).filter { _, syntaxTokens in
|
return file.rangesAndTokens(matching: pattern).filter { arg -> Bool in
|
||||||
|
let (_, syntaxTokens) = arg
|
||||||
return !syntaxTokens.isEmpty && SyntaxKind(rawValue: syntaxTokens[0].type) == .comment
|
return !syntaxTokens.isEmpty && SyntaxKind(rawValue: syntaxTokens[0].type) == .comment
|
||||||
}.flatMap { range, syntaxTokens in
|
}.flatMap { arg in
|
||||||
let identifierRange = nsstring
|
let (range, syntaxTokens) = arg
|
||||||
|
let identifierRange = nsstring
|
||||||
.byteRangeToNSRange(start: syntaxTokens[0].offset, length: 0)
|
.byteRangeToNSRange(start: syntaxTokens[0].offset, length: 0)
|
||||||
return identifierRange.map { NSUnionRange($0, range) }
|
return identifierRange.map { NSUnionRange($0, range) }
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,14 +57,17 @@ public struct NestingRule: ASTRule, ConfigurationProviderRule {
|
||||||
reason: "\(targetName) should be nested at most \(threshold) level\(pluralSuffix) deep"))
|
reason: "\(targetName) should be nested at most \(threshold) level\(pluralSuffix) deep"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
violations.append(contentsOf: dictionary.substructure.flatMap { subDict in
|
violations.append(contentsOf: dictionary.substructure
|
||||||
if let kind = (subDict.kind).flatMap(SwiftDeclarationKind.init) {
|
.flatMap { subDict -> [(SwiftDeclarationKind, [String: SourceKitRepresentable])] in
|
||||||
return (kind, subDict)
|
if let kind = (subDict.kind).flatMap(SwiftDeclarationKind.init) {
|
||||||
|
return [(kind, subDict)]
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}.flatMap { kindAndSubdict -> [StyleViolation] in
|
||||||
|
let (kind, subDict) = kindAndSubdict
|
||||||
|
return validate(file: file, kind: kind, dictionary: subDict, level: level + 1)
|
||||||
}
|
}
|
||||||
return nil
|
)
|
||||||
}.flatMap { kind, subDict in
|
|
||||||
return validate(file: file, kind: kind, dictionary: subDict, level: level + 1)
|
|
||||||
})
|
|
||||||
return violations
|
return violations
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,8 +88,9 @@ public struct NimbleOperatorRule: ConfigurationProviderRule, OptInRule, Correcta
|
||||||
let excludingKinds = SyntaxKind.commentKinds()
|
let excludingKinds = SyntaxKind.commentKinds()
|
||||||
|
|
||||||
return file.match(pattern: pattern)
|
return file.match(pattern: pattern)
|
||||||
.filter { _, kinds in
|
.filter { arg -> Bool in
|
||||||
kinds.filter(excludingKinds.contains).isEmpty && kinds.first == .identifier
|
let (_, kinds) = arg
|
||||||
|
return kinds.filter(excludingKinds.contains).isEmpty && kinds.first == .identifier
|
||||||
}.map { $0.0 }
|
}.map { $0.0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,8 @@ public struct NumberSeparatorRule: OptInRule, CorrectableRule, ConfigurationProv
|
||||||
)
|
)
|
||||||
|
|
||||||
public func validate(file: File) -> [StyleViolation] {
|
public func validate(file: File) -> [StyleViolation] {
|
||||||
return violatingRanges(in: file).map { range, _ in
|
return violatingRanges(in: file).map { rangeAndThing in
|
||||||
|
let (range, _) = rangeAndThing
|
||||||
return StyleViolation(ruleDescription: type(of: self).description,
|
return StyleViolation(ruleDescription: type(of: self).description,
|
||||||
severity: configuration.severityConfiguration.severity,
|
severity: configuration.severityConfiguration.severity,
|
||||||
location: Location(file: file, characterOffset: range.location))
|
location: Location(file: file, characterOffset: range.location))
|
||||||
|
@ -86,7 +87,8 @@ public struct NumberSeparatorRule: OptInRule, CorrectableRule, ConfigurationProv
|
||||||
}
|
}
|
||||||
|
|
||||||
public func correct(file: File) -> [Correction] {
|
public func correct(file: File) -> [Correction] {
|
||||||
let violatingRanges = self.violatingRanges(in: file).filter { range, _ in
|
let violatingRanges = self.violatingRanges(in: file).filter { arg -> Bool in
|
||||||
|
let (range, _) = arg
|
||||||
return !file.ruleEnabled(violatingRanges: [range], for: self).isEmpty
|
return !file.ruleEnabled(violatingRanges: [range], for: self).isEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -101,7 +101,7 @@ public struct OpeningBraceRule: CorrectableRule, ConfigurationProviderRule {
|
||||||
guard let indexRange = contents.nsrangeToIndexRange(violatingRange) else {
|
guard let indexRange = contents.nsrangeToIndexRange(violatingRange) else {
|
||||||
return (contents, nil)
|
return (contents, nil)
|
||||||
}
|
}
|
||||||
let capturedString = contents[indexRange]
|
let capturedString = String(contents[indexRange])
|
||||||
var adjustedRange = violatingRange
|
var adjustedRange = violatingRange
|
||||||
var correctString = " {"
|
var correctString = " {"
|
||||||
|
|
||||||
|
|
|
@ -40,9 +40,11 @@ public struct OperatorFunctionWhitespaceRule: ConfigurationProviderRule {
|
||||||
let zeroOrManySpaces = "(\\s{0}|\\s{2,})"
|
let zeroOrManySpaces = "(\\s{0}|\\s{2,})"
|
||||||
let pattern1 = "func\\s+[\(operators)]+\(zeroOrManySpaces)(<[A-Z]+>)?\\("
|
let pattern1 = "func\\s+[\(operators)]+\(zeroOrManySpaces)(<[A-Z]+>)?\\("
|
||||||
let pattern2 = "func\(zeroOrManySpaces)[\(operators)]+\\s+(<[A-Z]+>)?\\("
|
let pattern2 = "func\(zeroOrManySpaces)[\(operators)]+\\s+(<[A-Z]+>)?\\("
|
||||||
return file.match(pattern: "(\(pattern1)|\(pattern2))").filter { _, syntaxKinds in
|
return file.match(pattern: "(\(pattern1)|\(pattern2))").filter { arg -> Bool in
|
||||||
|
let (_, syntaxKinds) = arg
|
||||||
return syntaxKinds.first == .keyword
|
return syntaxKinds.first == .keyword
|
||||||
}.map { range, _ in
|
}.map { arg in
|
||||||
|
let (range, _) = arg
|
||||||
return StyleViolation(ruleDescription: type(of: self).description,
|
return StyleViolation(ruleDescription: type(of: self).description,
|
||||||
severity: configuration.severity,
|
severity: configuration.severity,
|
||||||
location: Location(file: file, characterOffset: range.location))
|
location: Location(file: file, characterOffset: range.location))
|
||||||
|
|
|
@ -74,10 +74,11 @@ public struct OperatorUsageWhitespaceRule: OptInRule, CorrectableRule, Configura
|
||||||
)
|
)
|
||||||
|
|
||||||
public func validate(file: File) -> [StyleViolation] {
|
public func validate(file: File) -> [StyleViolation] {
|
||||||
return violationRanges(file: file).map { range, _ in
|
return violationRanges(file: file).map { rangeAndString in
|
||||||
StyleViolation(ruleDescription: type(of: self).description,
|
let (range, _) = rangeAndString
|
||||||
severity: configuration.severity,
|
return StyleViolation(ruleDescription: type(of: self).description,
|
||||||
location: Location(file: file, characterOffset: range.location))
|
severity: configuration.severity,
|
||||||
|
location: Location(file: file, characterOffset: range.location))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,8 +99,9 @@ public struct OperatorUsageWhitespaceRule: OptInRule, CorrectableRule, Configura
|
||||||
|
|
||||||
let spaces = [(zeroSpaces, zeroSpaces), (oneSpace, manySpaces),
|
let spaces = [(zeroSpaces, zeroSpaces), (oneSpace, manySpaces),
|
||||||
(manySpaces, oneSpace), (manySpaces, manySpaces)]
|
(manySpaces, oneSpace), (manySpaces, manySpaces)]
|
||||||
let patterns = spaces.map { first, second in
|
let patterns = spaces.map { arg -> String in
|
||||||
leadingVariableOrNumber + first + operators + second + trailingVariableOrNumber
|
let (first, second) = arg
|
||||||
|
return leadingVariableOrNumber + first + operators + second + trailingVariableOrNumber
|
||||||
}
|
}
|
||||||
let pattern = "(?:\(patterns.joined(separator: "|")))"
|
let pattern = "(?:\(patterns.joined(separator: "|")))"
|
||||||
|
|
||||||
|
@ -156,7 +158,8 @@ public struct OperatorUsageWhitespaceRule: OptInRule, CorrectableRule, Configura
|
||||||
}
|
}
|
||||||
|
|
||||||
public func correct(file: File) -> [Correction] {
|
public func correct(file: File) -> [Correction] {
|
||||||
let violatingRanges = violationRanges(file: file).filter { range, _ in
|
let violatingRanges = violationRanges(file: file).filter { arg -> Bool in
|
||||||
|
let (range, _) = arg
|
||||||
return !file.ruleEnabled(violatingRanges: [range], for: self).isEmpty
|
return !file.ruleEnabled(violatingRanges: [range], for: self).isEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,8 @@ public struct SortedImportsRule: ConfigurationProviderRule, OptInRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
let modulePairs = zip(modulesAndOffsets, modulesAndOffsets.dropFirst())
|
let modulePairs = zip(modulesAndOffsets, modulesAndOffsets.dropFirst())
|
||||||
let violatingOffsets = modulePairs.flatMap { previous, current in
|
let violatingOffsets = modulePairs.flatMap { arg -> Int? in
|
||||||
|
let (previous, current) = arg
|
||||||
return current < previous ? current.1 : nil
|
return current < previous ? current.1 : nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -107,7 +107,8 @@ private extension StatementPositionRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
func defaultViolationRanges(in file: File, matching pattern: String) -> [NSRange] {
|
func defaultViolationRanges(in file: File, matching pattern: String) -> [NSRange] {
|
||||||
return file.match(pattern: pattern).filter { _, syntaxKinds in
|
return file.match(pattern: pattern).filter { arg -> Bool in
|
||||||
|
let (_, syntaxKinds) = arg
|
||||||
return syntaxKinds.starts(with: [.keyword])
|
return syntaxKinds.starts(with: [.keyword])
|
||||||
}.flatMap { $0.0 }
|
}.flatMap { $0.0 }
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,7 +86,8 @@ public struct TodoRule: ConfigurationProviderRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func validate(file: File) -> [StyleViolation] {
|
public func validate(file: File) -> [StyleViolation] {
|
||||||
return file.match(pattern: "\\b(?:TODO|FIXME)(?::|\\b)").flatMap { range, syntaxKinds in
|
return file.match(pattern: "\\b(?:TODO|FIXME)(?::|\\b)").flatMap { rangeAndKinds in
|
||||||
|
let (range, syntaxKinds) = rangeAndKinds
|
||||||
if !syntaxKinds.filter({ !$0.isCommentLike }).isEmpty {
|
if !syntaxKinds.filter({ !$0.isCommentLike }).isEmpty {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,8 @@ public struct TypeNameRule: ASTRule, ConfigurationProviderRule {
|
||||||
|
|
||||||
private func validateTypeAliasesAndAssociatedTypes(in file: File) -> [StyleViolation] {
|
private func validateTypeAliasesAndAssociatedTypes(in file: File) -> [StyleViolation] {
|
||||||
let rangesAndTokens = file.rangesAndTokens(matching: "(typealias|associatedtype)\\s+.+?\\b")
|
let rangesAndTokens = file.rangesAndTokens(matching: "(typealias|associatedtype)\\s+.+?\\b")
|
||||||
return rangesAndTokens.flatMap { _, tokens -> [StyleViolation] in
|
return rangesAndTokens.flatMap { arg -> [StyleViolation] in
|
||||||
|
let (_, tokens) = arg
|
||||||
guard tokens.count == 2,
|
guard tokens.count == 2,
|
||||||
let keywordToken = tokens.first,
|
let keywordToken = tokens.first,
|
||||||
let nameToken = tokens.last,
|
let nameToken = tokens.last,
|
||||||
|
|
|
@ -80,7 +80,8 @@ public struct UnusedClosureParameterRule: ASTRule, ConfigurationProviderRule, Co
|
||||||
|
|
||||||
public func validate(file: File, kind: SwiftExpressionKind,
|
public func validate(file: File, kind: SwiftExpressionKind,
|
||||||
dictionary: [String: SourceKitRepresentable]) -> [StyleViolation] {
|
dictionary: [String: SourceKitRepresentable]) -> [StyleViolation] {
|
||||||
return violationRanges(in: file, dictionary: dictionary, kind: kind).map { range, name in
|
return violationRanges(in: file, dictionary: dictionary, kind: kind).map { rangeName in
|
||||||
|
let (range, name) = rangeName
|
||||||
let reason = "Unused parameter \"\(name)\" in a closure should be replaced with _."
|
let reason = "Unused parameter \"\(name)\" in a closure should be replaced with _."
|
||||||
return StyleViolation(ruleDescription: type(of: self).description,
|
return StyleViolation(ruleDescription: type(of: self).description,
|
||||||
severity: configuration.severity,
|
severity: configuration.severity,
|
||||||
|
|
|
@ -115,7 +115,7 @@ public struct ValidIBInspectableRule: ASTRule, ConfigurationProviderRule {
|
||||||
"NSRect"
|
"NSRect"
|
||||||
]
|
]
|
||||||
|
|
||||||
let intTypes = ["", "8", "16", "32", "64"].flatMap { size in
|
let intTypes: [String] = ["", "8", "16", "32", "64"].flatMap { size in
|
||||||
["U", ""].flatMap { (sign: String) -> String in
|
["U", ""].flatMap { (sign: String) -> String in
|
||||||
"\(sign)Int\(size)"
|
"\(sign)Int\(size)"
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ struct AutoCorrectCommand: CommandProtocol {
|
||||||
if !options.quiet {
|
if !options.quiet {
|
||||||
queuedPrintError("Done correcting \(files.count) files!")
|
queuedPrintError("Done correcting \(files.count) files!")
|
||||||
}
|
}
|
||||||
return .success()
|
return .success(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ struct LintCommand: CommandProtocol {
|
||||||
currentViolations = LintCommand.applyLeniency(options: options, violations: _currentViolations)
|
currentViolations = LintCommand.applyLeniency(options: options, violations: _currentViolations)
|
||||||
visitorMutationQueue.sync {
|
visitorMutationQueue.sync {
|
||||||
fileBenchmark.record(file: linter.file, from: start)
|
fileBenchmark.record(file: linter.file, from: start)
|
||||||
currentRuleTimes.forEach { ruleBenchmark.record(id: $0, time: $1) }
|
currentRuleTimes.forEach { ruleBenchmark.record(id: $0.0, time: $0.1) }
|
||||||
violations += currentViolations
|
violations += currentViolations
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -73,7 +73,7 @@ struct LintCommand: CommandProtocol {
|
||||||
} else if strictWithViolations {
|
} else if strictWithViolations {
|
||||||
exit(3)
|
exit(3)
|
||||||
}
|
}
|
||||||
return .success()
|
return .success(())
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func printStatus(violations: [StyleViolation], files: [File], serious: Int) {
|
private static func printStatus(violations: [StyleViolation], files: [File], serious: Int) {
|
||||||
|
|
|
@ -38,14 +38,14 @@ struct RulesCommand: CommandProtocol {
|
||||||
}
|
}
|
||||||
|
|
||||||
print(ruleDescription: rule.description)
|
print(ruleDescription: rule.description)
|
||||||
return .success()
|
return .success(())
|
||||||
}
|
}
|
||||||
|
|
||||||
let configuration = Configuration(commandLinePath: options.configurationFile)
|
let configuration = Configuration(commandLinePath: options.configurationFile)
|
||||||
let rules = ruleList(for: options, configuration: configuration)
|
let rules = ruleList(for: options, configuration: configuration)
|
||||||
|
|
||||||
print(TextTable(ruleList: rules, configuration: configuration).render())
|
print(TextTable(ruleList: rules, configuration: configuration).render())
|
||||||
return .success()
|
return .success(())
|
||||||
}
|
}
|
||||||
|
|
||||||
private func ruleList(for options: RulesOptions, configuration: Configuration) -> RuleList {
|
private func ruleList(for options: RulesOptions, configuration: Configuration) -> RuleList {
|
||||||
|
@ -53,7 +53,8 @@ struct RulesCommand: CommandProtocol {
|
||||||
return masterRuleList
|
return masterRuleList
|
||||||
}
|
}
|
||||||
|
|
||||||
let filtered: [Rule.Type] = masterRuleList.list.flatMap { ruleID, ruleType in
|
let filtered: [Rule.Type] = masterRuleList.list.flatMap { ruleIDAndType in
|
||||||
|
let (ruleID, ruleType) = ruleIDAndType
|
||||||
let configuredRule = configuration.rules.first { rule in
|
let configuredRule = configuration.rules.first { rule in
|
||||||
return type(of: rule).description.identifier == ruleID
|
return type(of: rule).description.identifier == ruleID
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,6 @@ struct VersionCommand: CommandProtocol {
|
||||||
|
|
||||||
func run(_ options: NoOptions<CommandantError<()>>) -> Result<(), CommandantError<()>> {
|
func run(_ options: NoOptions<CommandantError<()>>) -> Result<(), CommandantError<()>> {
|
||||||
print(Version.current.value)
|
print(Version.current.value)
|
||||||
return .success()
|
return .success(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,8 @@ struct Benchmark {
|
||||||
return accu
|
return accu
|
||||||
}
|
}
|
||||||
let entriesKeyValues: [(String, Double)] = entriesDict.sorted { $0.1 < $1.1 }
|
let entriesKeyValues: [(String, Double)] = entriesDict.sorted { $0.1 < $1.1 }
|
||||||
let lines: [String] = entriesKeyValues.map { id, time -> String in
|
let lines: [String] = entriesKeyValues.map { idAndTime -> String in
|
||||||
|
let (id, time) = idAndTime
|
||||||
return "\(numberFormatter.string(from: NSNumber(value: time))!): \(id)"
|
return "\(numberFormatter.string(from: NSNumber(value: time))!): \(id)"
|
||||||
}
|
}
|
||||||
let string: String = lines.joined(separator: "\n") + "\n"
|
let string: String = lines.joined(separator: "\n") + "\n"
|
||||||
|
|
|
@ -1492,7 +1492,7 @@
|
||||||
FRAMEWORK_VERSION = A;
|
FRAMEWORK_VERSION = A;
|
||||||
INFOPLIST_FILE = "Source/SwiftLintFramework/Supporting Files/Info.plist";
|
INFOPLIST_FILE = "Source/SwiftLintFramework/Supporting Files/Info.plist";
|
||||||
LD_RUNPATH_SEARCH_PATHS = "@loader_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "@loader_path/Frameworks";
|
||||||
OTHER_SWIFT_FLAGS = "$(inherited) -Xfrontend -warn-long-function-bodies=150";
|
OTHER_SWIFT_FLAGS = "$(inherited)";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "io.realm.$(PRODUCT_NAME:rfc1034identifier)";
|
PRODUCT_BUNDLE_IDENTIFIER = "io.realm.$(PRODUCT_NAME:rfc1034identifier)";
|
||||||
PRODUCT_NAME = SwiftLintFramework;
|
PRODUCT_NAME = SwiftLintFramework;
|
||||||
SWIFT_VERSION = 3.0;
|
SWIFT_VERSION = 3.0;
|
||||||
|
@ -1574,7 +1574,7 @@
|
||||||
FRAMEWORK_VERSION = A;
|
FRAMEWORK_VERSION = A;
|
||||||
INFOPLIST_FILE = "Source/SwiftLintFramework/Supporting Files/Info.plist";
|
INFOPLIST_FILE = "Source/SwiftLintFramework/Supporting Files/Info.plist";
|
||||||
LD_RUNPATH_SEARCH_PATHS = "@loader_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "@loader_path/Frameworks";
|
||||||
OTHER_SWIFT_FLAGS = "$(inherited) -Xfrontend -warn-long-function-bodies=150";
|
OTHER_SWIFT_FLAGS = "$(inherited)";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "io.realm.$(PRODUCT_NAME:rfc1034identifier)";
|
PRODUCT_BUNDLE_IDENTIFIER = "io.realm.$(PRODUCT_NAME:rfc1034identifier)";
|
||||||
PRODUCT_NAME = SwiftLintFramework;
|
PRODUCT_NAME = SwiftLintFramework;
|
||||||
SWIFT_VERSION = 3.0;
|
SWIFT_VERSION = 3.0;
|
||||||
|
@ -1620,7 +1620,7 @@
|
||||||
FRAMEWORK_VERSION = A;
|
FRAMEWORK_VERSION = A;
|
||||||
INFOPLIST_FILE = "Source/SwiftLintFramework/Supporting Files/Info.plist";
|
INFOPLIST_FILE = "Source/SwiftLintFramework/Supporting Files/Info.plist";
|
||||||
LD_RUNPATH_SEARCH_PATHS = "@loader_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "@loader_path/Frameworks";
|
||||||
OTHER_SWIFT_FLAGS = "$(inherited) -Xfrontend -warn-long-function-bodies=150";
|
OTHER_SWIFT_FLAGS = "$(inherited)";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "io.realm.$(PRODUCT_NAME:rfc1034identifier)";
|
PRODUCT_BUNDLE_IDENTIFIER = "io.realm.$(PRODUCT_NAME:rfc1034identifier)";
|
||||||
PRODUCT_NAME = SwiftLintFramework;
|
PRODUCT_NAME = SwiftLintFramework;
|
||||||
SWIFT_VERSION = 3.0;
|
SWIFT_VERSION = 3.0;
|
||||||
|
|
|
@ -93,7 +93,7 @@ class CustomRulesTests: XCTestCase {
|
||||||
func getCustomRules(_ extraConfig: [String:String] = [:]) -> (RegexConfiguration, CustomRules) {
|
func getCustomRules(_ extraConfig: [String:String] = [:]) -> (RegexConfiguration, CustomRules) {
|
||||||
var config = ["regex": "pattern",
|
var config = ["regex": "pattern",
|
||||||
"match_kinds": "comment"]
|
"match_kinds": "comment"]
|
||||||
extraConfig.forEach { config[$0] = $1 }
|
extraConfig.forEach { config[$0.0] = $0.1 }
|
||||||
|
|
||||||
var regexConfig = RegexConfiguration(identifier: "custom")
|
var regexConfig = RegexConfiguration(identifier: "custom")
|
||||||
do {
|
do {
|
||||||
|
|
|
@ -191,7 +191,8 @@ extension XCTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
// "disable" commands do not correct
|
// "disable" commands do not correct
|
||||||
ruleDescription.corrections.forEach { before, _ in
|
ruleDescription.corrections.forEach { arg in
|
||||||
|
let (before, _) = arg
|
||||||
for command in disableCommands {
|
for command in disableCommands {
|
||||||
let beforeDisabled = command + before
|
let beforeDisabled = command + before
|
||||||
let expectedCleaned = cleanedContentsAndMarkerOffsets(from: beforeDisabled).0
|
let expectedCleaned = cleanedContentsAndMarkerOffsets(from: beforeDisabled).0
|
||||||
|
|
Loading…
Reference in New Issue