Compare commits
22 Commits
main
...
mf-plugins
Author | SHA1 | Date |
---|---|---|
![]() |
1e72a7bc97 | |
![]() |
cfc01e7875 | |
![]() |
00cf87af6b | |
![]() |
0f816cee95 | |
![]() |
053dd7043b | |
![]() |
b054c1f5df | |
![]() |
5c46ba8b8e | |
![]() |
988ebbb795 | |
![]() |
e470e7abff | |
![]() |
2cb160c538 | |
![]() |
155cd67997 | |
![]() |
be75073b74 | |
![]() |
3a3918e30c | |
![]() |
545580eb1f | |
![]() |
fb4e93e36c | |
![]() |
804e8d69a0 | |
![]() |
a42a8e940d | |
![]() |
0d1e1cb094 | |
![]() |
01b70ee2c9 | |
![]() |
1b984b6cf9 | |
![]() |
93d4696e4d | |
![]() |
7782098654 |
|
@ -19,3 +19,6 @@
|
|||
[submodule "Carthage/Checkouts/Yams"]
|
||||
path = Carthage/Checkouts/Yams
|
||||
url = https://github.com/jpsim/Yams.git
|
||||
[submodule "Carthage/Checkouts/BlueSocket"]
|
||||
path = Carthage/Checkouts/BlueSocket
|
||||
url = https://github.com/IBM-Swift/BlueSocket.git
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
github "Carthage/Commandant" ~> 0.15.0
|
||||
github "IBM-Swift/BlueSocket" ~> 1.0.0
|
||||
github "jpsim/Yams" ~> 1.0.1
|
||||
github "jspahrsummers/xcconfigs" ~> 0.12.0
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
github "Carthage/Commandant" "0.15.0"
|
||||
github "antitypical/Result" "4.0.0"
|
||||
github "drmohundro/SWXMLHash" "4.7.6"
|
||||
github "IBM-Swift/BlueSocket" "1.0.43"
|
||||
github "jpsim/SourceKitten" "0.22.0"
|
||||
github "jpsim/Yams" "1.0.1"
|
||||
github "jspahrsummers/xcconfigs" "0.12"
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 9c1b578c618f0bfac017aaaaead19de57bb37831
|
2
Makefile
2
Makefile
|
@ -119,7 +119,7 @@ archive:
|
|||
release: package archive portable_zip
|
||||
|
||||
docker_test:
|
||||
docker run -v `pwd`:`pwd` -w `pwd` --name swiftlint --rm norionomura/swift:42 swift test --parallel
|
||||
docker run -v `pwd`:`pwd` -w `pwd` --name swiftlint --rm norionomura/swift:421 swift test --parallel
|
||||
|
||||
docker_htop:
|
||||
docker run -it --rm --pid=container:swiftlint terencewestphal/htop || reset
|
||||
|
|
|
@ -1,6 +1,15 @@
|
|||
{
|
||||
"object": {
|
||||
"pins": [
|
||||
{
|
||||
"package": "Socket",
|
||||
"repositoryURL": "https://github.com/IBM-Swift/BlueSocket.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "9c1b578c618f0bfac017aaaaead19de57bb37831",
|
||||
"version": "1.0.43"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "Commandant",
|
||||
"repositoryURL": "https://github.com/Carthage/Commandant.git",
|
||||
|
|
|
@ -15,6 +15,7 @@ let package = Package(
|
|||
],
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/Carthage/Commandant.git", from: "0.15.0"),
|
||||
.package(url: "https://github.com/IBM-Swift/BlueSocket.git", from: "1.0.0"),
|
||||
.package(url: "https://github.com/jpsim/SourceKitten.git", from: "0.22.0"),
|
||||
.package(url: "https://github.com/jpsim/Yams.git", from: "1.0.1"),
|
||||
.package(url: "https://github.com/scottrhoyt/SwiftyTextTable.git", from: "0.8.2"),
|
||||
|
@ -31,6 +32,7 @@ let package = Package(
|
|||
.target(
|
||||
name: "SwiftLintFramework",
|
||||
dependencies: [
|
||||
"Socket",
|
||||
"SourceKittenFramework",
|
||||
"Yams",
|
||||
] + (addCryptoSwift ? ["CryptoSwift"] : [])
|
||||
|
|
|
@ -115,7 +115,9 @@ extension Configuration {
|
|||
rules: mergingRules(with: configuration),
|
||||
cachePath: cachePath, // Always use the parent cache path
|
||||
rootPath: configuration.rootPath,
|
||||
indentation: configuration.indentation
|
||||
indentation: configuration.indentation,
|
||||
plugins: plugins,
|
||||
remoteRules: remoteRules
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,10 +13,11 @@ extension Configuration {
|
|||
case whitelistRules = "whitelist_rules"
|
||||
case indentation = "indentation"
|
||||
case analyzerRules = "analyzer_rules"
|
||||
case plugins = "plugins"
|
||||
}
|
||||
|
||||
private static func validKeys(ruleList: RuleList) -> [String] {
|
||||
return [
|
||||
private static func validKeys(ruleList: RuleList, remoteRules: [RemoteRule]) -> [String] {
|
||||
let globalKeys = [
|
||||
Key.cachePath,
|
||||
.disabledRules,
|
||||
.enabledRules,
|
||||
|
@ -29,8 +30,12 @@ extension Configuration {
|
|||
.warningThreshold,
|
||||
.whitelistRules,
|
||||
.indentation,
|
||||
.analyzerRules
|
||||
].map({ $0.rawValue }) + ruleList.allValidIdentifiers()
|
||||
.analyzerRules,
|
||||
.plugins
|
||||
].map { $0.rawValue }
|
||||
|
||||
return globalKeys + ruleList.allValidIdentifiers() +
|
||||
remoteRules.flatMap { $0.ruleDescription.allIdentifiers }
|
||||
}
|
||||
|
||||
private static func getIndentationLogIfInvalid(from dict: [String: Any]) -> IndentationStyle {
|
||||
|
@ -46,23 +51,23 @@ extension Configuration {
|
|||
return .default
|
||||
}
|
||||
|
||||
public init?(dict: [String: Any], ruleList: RuleList = masterRuleList, enableAllRules: Bool = false,
|
||||
cachePath: String? = nil) {
|
||||
func defaultStringArray(_ object: Any?) -> [String] {
|
||||
return [String].array(of: object) ?? []
|
||||
}
|
||||
|
||||
public init?(dict: [String: Any],
|
||||
ruleList: RuleList = masterRuleList,
|
||||
enableAllRules: Bool = false,
|
||||
cachePath: String? = nil,
|
||||
remoteRulesResolver: RemoteRuleResolverProtocol = RemoteRuleResolver()) {
|
||||
// Use either new 'opt_in_rules' or deprecated 'enabled_rules' for now.
|
||||
let optInRules = defaultStringArray(
|
||||
dict[Key.optInRules.rawValue] ?? dict[Key.enabledRules.rawValue]
|
||||
)
|
||||
|
||||
// Log an error when supplying invalid keys in the configuration dictionary
|
||||
let invalidKeys = Set(dict.keys).subtracting(Configuration.validKeys(ruleList: ruleList))
|
||||
if !invalidKeys.isEmpty {
|
||||
queuedPrintError("Configuration contains invalid keys:\n\(invalidKeys)")
|
||||
let plugins = defaultStringArray(dict[Key.plugins.rawValue])
|
||||
let remoteRules = plugins.compactMap {
|
||||
try? remoteRulesResolver.remoteRule(forExecutable: $0, configuration: dict)
|
||||
}
|
||||
|
||||
Configuration.validateConfigurationKeys(dict: dict, ruleList: ruleList, remoteRules: remoteRules)
|
||||
|
||||
let disabledRules = defaultStringArray(dict[Key.disabledRules.rawValue])
|
||||
let whitelistRules = defaultStringArray(dict[Key.whitelistRules.rawValue])
|
||||
let analyzerRules = defaultStringArray(dict[Key.analyzerRules.rawValue])
|
||||
|
@ -77,9 +82,7 @@ extension Configuration {
|
|||
do {
|
||||
configuredRules = try ruleList.configuredRules(with: dict)
|
||||
} catch RuleListError.duplicatedConfigurations(let ruleType) {
|
||||
let aliases = ruleType.description.deprecatedAliases.map { "'\($0)'" }.joined(separator: ", ")
|
||||
let identifier = ruleType.description.identifier
|
||||
queuedPrintError("Multiple configurations found for '\(identifier)'. Check for any aliases: \(aliases).")
|
||||
Configuration.warnAboutDuplicateConfigurations(for: ruleType)
|
||||
return nil
|
||||
} catch {
|
||||
return nil
|
||||
|
@ -99,7 +102,9 @@ extension Configuration {
|
|||
configuredRules: configuredRules,
|
||||
swiftlintVersion: swiftlintVersion,
|
||||
cachePath: cachePath ?? dict[Key.cachePath.rawValue] as? String,
|
||||
indentation: indentation)
|
||||
indentation: indentation,
|
||||
plugins: plugins,
|
||||
remoteRules: remoteRules)
|
||||
}
|
||||
|
||||
private init?(disabledRules: [String],
|
||||
|
@ -115,7 +120,9 @@ extension Configuration {
|
|||
configuredRules: [Rule]?,
|
||||
swiftlintVersion: String?,
|
||||
cachePath: String?,
|
||||
indentation: IndentationStyle) {
|
||||
indentation: IndentationStyle,
|
||||
plugins: [String],
|
||||
remoteRules: [RemoteRule]) {
|
||||
let rulesMode: RulesMode
|
||||
if enableAllRules {
|
||||
rulesMode = .allEnabled
|
||||
|
@ -140,7 +147,9 @@ extension Configuration {
|
|||
configuredRules: configuredRules,
|
||||
swiftlintVersion: swiftlintVersion,
|
||||
cachePath: cachePath,
|
||||
indentation: indentation)
|
||||
indentation: indentation,
|
||||
plugins: plugins,
|
||||
remoteRules: remoteRules)
|
||||
}
|
||||
|
||||
private static func warnAboutDeprecations(configurationDictionary dict: [String: Any],
|
||||
|
@ -177,4 +186,23 @@ extension Configuration {
|
|||
"completely removed in a future release.")
|
||||
}
|
||||
}
|
||||
|
||||
private static func warnAboutDuplicateConfigurations(for ruleType: Rule.Type) {
|
||||
let aliases = ruleType.description.deprecatedAliases.map { "'\($0)'" }.joined(separator: ", ")
|
||||
let identifier = ruleType.description.identifier
|
||||
queuedPrintError("Multiple configurations found for '\(identifier)'. Check for any aliases: \(aliases).")
|
||||
}
|
||||
|
||||
private static func validateConfigurationKeys(dict: [String: Any], ruleList: RuleList, remoteRules: [RemoteRule]) {
|
||||
// Log an error when supplying invalid keys in the configuration dictionary
|
||||
let invalidKeys = Set(dict.keys).subtracting(validKeys(ruleList: ruleList,
|
||||
remoteRules: remoteRules))
|
||||
if !invalidKeys.isEmpty {
|
||||
queuedPrintError("Configuration contains invalid keys:\n\(invalidKeys)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func defaultStringArray(_ object: Any?) -> [String] {
|
||||
return [String].array(of: object) ?? []
|
||||
}
|
||||
|
|
|
@ -40,6 +40,15 @@ public func queuedPrintError(_ string: String) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
A thread-safe, newline-terminated version of fputs(..., stderr).
|
||||
|
||||
- parameter error: Error to print.
|
||||
*/
|
||||
public func queuedPrintError(_ error: Error) {
|
||||
queuedPrintError(error.localizedDescription)
|
||||
}
|
||||
|
||||
/**
|
||||
A thread-safe, newline-terminated version of fatalError that doesn't leak
|
||||
the source path from the compiled binary.
|
||||
|
|
|
@ -18,6 +18,7 @@ public struct Configuration: Hashable {
|
|||
public let excluded: [String] // excluded
|
||||
public let reporter: String // reporter (xcode, json, csv, checkstyle)
|
||||
public let warningThreshold: Int? // warning threshold
|
||||
public let plugins: [String]
|
||||
public private(set) var rootPath: String? // the root path to search for nested configurations
|
||||
public private(set) var configurationPath: String? // if successfully loaded from a path
|
||||
public let cachePath: String?
|
||||
|
@ -33,6 +34,7 @@ public struct Configuration: Hashable {
|
|||
hasher.combine(included)
|
||||
hasher.combine(excluded)
|
||||
hasher.combine(reporter)
|
||||
hasher.combine(plugins)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,9 +42,11 @@ public struct Configuration: Hashable {
|
|||
|
||||
// MARK: Rules Properties
|
||||
|
||||
// All rules enabled in this configuration, derived from disabled, opt-in and whitelist rules
|
||||
/// All rules enabled in this configuration, derived from disabled, opt-in and whitelist rules
|
||||
public let rules: [Rule]
|
||||
|
||||
internal let remoteRules: [RemoteRule]
|
||||
|
||||
internal let rulesMode: RulesMode
|
||||
|
||||
// MARK: Initializers
|
||||
|
@ -56,7 +60,10 @@ public struct Configuration: Hashable {
|
|||
configuredRules: [Rule]? = nil,
|
||||
swiftlintVersion: String? = nil,
|
||||
cachePath: String? = nil,
|
||||
indentation: IndentationStyle = .default) {
|
||||
indentation: IndentationStyle = .default,
|
||||
plugins: [String] = [],
|
||||
remoteRulesResolver: RemoteRuleResolverProtocol = RemoteRuleResolver(),
|
||||
remoteRules: [RemoteRule]? = nil) {
|
||||
if let pinnedVersion = swiftlintVersion, pinnedVersion != Version.current.value {
|
||||
queuedPrintError("Currently running SwiftLint \(Version.current.value) but " +
|
||||
"configuration specified version \(pinnedVersion).")
|
||||
|
@ -67,11 +74,16 @@ public struct Configuration: Hashable {
|
|||
?? (try? ruleList.configuredRules(with: [:]))
|
||||
?? []
|
||||
|
||||
let remoteRules = remoteRules ?? plugins.compactMap {
|
||||
try? remoteRulesResolver.remoteRule(forExecutable: $0, configuration: nil)
|
||||
}
|
||||
|
||||
let handleAliasWithRuleList: (String) -> String = { ruleList.identifier(for: $0) ?? $0 }
|
||||
|
||||
guard let rules = enabledRules(from: configuredRules,
|
||||
with: rulesMode,
|
||||
aliasResolver: handleAliasWithRuleList) else {
|
||||
guard let enabledRules = enabledRules(from: configuredRules,
|
||||
with: rulesMode,
|
||||
remoteRules: remoteRules,
|
||||
aliasResolver: handleAliasWithRuleList) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -80,9 +92,11 @@ public struct Configuration: Hashable {
|
|||
excluded: excluded,
|
||||
warningThreshold: warningThreshold,
|
||||
reporter: reporter,
|
||||
rules: rules,
|
||||
rules: enabledRules.0,
|
||||
cachePath: cachePath,
|
||||
indentation: indentation)
|
||||
indentation: indentation,
|
||||
plugins: plugins,
|
||||
remoteRules: enabledRules.1)
|
||||
}
|
||||
|
||||
internal init(rulesMode: RulesMode,
|
||||
|
@ -93,7 +107,9 @@ public struct Configuration: Hashable {
|
|||
rules: [Rule],
|
||||
cachePath: String?,
|
||||
rootPath: String? = nil,
|
||||
indentation: IndentationStyle) {
|
||||
indentation: IndentationStyle,
|
||||
plugins: [String],
|
||||
remoteRules: [RemoteRule]) {
|
||||
self.rulesMode = rulesMode
|
||||
self.included = included
|
||||
self.excluded = excluded
|
||||
|
@ -102,6 +118,8 @@ public struct Configuration: Hashable {
|
|||
self.rules = rules
|
||||
self.rootPath = rootPath
|
||||
self.indentation = indentation
|
||||
self.plugins = plugins
|
||||
self.remoteRules = remoteRules
|
||||
|
||||
// set the config threshold to the threshold provided in the config file
|
||||
self.warningThreshold = warningThreshold
|
||||
|
@ -117,6 +135,8 @@ public struct Configuration: Hashable {
|
|||
cachePath = configuration.cachePath
|
||||
rootPath = configuration.rootPath
|
||||
indentation = configuration.indentation
|
||||
plugins = configuration.plugins
|
||||
remoteRules = configuration.remoteRules
|
||||
}
|
||||
|
||||
public init(path: String = Configuration.fileName, rootPath: String? = nil,
|
||||
|
@ -176,7 +196,8 @@ public struct Configuration: Hashable {
|
|||
(lhs.included == rhs.included) &&
|
||||
(lhs.excluded == rhs.excluded) &&
|
||||
(lhs.rules == rhs.rules) &&
|
||||
(lhs.indentation == rhs.indentation)
|
||||
(lhs.indentation == rhs.indentation) &&
|
||||
(lhs.plugins == rhs.plugins)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -213,12 +234,13 @@ private func containsDuplicateIdentifiers(_ identifiers: [String]) -> Bool {
|
|||
|
||||
private func enabledRules(from configuredRules: [Rule],
|
||||
with mode: Configuration.RulesMode,
|
||||
aliasResolver: (String) -> String) -> [Rule]? {
|
||||
let validRuleIdentifiers = configuredRules.map { type(of: $0).description.identifier }
|
||||
remoteRules: [RemoteRule],
|
||||
aliasResolver: (String) -> String) -> ([Rule], [RemoteRule])? {
|
||||
let validRuleIdentifiers = configuredRules.map { type(of: $0).description.identifier } + remoteRules.identifiers
|
||||
|
||||
switch mode {
|
||||
case .allEnabled:
|
||||
return configuredRules
|
||||
return (configuredRules, remoteRules)
|
||||
case .whitelisted(let whitelistedRuleIdentifiers):
|
||||
let validWhitelistedRuleIdentifiers = validateRuleIdentifiers(
|
||||
ruleIdentifiers: whitelistedRuleIdentifiers.map(aliasResolver),
|
||||
|
@ -227,9 +249,15 @@ private func enabledRules(from configuredRules: [Rule],
|
|||
if containsDuplicateIdentifiers(validWhitelistedRuleIdentifiers) {
|
||||
return nil
|
||||
}
|
||||
return configuredRules.filter { rule in
|
||||
|
||||
let whitelistedRules = configuredRules.filter { rule in
|
||||
return validWhitelistedRuleIdentifiers.contains(type(of: rule).description.identifier)
|
||||
}
|
||||
let whitelistedRemoteRules = remoteRules.filter { rule in
|
||||
return validWhitelistedRuleIdentifiers.contains(rule.ruleDescription.identifier)
|
||||
}
|
||||
|
||||
return (whitelistedRules, whitelistedRemoteRules)
|
||||
case let .default(disabledRuleIdentifiers, optInRuleIdentifiers):
|
||||
let validDisabledRuleIdentifiers = validateRuleIdentifiers(
|
||||
ruleIdentifiers: disabledRuleIdentifiers.map(aliasResolver),
|
||||
|
@ -242,11 +270,19 @@ private func enabledRules(from configuredRules: [Rule],
|
|||
|| containsDuplicateIdentifiers(validOptInRuleIdentifiers) {
|
||||
return nil
|
||||
}
|
||||
return configuredRules.filter { rule in
|
||||
|
||||
let enabledRules = configuredRules.filter { rule in
|
||||
let id = type(of: rule).description.identifier
|
||||
if validDisabledRuleIdentifiers.contains(id) { return false }
|
||||
return validOptInRuleIdentifiers.contains(id) || !(rule is OptInRule)
|
||||
}
|
||||
|
||||
let enabledRemoteRules = remoteRules.filter { rule in
|
||||
let id = rule.ruleDescription.identifier
|
||||
return !validDisabledRuleIdentifiers.contains(id)
|
||||
}
|
||||
|
||||
return (enabledRules, enabledRemoteRules)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
// extracted from https://forums.swift.org/t/pitch-declaring-local-variables-as-lazy/9287/3
|
||||
internal class Lazy<Result> {
|
||||
private var computation: () -> Result
|
||||
private(set) lazy var value: Result = computation()
|
||||
|
||||
init(_ computation: @escaping @autoclosure () -> Result) {
|
||||
self.computation = computation
|
||||
}
|
||||
}
|
|
@ -7,16 +7,83 @@ private struct LintResult {
|
|||
let deprecatedToValidIDPairs: [(String, String)]
|
||||
}
|
||||
|
||||
private extension Rule {
|
||||
static func superfluousDisableCommandViolations(regions: [Region],
|
||||
superfluousDisableCommandRule: SuperfluousDisableCommandRule?,
|
||||
allViolations: [StyleViolation]) -> [StyleViolation] {
|
||||
private enum LintableBox {
|
||||
case rule(Rule)
|
||||
case remoteRule(RemoteRule)
|
||||
|
||||
private var ruleDescription: RuleDescription {
|
||||
switch self {
|
||||
case .rule(let rule):
|
||||
return type(of: rule).description
|
||||
case .remoteRule(let remoteRule):
|
||||
return remoteRule.ruleDescription
|
||||
}
|
||||
}
|
||||
|
||||
func lint(file: File, regions: [Region], benchmark: Bool,
|
||||
superfluousDisableCommandRule: SuperfluousDisableCommandRule?,
|
||||
compilerArguments: [String]) -> LintResult? {
|
||||
let lintResultBeforeDisableCommand: LintResult?
|
||||
switch self {
|
||||
case .rule(let rule):
|
||||
lintResultBeforeDisableCommand = rule.lint(file: file, regions: regions, benchmark: benchmark,
|
||||
compilerArguments: compilerArguments)
|
||||
case .remoteRule(let remoteRule):
|
||||
lintResultBeforeDisableCommand = remoteRule.lint(file: file, regions: regions, benchmark: benchmark,
|
||||
compilerArguments: compilerArguments)
|
||||
}
|
||||
|
||||
guard let lintResult = lintResultBeforeDisableCommand else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let violations = lintResult.violations
|
||||
let (disabledViolationsAndRegions, enabledViolationsAndRegions) = violations.map { violation in
|
||||
return (violation, regions.first { $0.contains(violation.location) })
|
||||
}.partitioned { _, region in
|
||||
return region?.isRuleEnabled(ruleDescription) ?? true
|
||||
}
|
||||
|
||||
let ruleIDs = ruleDescription.allIdentifiers +
|
||||
(superfluousDisableCommandRule.map({ type(of: $0) })?.description.allIdentifiers ?? [])
|
||||
let ruleIdentifiers = Set(ruleIDs.map { RuleIdentifier($0) })
|
||||
|
||||
let superfluousDisableCommandViolations = ruleDescription.superfluousDisableCommandViolations(
|
||||
regions: regions.count > 1 ? file.regions(restrictingRuleIdentifiers: ruleIdentifiers) : regions,
|
||||
superfluousDisableCommandRule: superfluousDisableCommandRule,
|
||||
allViolations: violations
|
||||
)
|
||||
|
||||
let enabledViolations: [StyleViolation]
|
||||
if file.contents.hasPrefix("#!") { // if a violation happens on the same line as a shebang, ignore it
|
||||
enabledViolations = enabledViolationsAndRegions.compactMap { violation, _ in
|
||||
if violation.location.line == 1 { return nil }
|
||||
return violation
|
||||
}
|
||||
} else {
|
||||
enabledViolations = enabledViolationsAndRegions.map { $0.0 }
|
||||
}
|
||||
let deprecatedToValidIDPairs = disabledViolationsAndRegions.flatMap { _, region -> [(String, String)] in
|
||||
let identifiers = region?.deprecatedAliasesDisabling(ruleDescription: ruleDescription) ?? []
|
||||
return identifiers.map { ($0, ruleDescription.identifier) }
|
||||
}
|
||||
|
||||
return LintResult(violations: enabledViolations + superfluousDisableCommandViolations,
|
||||
ruleTime: lintResult.ruleTime,
|
||||
deprecatedToValidIDPairs: deprecatedToValidIDPairs)
|
||||
}
|
||||
}
|
||||
|
||||
private extension RuleDescription {
|
||||
func superfluousDisableCommandViolations(regions: [Region],
|
||||
superfluousDisableCommandRule: SuperfluousDisableCommandRule?,
|
||||
allViolations: [StyleViolation]) -> [StyleViolation] {
|
||||
guard !regions.isEmpty, let superfluousDisableCommandRule = superfluousDisableCommandRule else {
|
||||
return []
|
||||
}
|
||||
|
||||
let regionsDisablingCurrentRule = regions.filter { region in
|
||||
return region.isRuleDisabled(self.init())
|
||||
return region.isRuleDisabled(self)
|
||||
}
|
||||
let regionsDisablingSuperflousDisableRule = regions.filter { region in
|
||||
return region.isRuleDisabled(superfluousDisableCommandRule)
|
||||
|
@ -43,9 +110,10 @@ private extension Rule {
|
|||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension Rule {
|
||||
func lint(file: File, regions: [Region], benchmark: Bool,
|
||||
superfluousDisableCommandRule: SuperfluousDisableCommandRule?,
|
||||
compilerArguments: [String]) -> LintResult? {
|
||||
if !(self is SourceKitFreeRule) && file.sourcekitdFailed {
|
||||
return nil
|
||||
|
@ -64,38 +132,29 @@ private extension Rule {
|
|||
ruleTime = nil
|
||||
}
|
||||
|
||||
let (disabledViolationsAndRegions, enabledViolationsAndRegions) = violations.map { violation in
|
||||
return (violation, regions.first { $0.contains(violation.location) })
|
||||
}.partitioned { _, region in
|
||||
return region?.isRuleEnabled(self) ?? true
|
||||
}
|
||||
return LintResult(violations: violations, ruleTime: ruleTime,
|
||||
deprecatedToValidIDPairs: [])
|
||||
}
|
||||
}
|
||||
|
||||
let ruleIDs = Self.description.allIdentifiers +
|
||||
(superfluousDisableCommandRule.map({ type(of: $0) })?.description.allIdentifiers ?? [])
|
||||
let ruleIdentifiers = Set(ruleIDs.map { RuleIdentifier($0) })
|
||||
private extension RemoteRule {
|
||||
func lint(file: File, regions: [Region], benchmark: Bool,
|
||||
compilerArguments: [String]) -> LintResult? {
|
||||
let ruleID = ruleDescription.identifier
|
||||
|
||||
let superfluousDisableCommandViolations = Self.superfluousDisableCommandViolations(
|
||||
regions: regions.count > 1 ? file.regions(restrictingRuleIdentifiers: ruleIdentifiers) : regions,
|
||||
superfluousDisableCommandRule: superfluousDisableCommandRule,
|
||||
allViolations: violations
|
||||
)
|
||||
|
||||
let enabledViolations: [StyleViolation]
|
||||
if file.contents.hasPrefix("#!") { // if a violation happens on the same line as a shebang, ignore it
|
||||
enabledViolations = enabledViolationsAndRegions.compactMap { violation, _ in
|
||||
if violation.location.line == 1 { return nil }
|
||||
return violation
|
||||
}
|
||||
let violations: [StyleViolation]
|
||||
let ruleTime: (String, Double)?
|
||||
if benchmark {
|
||||
let start = Date()
|
||||
violations = validate(file: file)
|
||||
ruleTime = (ruleID, -start.timeIntervalSinceNow)
|
||||
} else {
|
||||
enabledViolations = enabledViolationsAndRegions.map { $0.0 }
|
||||
}
|
||||
let deprecatedToValidIDPairs = disabledViolationsAndRegions.flatMap { _, region -> [(String, String)] in
|
||||
let identifiers = region?.deprecatedAliasesDisabling(rule: self) ?? []
|
||||
return identifiers.map { ($0, ruleID) }
|
||||
violations = validate(file: file)
|
||||
ruleTime = nil
|
||||
}
|
||||
|
||||
return LintResult(violations: enabledViolations + superfluousDisableCommandViolations, ruleTime: ruleTime,
|
||||
deprecatedToValidIDPairs: deprecatedToValidIDPairs)
|
||||
return LintResult(violations: violations, ruleTime: ruleTime,
|
||||
deprecatedToValidIDPairs: [])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,6 +164,7 @@ public struct Linter {
|
|||
private let cache: LinterCache?
|
||||
private let configuration: Configuration
|
||||
private let compilerArguments: [String]
|
||||
private let remoteRules: [RemoteRule]
|
||||
|
||||
public var styleViolations: [StyleViolation] {
|
||||
return getStyleViolations().0
|
||||
|
@ -126,7 +186,9 @@ public struct Linter {
|
|||
let superfluousDisableCommandRule = rules.first(where: {
|
||||
$0 is SuperfluousDisableCommandRule
|
||||
}) as? SuperfluousDisableCommandRule
|
||||
let validationResults = rules.parallelCompactMap {
|
||||
|
||||
let lintables = remoteRules.map(LintableBox.remoteRule) + rules.map(LintableBox.rule)
|
||||
let validationResults = lintables.parallelCompactMap {
|
||||
$0.lint(file: self.file, regions: regions, benchmark: benchmark,
|
||||
superfluousDisableCommandRule: superfluousDisableCommandRule,
|
||||
compilerArguments: self.compilerArguments)
|
||||
|
@ -184,6 +246,7 @@ public struct Linter {
|
|||
return rule is AnalyzerRule
|
||||
}
|
||||
}
|
||||
self.remoteRules = configuration.remoteRules
|
||||
}
|
||||
|
||||
public func correct() -> [Correction] {
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
public enum PluginRequiredInput: String, Codable {
|
||||
case syntaxMap = "syntax_map"
|
||||
case structure = "structure"
|
||||
}
|
||||
|
||||
public struct PluginDescription: Equatable, Codable {
|
||||
public let ruleDescription: RuleDescription
|
||||
public let requiredInformation: Set<PluginRequiredInput>
|
||||
|
||||
public init(ruleDescription: RuleDescription,
|
||||
requiredInformation: Set<PluginRequiredInput> = []) {
|
||||
self.ruleDescription = ruleDescription
|
||||
self.requiredInformation = requiredInformation
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
self.ruleDescription = try container.decode(RuleDescription.self, forKey: .ruleDescription)
|
||||
self.requiredInformation = try container.decodeIfPresent(Set<PluginRequiredInput>.self,
|
||||
forKey: .requiredInformation) ?? []
|
||||
}
|
||||
}
|
|
@ -13,22 +13,34 @@ public struct Region: Equatable {
|
|||
return start <= location && end >= location
|
||||
}
|
||||
|
||||
public func isRuleEnabled(_ ruleDescription: RuleDescription) -> Bool {
|
||||
return !isRuleDisabled(ruleDescription)
|
||||
}
|
||||
|
||||
public func isRuleDisabled(_ ruleDescription: RuleDescription) -> Bool {
|
||||
guard !disabledRuleIdentifiers.contains(.all) else {
|
||||
return true
|
||||
}
|
||||
|
||||
let identifiersToCheck = ruleDescription.allIdentifiers
|
||||
let regionIdentifiers = Set(disabledRuleIdentifiers.map { $0.stringRepresentation })
|
||||
return !regionIdentifiers.isDisjoint(with: identifiersToCheck)
|
||||
}
|
||||
|
||||
public func isRuleEnabled(_ rule: Rule) -> Bool {
|
||||
return !isRuleDisabled(rule)
|
||||
}
|
||||
|
||||
public func isRuleDisabled(_ rule: Rule) -> Bool {
|
||||
guard !disabledRuleIdentifiers.contains(.all) else {
|
||||
return true
|
||||
}
|
||||
return isRuleDisabled(type(of: rule).description)
|
||||
}
|
||||
|
||||
let identifiersToCheck = type(of: rule).description.allIdentifiers
|
||||
let regionIdentifiers = Set(disabledRuleIdentifiers.map { $0.stringRepresentation })
|
||||
return !regionIdentifiers.isDisjoint(with: identifiersToCheck)
|
||||
public func deprecatedAliasesDisabling(ruleDescription: RuleDescription) -> Set<String> {
|
||||
let identifiers = ruleDescription.deprecatedAliases
|
||||
return Set(disabledRuleIdentifiers.map { $0.stringRepresentation }).intersection(identifiers)
|
||||
}
|
||||
|
||||
public func deprecatedAliasesDisabling(rule: Rule) -> Set<String> {
|
||||
let identifiers = type(of: rule).description.deprecatedAliases
|
||||
return Set(disabledRuleIdentifiers.map { $0.stringRepresentation }).intersection(identifiers)
|
||||
return deprecatedAliasesDisabling(ruleDescription: type(of: rule).description)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
import Dispatch
|
||||
import Foundation
|
||||
import Socket
|
||||
|
||||
protocol RemoteLintServerDelegate: AnyObject {
|
||||
func server(_ server: RemoteLintServer, didReceivePayload payload: RemoteRulePayload) -> [StyleViolation]
|
||||
func serverStartedListening(_ server: RemoteLintServer)
|
||||
}
|
||||
|
||||
final class RemoteLintServer {
|
||||
private let socketPath: String
|
||||
private var listenSocket: Socket?
|
||||
private var continueRunning = true
|
||||
private var connectedSockets = [Int32: Socket]()
|
||||
private let socketLockQueue = DispatchQueue(label: "io.realm.swiftlint.remoteLintServer")
|
||||
|
||||
weak var delegate: RemoteLintServerDelegate?
|
||||
|
||||
init(socketPath: String) {
|
||||
self.socketPath = socketPath
|
||||
}
|
||||
|
||||
deinit {
|
||||
for socket in connectedSockets.values {
|
||||
socket.close()
|
||||
}
|
||||
listenSocket?.close()
|
||||
}
|
||||
|
||||
func run() {
|
||||
let queue = DispatchQueue.global(qos: .userInteractive)
|
||||
queue.async { [unowned self] in
|
||||
do {
|
||||
let socket = try Socket.create(family: .unix, type: .stream, proto: .unix)
|
||||
socket.readBufferSize = 65_507
|
||||
self.listenSocket = socket
|
||||
|
||||
try socket.listen(on: self.socketPath)
|
||||
|
||||
self.delegate?.serverStartedListening(self)
|
||||
|
||||
repeat {
|
||||
let newSocket = try socket.acceptClientConnection()
|
||||
self.addNewConnection(socket: newSocket)
|
||||
} while self.continueRunning
|
||||
} catch {
|
||||
queuedPrintError(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func addNewConnection(socket: Socket) {
|
||||
socketLockQueue.sync { [unowned self, socket] in
|
||||
self.connectedSockets[socket.socketfd] = socket
|
||||
}
|
||||
|
||||
let queue = DispatchQueue.global(qos: .default)
|
||||
queue.async { [unowned self, socket] in
|
||||
var shouldKeepRunning = true
|
||||
var readData = Data()
|
||||
|
||||
do {
|
||||
repeat {
|
||||
let bytesRead = try socket.read(into: &readData)
|
||||
|
||||
if bytesRead > 0 {
|
||||
guard let json = (try? JSONSerialization.jsonObject(with: readData)) as? [String: Any],
|
||||
let payload = RemoteRulePayload(json: json) else {
|
||||
readData.count = 0
|
||||
break
|
||||
}
|
||||
|
||||
let violations = self.delegate?.server(self, didReceivePayload: payload) ?? []
|
||||
let data = try JSONSerialization.data(withJSONObject: violations.map { $0.toPluginJSON() })
|
||||
try socket.write(from: data)
|
||||
}
|
||||
|
||||
if bytesRead == 0 {
|
||||
shouldKeepRunning = false
|
||||
break
|
||||
}
|
||||
|
||||
readData.count = 0
|
||||
} while shouldKeepRunning
|
||||
|
||||
socket.close()
|
||||
|
||||
self.socketLockQueue.sync { [unowned self, socket] in
|
||||
self.connectedSockets[socket.socketfd] = nil
|
||||
}
|
||||
} catch {
|
||||
queuedPrintError(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func shutdown() {
|
||||
continueRunning = false
|
||||
|
||||
for socket in connectedSockets.values {
|
||||
socket.close()
|
||||
}
|
||||
|
||||
listenSocket?.close()
|
||||
}
|
||||
}
|
||||
|
||||
private extension StyleViolation {
|
||||
func toPluginJSON() -> [String: Any] {
|
||||
return [
|
||||
"severity": severity.rawValue,
|
||||
"location": [
|
||||
"line": location.line ?? 1,
|
||||
"character": location.character ?? 1
|
||||
],
|
||||
"reason": reason
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
import Foundation
|
||||
import Socket
|
||||
import SourceKittenFramework
|
||||
|
||||
public final class RemoteRule {
|
||||
public let description: PluginDescription
|
||||
public let configuration: Any?
|
||||
|
||||
public var ruleDescription: RuleDescription {
|
||||
return description.ruleDescription
|
||||
}
|
||||
|
||||
public init(description: PluginDescription, configuration: Any?) {
|
||||
self.description = description
|
||||
self.configuration = configuration
|
||||
}
|
||||
|
||||
public func validate(file: File) -> [StyleViolation] {
|
||||
let payload = RemoteRulePayload(structure: Lazy(file.structure.dictionary),
|
||||
syntaxMap: Lazy(file.syntaxMap.tokens),
|
||||
path: file.path,
|
||||
contents: Lazy(file.contents),
|
||||
configuration: configuration)
|
||||
return validate(payload: payload, file: file)
|
||||
}
|
||||
|
||||
private func validate(payload: RemoteRulePayload, file: File) -> [StyleViolation] {
|
||||
do {
|
||||
let socket = try Socket.create(family: .unix, type: .stream, proto: .unix)
|
||||
try socket.connect(to: "/tmp/\(ruleDescription.identifier).socket")
|
||||
|
||||
let data = try payload.asJSONData(input: description.requiredInformation)
|
||||
try socket.write(from: data)
|
||||
|
||||
var readData = Data()
|
||||
_ = try socket.read(into: &readData)
|
||||
|
||||
guard let json = try JSONSerialization.jsonObject(with: readData) as? [[String: Any]] else {
|
||||
return []
|
||||
}
|
||||
|
||||
return json.compactMap { dictionary -> StyleViolation? in
|
||||
let severity = (dictionary["severity"] as? String).flatMap(ViolationSeverity.init) ?? .warning
|
||||
guard let location = Location(file: file, json: dictionary) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return StyleViolation(ruleDescription: ruleDescription,
|
||||
severity: severity,
|
||||
location: location,
|
||||
reason: dictionary["reason"] as? String)
|
||||
}
|
||||
} catch {
|
||||
queuedPrintError(error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal extension Array where Element == RemoteRule {
|
||||
var identifiers: [String] {
|
||||
return map { $0.ruleDescription.identifier }
|
||||
}
|
||||
}
|
||||
|
||||
private extension Location {
|
||||
init?(file: File, json: [String: Any]) {
|
||||
if let byteOffset = json["byte_offset"] as? Int {
|
||||
self = Location(file: file, byteOffset: byteOffset)
|
||||
} else if let characterOffset = json["character_offset"] as? Int {
|
||||
self = Location(file: file, characterOffset: characterOffset)
|
||||
} else if let location = json["location"] as? [String: Int],
|
||||
let line = location["line"] {
|
||||
self = Location(file: file.path, line: line, character: location["character"] ?? 1)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
import Foundation
|
||||
import SourceKittenFramework
|
||||
|
||||
internal struct RemoteRulePayload {
|
||||
let structure: Lazy<[String: SourceKitRepresentable]>
|
||||
let syntaxMap: Lazy<[SyntaxToken]>
|
||||
let path: String?
|
||||
let contents: Lazy<String?>
|
||||
let configuration: Any?
|
||||
|
||||
func asJSONData(input: Set<PluginRequiredInput>) throws -> Data {
|
||||
return try JSONSerialization.data(withJSONObject: asJSONDictionary(input: input))
|
||||
}
|
||||
|
||||
func asJSONDictionary(input: Set<PluginRequiredInput>) -> [String: Any] {
|
||||
var json = [String: Any]()
|
||||
if input.contains(.structure) {
|
||||
json["structure"] = structure.value
|
||||
}
|
||||
if input.contains(.syntaxMap) {
|
||||
json["syntax_map"] = syntaxMap.value.map { $0.dictionaryValue }
|
||||
}
|
||||
|
||||
json["path"] = path
|
||||
json["configuration"] = configuration
|
||||
if path == nil {
|
||||
json["contents"] = contents.value
|
||||
}
|
||||
|
||||
return json
|
||||
}
|
||||
}
|
||||
|
||||
extension RemoteRulePayload {
|
||||
init?(json: [String: Any]) {
|
||||
let rawStructure = json["structure"]
|
||||
let structure = rawStructure.map(convertingIntsToInt64) as? [String: SourceKitRepresentable] ?? [:]
|
||||
let syntaxMap = (json["syntax_map"] as? [[String: Any]])?.compactMap(SyntaxToken.init(json:)) ?? []
|
||||
let path = json["path"] as? String
|
||||
let contents = json["contents"] as? String
|
||||
let configuration = json["configuration"]
|
||||
|
||||
if path == nil && contents == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.init(structure: Lazy(structure), syntaxMap: Lazy(syntaxMap),
|
||||
path: path, contents: Lazy(contents), configuration: configuration)
|
||||
}
|
||||
}
|
||||
|
||||
private func convertingIntsToInt64(value: Any) -> Any {
|
||||
switch value {
|
||||
case let value as Int:
|
||||
return Int64(value)
|
||||
case let values as [Any]:
|
||||
return values.map(convertingIntsToInt64)
|
||||
case let values as [String: Any]:
|
||||
return values.mapValues(convertingIntsToInt64)
|
||||
case let value as String:
|
||||
return value
|
||||
case let value as Int64:
|
||||
return value
|
||||
case let value as Bool:
|
||||
return value
|
||||
default:
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
private extension SyntaxToken {
|
||||
init?(json: [String: Any]) {
|
||||
guard let type = json["type"] as? String,
|
||||
let offset = json["offset"] as? Int,
|
||||
let length = json["length"] as? Int else {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.init(type: type, offset: offset, length: length)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
import Foundation
|
||||
|
||||
public protocol RemoteRuleResolverProtocol {
|
||||
func remoteRule(forExecutable executable: String,
|
||||
configuration: [String: Any]?) throws -> RemoteRule
|
||||
}
|
||||
|
||||
public final class RemoteRuleResolver: RemoteRuleResolverProtocol {
|
||||
public init() {}
|
||||
|
||||
public func remoteRule(forExecutable executable: String,
|
||||
configuration: [String: Any]?) throws -> RemoteRule {
|
||||
let task = Process()
|
||||
task.launchPath = executable
|
||||
task.arguments = ["plugin_description"]
|
||||
|
||||
let pipe = Pipe()
|
||||
task.standardOutput = pipe
|
||||
|
||||
task.launch()
|
||||
|
||||
let file = pipe.fileHandleForReading
|
||||
defer { file.closeFile() }
|
||||
|
||||
let decoder = JSONDecoder()
|
||||
decoder.keyDecodingStrategy = .convertFromSnakeCase
|
||||
let description = try decoder.decode(PluginDescription.self, from: file.readDataToEndOfFile())
|
||||
return RemoteRule(description: description,
|
||||
configuration: configuration?[description.ruleDescription.identifier])
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
public struct RuleDescription: Equatable {
|
||||
public struct RuleDescription: Equatable, Codable {
|
||||
public let identifier: String
|
||||
public let name: String
|
||||
public let description: String
|
||||
|
@ -39,4 +39,31 @@ public struct RuleDescription: Equatable {
|
|||
public static func == (lhs: RuleDescription, rhs: RuleDescription) -> Bool {
|
||||
return lhs.identifier == rhs.identifier
|
||||
}
|
||||
|
||||
// MARK: Codable
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
self.identifier = try container.decode(String.self, forKey: .identifier)
|
||||
self.name = try container.decode(String.self, forKey: .name)
|
||||
self.description = try container.decode(String.self, forKey: .description)
|
||||
self.kind = try container.decode(RuleKind.self, forKey: .kind)
|
||||
self.nonTriggeringExamples = container.optionalDecode([String].self, forKey: .nonTriggeringExamples) ?? []
|
||||
self.triggeringExamples = container.optionalDecode([String].self, forKey: .triggeringExamples) ?? []
|
||||
self.corrections = container.optionalDecode([String: String].self, forKey: .corrections) ?? [:]
|
||||
self.deprecatedAliases = container.optionalDecode(Set<String>.self, forKey: .deprecatedAliases) ?? []
|
||||
self.minSwiftVersion = container.optionalDecode(SwiftVersion.self, forKey: .minSwiftVersion) ?? .three
|
||||
self.requiresFileOnDisk = container.optionalDecode(Bool.self, forKey: .requiresFileOnDisk) ?? false
|
||||
}
|
||||
}
|
||||
|
||||
private extension KeyedDecodingContainerProtocol {
|
||||
func optionalDecode<T>(_ type: T.Type, forKey key: KeyedDecodingContainer<Key>.Key) -> T? where T: Decodable {
|
||||
do {
|
||||
return try decodeIfPresent(type, forKey: key)
|
||||
} catch {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
public enum RuleKind: String {
|
||||
public enum RuleKind: String, Codable {
|
||||
case lint
|
||||
case idiomatic
|
||||
case style
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import Foundation
|
||||
import SourceKittenFramework
|
||||
|
||||
public struct SwiftVersion: RawRepresentable {
|
||||
public struct SwiftVersion: RawRepresentable, Codable {
|
||||
public typealias RawValue = String
|
||||
|
||||
public let rawValue: String
|
||||
|
|
|
@ -18,8 +18,8 @@ public struct SuperfluousDisableCommandRule: ConfigurationProviderRule {
|
|||
return []
|
||||
}
|
||||
|
||||
public func reason(for rule: Rule.Type) -> String {
|
||||
return "SwiftLint rule '\(rule.description.identifier)' did not trigger a violation " +
|
||||
public func reason(for ruleDescription: RuleDescription) -> String {
|
||||
return "SwiftLint rule '\(ruleDescription.identifier)' did not trigger a violation " +
|
||||
"in the disabled region. Please remove the disable command."
|
||||
}
|
||||
}
|
||||
|
|
|
@ -124,16 +124,6 @@ public struct LineLengthRule: ConfigurationProviderRule {
|
|||
}
|
||||
}
|
||||
|
||||
// extracted from https://forums.swift.org/t/pitch-declaring-local-variables-as-lazy/9287/3
|
||||
private class Lazy<Result> {
|
||||
private var computation: () -> Result
|
||||
fileprivate private(set) lazy var value: Result = computation()
|
||||
|
||||
init(_ computation: @escaping @autoclosure () -> Result) {
|
||||
self.computation = computation
|
||||
}
|
||||
}
|
||||
|
||||
private extension String {
|
||||
var strippingURLs: String {
|
||||
let range = NSRange(location: 0, length: bridge().length)
|
||||
|
|
|
@ -163,6 +163,16 @@ extension Configuration {
|
|||
return LintableFilesVisitor.create(options, cache: cache, block: visitorBlock).flatMap(visitLintableFiles)
|
||||
}
|
||||
|
||||
func startPlugins() -> [Process] {
|
||||
return plugins.map { executable in
|
||||
let task = Process()
|
||||
task.launchPath = executable
|
||||
task.arguments = ["lint"]
|
||||
task.launch()
|
||||
return task
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: AutoCorrect Command
|
||||
|
||||
init(options: AutoCorrectOptions) {
|
||||
|
|
|
@ -27,6 +27,8 @@ struct LintOrAnalyzeCommand {
|
|||
let reporter = reporterFrom(optionsReporter: options.reporter, configuration: configuration)
|
||||
let cache = options.ignoreCache ? nil : LinterCache(configuration: configuration)
|
||||
let visitorMutationQueue = DispatchQueue(label: "io.realm.swiftlint.lintVisitorMutation")
|
||||
let pluginsProcesses = configuration.startPlugins()
|
||||
|
||||
return configuration.visitLintableFiles(options: options, cache: cache) { linter in
|
||||
let currentViolations: [StyleViolation]
|
||||
if options.benchmark {
|
||||
|
@ -47,8 +49,7 @@ struct LintOrAnalyzeCommand {
|
|||
linter.file.invalidateCache()
|
||||
reporter.report(violations: currentViolations, realtimeCondition: true)
|
||||
}.flatMap { files in
|
||||
if isWarningThresholdBroken(configuration: configuration, violations: violations)
|
||||
&& !options.lenient {
|
||||
if isWarningThresholdBroken(configuration: configuration, violations: violations) && !options.lenient {
|
||||
violations.append(createThresholdViolation(threshold: configuration.warningThreshold!))
|
||||
reporter.report(violations: [violations.last!], realtimeCondition: true)
|
||||
}
|
||||
|
@ -63,6 +64,7 @@ struct LintOrAnalyzeCommand {
|
|||
ruleBenchmark.save()
|
||||
}
|
||||
try? cache?.save()
|
||||
pluginsProcesses.forEach { $0.terminate() }
|
||||
return successOrExit(numberOfSeriousViolations: numberOfSeriousViolations,
|
||||
strictWithViolations: options.strict && !violations.isEmpty)
|
||||
}
|
||||
|
|
|
@ -224,6 +224,8 @@
|
|||
D4130D991E16CC1300242361 /* TypeNameRuleExamples.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4130D981E16CC1300242361 /* TypeNameRuleExamples.swift */; };
|
||||
D414D6AC21D0B77F00960935 /* DiscouragedObjectLiteralRuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D414D6AB21D0B77F00960935 /* DiscouragedObjectLiteralRuleTests.swift */; };
|
||||
D414D6AE21D22FF500960935 /* LastWhereRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D414D6AD21D22FF500960935 /* LastWhereRule.swift */; };
|
||||
D414D6B021D34FA500960935 /* RemoteRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D414D6AF21D34FA500960935 /* RemoteRule.swift */; };
|
||||
D414D6B221D3509B00960935 /* RemoteRuleResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = D414D6B121D3509B00960935 /* RemoteRuleResolver.swift */; };
|
||||
D41B57781ED8CEE0007B0470 /* ExtensionAccessModifierRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D41B57771ED8CEE0007B0470 /* ExtensionAccessModifierRule.swift */; };
|
||||
D41E7E0B1DF9DABB0065259A /* RedundantStringEnumValueRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D41E7E0A1DF9DABB0065259A /* RedundantStringEnumValueRule.swift */; };
|
||||
D4246D6D1F30D8620097E658 /* PrivateOverFilePrivateRuleConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4246D6C1F30D8620097E658 /* PrivateOverFilePrivateRuleConfiguration.swift */; };
|
||||
|
@ -236,6 +238,7 @@
|
|||
D43B04661E071ED3004016AF /* ColonRuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D43B04651E071ED3004016AF /* ColonRuleTests.swift */; };
|
||||
D43B04691E072291004016AF /* ColonConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D43B04671E07228D004016AF /* ColonConfiguration.swift */; };
|
||||
D43B046B1E075905004016AF /* ClosureEndIndentationRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D43B046A1E075905004016AF /* ClosureEndIndentationRule.swift */; };
|
||||
D43CD01721D4AFD200944FAC /* Socket.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D43CD01621D4AFD200944FAC /* Socket.framework */; };
|
||||
D43DB1081DC573DA00281215 /* ImplicitGetterRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D43DB1071DC573DA00281215 /* ImplicitGetterRule.swift */; };
|
||||
D44037972132730000FDA77B /* ProhibitedInterfaceBuilderRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D44037962132730000FDA77B /* ProhibitedInterfaceBuilderRule.swift */; };
|
||||
D44254201DB87CA200492EA4 /* ValidIBInspectableRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D442541E1DB87C3D00492EA4 /* ValidIBInspectableRule.swift */; };
|
||||
|
@ -256,6 +259,10 @@
|
|||
D47079AB1DFDCF7A00027086 /* SwiftExpressionKind.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47079AA1DFDCF7A00027086 /* SwiftExpressionKind.swift */; };
|
||||
D47079AD1DFE2FA700027086 /* EmptyParametersRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47079AC1DFE2FA700027086 /* EmptyParametersRule.swift */; };
|
||||
D47079AF1DFE520000027086 /* VoidReturnRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47079AE1DFE520000027086 /* VoidReturnRule.swift */; };
|
||||
D47820C621D8B3ED00F24836 /* ConfigurationTests+Plugins.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47820C521D8B3ED00F24836 /* ConfigurationTests+Plugins.swift */; };
|
||||
D47820C821D8CBC000F24836 /* PluginDescription.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47820C721D8CBC000F24836 /* PluginDescription.swift */; };
|
||||
D47820CA21D8CD2C00F24836 /* Lazy.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47820C921D8CD2C00F24836 /* Lazy.swift */; };
|
||||
D47820CD21D9FA7100F24836 /* RemoteRuleResolverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47820CB21D9FA5B00F24836 /* RemoteRuleResolverTests.swift */; };
|
||||
D47A510E1DB29EEB00A4CC21 /* SwitchCaseOnNewlineRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47A510D1DB29EEB00A4CC21 /* SwitchCaseOnNewlineRule.swift */; };
|
||||
D47A51101DB2DD4800A4CC21 /* AttributesRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47A510F1DB2DD4800A4CC21 /* AttributesRule.swift */; };
|
||||
D47EF4801F69E3100012C4CA /* ColonRule+FunctionCall.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47EF47F1F69E3100012C4CA /* ColonRule+FunctionCall.swift */; };
|
||||
|
@ -267,6 +274,9 @@
|
|||
D48B51231F4F5E4B0068AB98 /* DocumentationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D48B51221F4F5E4B0068AB98 /* DocumentationTests.swift */; };
|
||||
D495B1A221165DAA00E2CD7B /* FileNameRuleFixtures in Resources */ = {isa = PBXBuildFile; fileRef = D495B1A021165DAA00E2CD7B /* FileNameRuleFixtures */; };
|
||||
D495B1A321165DAA00E2CD7B /* FileHeaderRuleFixtures in Resources */ = {isa = PBXBuildFile; fileRef = D495B1A121165DAA00E2CD7B /* FileHeaderRuleFixtures */; };
|
||||
D496009221DF32D700520803 /* RemoteLintServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D496009121DF32D700520803 /* RemoteLintServer.swift */; };
|
||||
D496009421DF376B00520803 /* RemoteRulePayload.swift in Sources */ = {isa = PBXBuildFile; fileRef = D496009321DF376B00520803 /* RemoteRulePayload.swift */; };
|
||||
D496009621DF39DF00520803 /* RemoteRuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D496009521DF39DF00520803 /* RemoteRuleTests.swift */; };
|
||||
D49896F12026B36C00814A83 /* RedundantSetAccessControlRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D49896F02026B36C00814A83 /* RedundantSetAccessControlRule.swift */; };
|
||||
D4998DE71DF191380006E05D /* AttributesRuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4998DE61DF191380006E05D /* AttributesRuleTests.swift */; };
|
||||
D4998DE91DF194F20006E05D /* FileHeaderRuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4998DE81DF194F20006E05D /* FileHeaderRuleTests.swift */; };
|
||||
|
@ -288,6 +298,7 @@
|
|||
D4C889711E385B7B00BAE88D /* RedundantDiscardableLetRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4C889701E385B7B00BAE88D /* RedundantDiscardableLetRule.swift */; };
|
||||
D4CA758F1E2DEEA500A40E8A /* NumberSeparatorRuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4CA758E1E2DEEA500A40E8A /* NumberSeparatorRuleTests.swift */; };
|
||||
D4CFC5D2209EC95A00668488 /* FunctionDefaultParameterAtEndRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4CFC5D1209EC95A00668488 /* FunctionDefaultParameterAtEndRule.swift */; };
|
||||
D4D11B1D21D8AE2300FB9D93 /* RuleDescriptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4D11B1C21D8AE2300FB9D93 /* RuleDescriptionTests.swift */; };
|
||||
D4D1B9BB1EAC2C910028BE6A /* AccessControlLevel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4D1B9B91EAC2C870028BE6A /* AccessControlLevel.swift */; };
|
||||
D4D383852145F550000235BD /* StaticOperatorRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4D383842145F550000235BD /* StaticOperatorRule.swift */; };
|
||||
D4D5A5FF1E1F3A1C00D15E0C /* ShorthandOperatorRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4D5A5FE1E1F3A1C00D15E0C /* ShorthandOperatorRule.swift */; };
|
||||
|
@ -678,6 +689,8 @@
|
|||
D4130D981E16CC1300242361 /* TypeNameRuleExamples.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TypeNameRuleExamples.swift; sourceTree = "<group>"; };
|
||||
D414D6AB21D0B77F00960935 /* DiscouragedObjectLiteralRuleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiscouragedObjectLiteralRuleTests.swift; sourceTree = "<group>"; };
|
||||
D414D6AD21D22FF500960935 /* LastWhereRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LastWhereRule.swift; sourceTree = "<group>"; };
|
||||
D414D6AF21D34FA500960935 /* RemoteRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteRule.swift; sourceTree = "<group>"; };
|
||||
D414D6B121D3509B00960935 /* RemoteRuleResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteRuleResolver.swift; sourceTree = "<group>"; };
|
||||
D41B57771ED8CEE0007B0470 /* ExtensionAccessModifierRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExtensionAccessModifierRule.swift; sourceTree = "<group>"; };
|
||||
D41E7E0A1DF9DABB0065259A /* RedundantStringEnumValueRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RedundantStringEnumValueRule.swift; sourceTree = "<group>"; };
|
||||
D4246D6C1F30D8620097E658 /* PrivateOverFilePrivateRuleConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrivateOverFilePrivateRuleConfiguration.swift; sourceTree = "<group>"; };
|
||||
|
@ -690,6 +703,7 @@
|
|||
D43B04651E071ED3004016AF /* ColonRuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColonRuleTests.swift; sourceTree = "<group>"; };
|
||||
D43B04671E07228D004016AF /* ColonConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColonConfiguration.swift; sourceTree = "<group>"; };
|
||||
D43B046A1E075905004016AF /* ClosureEndIndentationRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClosureEndIndentationRule.swift; sourceTree = "<group>"; };
|
||||
D43CD01621D4AFD200944FAC /* Socket.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Socket.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D43DB1071DC573DA00281215 /* ImplicitGetterRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImplicitGetterRule.swift; sourceTree = "<group>"; };
|
||||
D44037962132730000FDA77B /* ProhibitedInterfaceBuilderRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProhibitedInterfaceBuilderRule.swift; sourceTree = "<group>"; };
|
||||
D442541E1DB87C3D00492EA4 /* ValidIBInspectableRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidIBInspectableRule.swift; sourceTree = "<group>"; };
|
||||
|
@ -710,6 +724,10 @@
|
|||
D47079AA1DFDCF7A00027086 /* SwiftExpressionKind.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftExpressionKind.swift; sourceTree = "<group>"; };
|
||||
D47079AC1DFE2FA700027086 /* EmptyParametersRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmptyParametersRule.swift; sourceTree = "<group>"; };
|
||||
D47079AE1DFE520000027086 /* VoidReturnRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VoidReturnRule.swift; sourceTree = "<group>"; };
|
||||
D47820C521D8B3ED00F24836 /* ConfigurationTests+Plugins.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ConfigurationTests+Plugins.swift"; sourceTree = "<group>"; };
|
||||
D47820C721D8CBC000F24836 /* PluginDescription.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginDescription.swift; sourceTree = "<group>"; };
|
||||
D47820C921D8CD2C00F24836 /* Lazy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Lazy.swift; sourceTree = "<group>"; };
|
||||
D47820CB21D9FA5B00F24836 /* RemoteRuleResolverTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteRuleResolverTests.swift; sourceTree = "<group>"; };
|
||||
D47A510D1DB29EEB00A4CC21 /* SwitchCaseOnNewlineRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwitchCaseOnNewlineRule.swift; sourceTree = "<group>"; };
|
||||
D47A510F1DB2DD4800A4CC21 /* AttributesRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttributesRule.swift; sourceTree = "<group>"; };
|
||||
D47EF47F1F69E3100012C4CA /* ColonRule+FunctionCall.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ColonRule+FunctionCall.swift"; sourceTree = "<group>"; };
|
||||
|
@ -721,6 +739,9 @@
|
|||
D48B51221F4F5E4B0068AB98 /* DocumentationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DocumentationTests.swift; sourceTree = "<group>"; };
|
||||
D495B1A021165DAA00E2CD7B /* FileNameRuleFixtures */ = {isa = PBXFileReference; lastKnownFileType = folder; path = FileNameRuleFixtures; sourceTree = "<group>"; };
|
||||
D495B1A121165DAA00E2CD7B /* FileHeaderRuleFixtures */ = {isa = PBXFileReference; lastKnownFileType = folder; path = FileHeaderRuleFixtures; sourceTree = "<group>"; };
|
||||
D496009121DF32D700520803 /* RemoteLintServer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteLintServer.swift; sourceTree = "<group>"; };
|
||||
D496009321DF376B00520803 /* RemoteRulePayload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteRulePayload.swift; sourceTree = "<group>"; };
|
||||
D496009521DF39DF00520803 /* RemoteRuleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteRuleTests.swift; sourceTree = "<group>"; };
|
||||
D49896F02026B36C00814A83 /* RedundantSetAccessControlRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RedundantSetAccessControlRule.swift; sourceTree = "<group>"; };
|
||||
D4998DE61DF191380006E05D /* AttributesRuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttributesRuleTests.swift; sourceTree = "<group>"; };
|
||||
D4998DE81DF194F20006E05D /* FileHeaderRuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileHeaderRuleTests.swift; sourceTree = "<group>"; };
|
||||
|
@ -742,6 +763,7 @@
|
|||
D4C889701E385B7B00BAE88D /* RedundantDiscardableLetRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RedundantDiscardableLetRule.swift; sourceTree = "<group>"; };
|
||||
D4CA758E1E2DEEA500A40E8A /* NumberSeparatorRuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NumberSeparatorRuleTests.swift; sourceTree = "<group>"; };
|
||||
D4CFC5D1209EC95A00668488 /* FunctionDefaultParameterAtEndRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FunctionDefaultParameterAtEndRule.swift; sourceTree = "<group>"; };
|
||||
D4D11B1C21D8AE2300FB9D93 /* RuleDescriptionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleDescriptionTests.swift; sourceTree = "<group>"; };
|
||||
D4D1B9B91EAC2C870028BE6A /* AccessControlLevel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccessControlLevel.swift; sourceTree = "<group>"; };
|
||||
D4D383842145F550000235BD /* StaticOperatorRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StaticOperatorRule.swift; sourceTree = "<group>"; };
|
||||
D4D5A5FE1E1F3A1C00D15E0C /* ShorthandOperatorRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShorthandOperatorRule.swift; sourceTree = "<group>"; };
|
||||
|
@ -855,6 +877,7 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D43CD01721D4AFD200944FAC /* Socket.framework in Frameworks */,
|
||||
E876BFBE1B07828500114ED5 /* SourceKittenFramework.framework in Frameworks */,
|
||||
E8C0DFCD1AD349DB007EE3D4 /* SWXMLHash.framework in Frameworks */,
|
||||
3BBF2F9D1C640A0F006CD775 /* SwiftyTextTable.framework in Frameworks */,
|
||||
|
@ -1190,6 +1213,7 @@
|
|||
D0D1211A19E87861005E4BAA /* swiftlint */,
|
||||
D0D1216E19E87B05005E4BAA /* SwiftLintFramework */,
|
||||
D0D1217B19E87B05005E4BAA /* SwiftLintFrameworkTests */,
|
||||
D43CD01521D4AFD200944FAC /* Frameworks */,
|
||||
);
|
||||
indentWidth = 4;
|
||||
sourceTree = "<group>";
|
||||
|
@ -1329,6 +1353,7 @@
|
|||
C2B3C15F2106F78100088928 /* ConfigurationAliasesTests.swift */,
|
||||
E809EDA21B8A73FB00399043 /* ConfigurationTests.swift */,
|
||||
F480DC7E1F26090000099465 /* ConfigurationTests+Nested.swift */,
|
||||
D47820C521D8B3ED00F24836 /* ConfigurationTests+Plugins.swift */,
|
||||
F480DC821F2609D700099465 /* ConfigurationTests+ProjectMock.swift */,
|
||||
3BB47D861C51DE6E00AE6A10 /* CustomRulesTests.swift */,
|
||||
67932E2C1E54AF4B00CB0629 /* CyclomaticComplexityConfigurationTests.swift */,
|
||||
|
@ -1363,11 +1388,14 @@
|
|||
D4F5851820E99B5A0085C6D8 /* PrivateOutletRuleTests.swift */,
|
||||
D4246D6E1F30DB260097E658 /* PrivateOverFilePrivateRuleTests.swift */,
|
||||
E81ADD711ED5ED9D000CD451 /* RegionTests.swift */,
|
||||
D47820CB21D9FA5B00F24836 /* RemoteRuleResolverTests.swift */,
|
||||
D496009521DF39DF00520803 /* RemoteRuleTests.swift */,
|
||||
E86396C61BADAFE6002C9E88 /* ReporterTests.swift */,
|
||||
B89F3BCB1FD5EDA900931E59 /* RequiredEnumCaseRuleTestCase.swift */,
|
||||
3B12C9BE1C3209AC000B423F /* Resources */,
|
||||
3BCC04D31C502BAB006073C3 /* RuleConfigurationTests.swift */,
|
||||
D45255C71F0932F8003C9B56 /* RuleDescription+Examples.swift */,
|
||||
D4D11B1C21D8AE2300FB9D93 /* RuleDescriptionTests.swift */,
|
||||
E8BB8F9B1B17DE3B00199606 /* RulesTests.swift */,
|
||||
3B12C9C61C3361CB000B423F /* RuleTests.swift */,
|
||||
6C7045431C6ADA450003F15A /* SourceKitCrashTests.swift */,
|
||||
|
@ -1399,6 +1427,14 @@
|
|||
path = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D43CD01521D4AFD200944FAC /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D43CD01621D4AFD200944FAC /* Socket.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
E802ECFE1C56A54600A35AE1 /* Helpers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -1475,10 +1511,12 @@
|
|||
3BCC04CC1C4F5694006073C3 /* ConfigurationError.swift */,
|
||||
E8B67C3D1C095E6300FDED8E /* Correction.swift */,
|
||||
E812249B1B04FADC001783D2 /* Linter.swift */,
|
||||
D47820C921D8CD2C00F24836 /* Lazy.swift */,
|
||||
D4FD58B11E12A0200019503C /* LinterCache.swift */,
|
||||
E88DEA6E1B09843F00A66CB0 /* Location.swift */,
|
||||
3B12C9C41C322032000B423F /* MasterRuleList.swift */,
|
||||
E80E018E1B92C1350078EB70 /* Region.swift */,
|
||||
D47820C721D8CBC000F24836 /* PluginDescription.swift */,
|
||||
83D71E261B131EB5000395DE /* RuleDescription.swift */,
|
||||
CC26ED05204DE86E0013BBBC /* RuleIdentifier.swift */,
|
||||
E8BDE3FE1EDF91B6002EC12F /* RuleList.swift */,
|
||||
|
@ -1490,6 +1528,10 @@
|
|||
D4C27BFD1E12D53F00DF713E /* Version.swift */,
|
||||
E88DEA701B09847500A66CB0 /* ViolationSeverity.swift */,
|
||||
3BD9CD3C1C37175B009A5D25 /* YamlParser.swift */,
|
||||
D414D6AF21D34FA500960935 /* RemoteRule.swift */,
|
||||
D414D6B121D3509B00960935 /* RemoteRuleResolver.swift */,
|
||||
D496009121DF32D700520803 /* RemoteLintServer.swift */,
|
||||
D496009321DF376B00520803 /* RemoteRulePayload.swift */,
|
||||
);
|
||||
path = Models;
|
||||
sourceTree = "<group>";
|
||||
|
@ -1831,6 +1873,7 @@
|
|||
BFF028AE1CBCF8A500B38A9D /* TrailingWhitespaceConfiguration.swift in Sources */,
|
||||
3B034B6E1E0BE549005D49A9 /* LineLengthConfiguration.swift in Sources */,
|
||||
B25DCD0C1F7E9FA20028A199 /* MultilineArgumentsRule.swift in Sources */,
|
||||
D414D6B021D34FA500960935 /* RemoteRule.swift in Sources */,
|
||||
6258783B1FFC458100AC34F2 /* DiscouragedObjectLiteralRule.swift in Sources */,
|
||||
62A3E95D209E084000547A86 /* EmptyXCTestMethodRule.swift in Sources */,
|
||||
D4C4A34C1DEA4FF000E0E04C /* AttributesConfiguration.swift in Sources */,
|
||||
|
@ -1862,12 +1905,14 @@
|
|||
D4DA1DFE1E1A10DB0037413D /* NumberSeparatorConfiguration.swift in Sources */,
|
||||
E88198601BEA98F000333A11 /* IdentifierNameRule.swift in Sources */,
|
||||
E88DEA791B098D4400A66CB0 /* RuleParameter.swift in Sources */,
|
||||
D496009221DF32D700520803 /* RemoteLintServer.swift in Sources */,
|
||||
8F715B83213B528B00427BD9 /* UnusedImportRule.swift in Sources */,
|
||||
626D02971F31CBCC0054788D /* XCTFailMessageRule.swift in Sources */,
|
||||
D4DA1DFA1E18D6200037413D /* LargeTupleRule.swift in Sources */,
|
||||
D4B022A41E105636007E5297 /* GenericTypeNameRule.swift in Sources */,
|
||||
E86396CB1BADB519002C9E88 /* CSVReporter.swift in Sources */,
|
||||
37B3FA8B1DFD45A700AD30D2 /* Dictionary+SwiftLint.swift in Sources */,
|
||||
D47820CA21D8CD2C00F24836 /* Lazy.swift in Sources */,
|
||||
823EDC6221020D850070B7CD /* MultilineLiteralBracketsRule.swift in Sources */,
|
||||
D47EF4801F69E3100012C4CA /* ColonRule+FunctionCall.swift in Sources */,
|
||||
E88198561BEA94D800333A11 /* FileLengthRule.swift in Sources */,
|
||||
|
@ -1907,6 +1952,7 @@
|
|||
C3EF547821B5A4000009262F /* LegacyHashingRule.swift in Sources */,
|
||||
31F1B6CC1F60BF4500A57456 /* SwitchCaseAlignmentRule.swift in Sources */,
|
||||
E88DEA731B0984C400A66CB0 /* String+SwiftLint.swift in Sources */,
|
||||
D496009421DF376B00520803 /* RemoteRulePayload.swift in Sources */,
|
||||
E88198591BEA95F100333A11 /* LeadingWhitespaceRule.swift in Sources */,
|
||||
D42B45D91F0AF5E30086B683 /* StrictFilePrivateRule.swift in Sources */,
|
||||
1EC163521D5992D900DD2928 /* VerticalWhitespaceRule.swift in Sources */,
|
||||
|
@ -2008,10 +2054,12 @@
|
|||
CE8178ED1EAC039D0063186E /* UnusedOptionalBindingConfiguration.swift in Sources */,
|
||||
62DEA1661FB21A9E00BCCCC6 /* PrivateActionRule.swift in Sources */,
|
||||
D4FD58B21E12A0200019503C /* LinterCache.swift in Sources */,
|
||||
D47820C821D8CBC000F24836 /* PluginDescription.swift in Sources */,
|
||||
3BD9CD3D1C37175B009A5D25 /* YamlParser.swift in Sources */,
|
||||
F22314B01D4FA4D7009AD165 /* LegacyNSGeometryFunctionsRule.swift in Sources */,
|
||||
E88DEA8C1B0999A000A66CB0 /* ASTRule.swift in Sources */,
|
||||
62426A032118BA6E007E6340 /* ClosureBodyLengthRule.swift in Sources */,
|
||||
D414D6B221D3509B00960935 /* RemoteRuleResolver.swift in Sources */,
|
||||
1E82D5591D7775C7009553D7 /* ClosureSpacingRule.swift in Sources */,
|
||||
E80746F61ECB722F00548D31 /* CacheDescriptionProvider.swift in Sources */,
|
||||
094385041D5D4F7C009168CF /* PrivateOutletRule.swift in Sources */,
|
||||
|
@ -2085,6 +2133,7 @@
|
|||
D4C27C001E12DFF500DF713E /* LinterCacheTests.swift in Sources */,
|
||||
D45255C81F0932F8003C9B56 /* RuleDescription+Examples.swift in Sources */,
|
||||
E81ADD721ED5ED9D000CD451 /* RegionTests.swift in Sources */,
|
||||
D4D11B1D21D8AE2300FB9D93 /* RuleDescriptionTests.swift in Sources */,
|
||||
D4998DE91DF194F20006E05D /* FileHeaderRuleTests.swift in Sources */,
|
||||
750BBD0B214180AF007EC437 /* CollectionAlignmentRuleTests.swift in Sources */,
|
||||
8B01E50220A4349100C9233E /* FunctionParameterCountRuleTests.swift in Sources */,
|
||||
|
@ -2092,6 +2141,7 @@
|
|||
E81ADD741ED6052F000CD451 /* CommandTests.swift in Sources */,
|
||||
29FFC37D1F157BDE007E4825 /* FileLengthRuleTests.swift in Sources */,
|
||||
006204DE1E1E4E0A00FFFBE1 /* VerticalWhitespaceRuleTests.swift in Sources */,
|
||||
D47820CD21D9FA7100F24836 /* RemoteRuleResolverTests.swift in Sources */,
|
||||
02FD8AEF1BFC18D60014BFFB /* ExtendedNSStringTests.swift in Sources */,
|
||||
12E3D4DC2042729300B3E30E /* ExplicitTypeInterfaceRuleTests.swift in Sources */,
|
||||
D48B51231F4F5E4B0068AB98 /* DocumentationTests.swift in Sources */,
|
||||
|
@ -2103,6 +2153,7 @@
|
|||
6C7045441C6ADA450003F15A /* SourceKitCrashTests.swift in Sources */,
|
||||
820F451E21073D7200AA056A /* ConditionalReturnsOnNewlineRuleTests.swift in Sources */,
|
||||
D4246D6F1F30DB260097E658 /* PrivateOverFilePrivateRuleTests.swift in Sources */,
|
||||
D496009621DF39DF00520803 /* RemoteRuleTests.swift in Sources */,
|
||||
B25DCD101F7EF6DC0028A199 /* MultilineArgumentsRuleTests.swift in Sources */,
|
||||
3BB47D871C51DE6E00AE6A10 /* CustomRulesTests.swift in Sources */,
|
||||
E812249A1B04F85B001783D2 /* TestHelpers.swift in Sources */,
|
||||
|
@ -2135,6 +2186,7 @@
|
|||
D4470D5B1EB76F44008A1B2E /* UnusedOptionalBindingRuleTests.swift in Sources */,
|
||||
787CDE3B208F9C34005F3D2F /* SwitchCaseAlignmentRuleTests.swift in Sources */,
|
||||
F480DC811F2609AB00099465 /* XCTestCase+BundlePath.swift in Sources */,
|
||||
D47820C621D8B3ED00F24836 /* ConfigurationTests+Plugins.swift in Sources */,
|
||||
B89F3BCE1FD5EE0200931E59 /* RequiredEnumCaseRuleTestCase.swift in Sources */,
|
||||
C9802F2F1E0C8AEE008AB27F /* TrailingCommaRuleTests.swift in Sources */,
|
||||
3B63D46F1E1F09DF0057BE35 /* LineLengthRuleTests.swift in Sources */,
|
||||
|
@ -2186,7 +2238,7 @@
|
|||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = D0D1212619E878CC005E4BAA /* Debug.xcconfig */;
|
||||
buildSettings = {
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
||||
SWIFT_VERSION = 4.0;
|
||||
};
|
||||
name = Debug;
|
||||
|
@ -2195,7 +2247,7 @@
|
|||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = D0D1212819E878CC005E4BAA /* Release.xcconfig */;
|
||||
buildSettings = {
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
||||
SWIFT_VERSION = 4.0;
|
||||
};
|
||||
name = Release;
|
||||
|
@ -2272,7 +2324,7 @@
|
|||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = D0D1212719E878CC005E4BAA /* Profile.xcconfig */;
|
||||
buildSettings = {
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
||||
SWIFT_VERSION = 4.0;
|
||||
};
|
||||
name = Profile;
|
||||
|
@ -2315,7 +2367,7 @@
|
|||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = D0D1212919E878CC005E4BAA /* Test.xcconfig */;
|
||||
buildSettings = {
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
||||
SWIFT_VERSION = 4.0;
|
||||
};
|
||||
name = Test;
|
||||
|
|
|
@ -22,4 +22,7 @@
|
|||
<FileRef
|
||||
location = "group:Carthage/Checkouts/Yams/Yams.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Carthage/Checkouts/BlueSocket/BlueSocket.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
|
|
|
@ -6,9 +6,10 @@ Pod::Spec.new do |s|
|
|||
s.source = { :git => s.homepage + '.git', :tag => s.version }
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
s.author = { 'JP Simard' => 'jp@jpsim.com' }
|
||||
s.platform = :osx, '10.10'
|
||||
s.platform = :osx, '10.11'
|
||||
s.source_files = 'Source/SwiftLintFramework/**/*.swift'
|
||||
s.pod_target_xcconfig = { 'APPLICATION_EXTENSION_API_ONLY' => 'YES' }
|
||||
s.dependency 'SourceKittenFramework', '~> 0.22'
|
||||
s.dependency 'Yams', '~> 1.0'
|
||||
s.dependency 'BlueSocket', '~> 1.0.43'
|
||||
end
|
||||
|
|
|
@ -168,6 +168,10 @@ extension ConfigurationTests {
|
|||
("testIndentationFallback", testIndentationFallback),
|
||||
("testConfiguresCorrectlyFromDict", testConfiguresCorrectlyFromDict),
|
||||
("testConfigureFallsBackCorrectly", testConfigureFallsBackCorrectly),
|
||||
("testLoadsPlugins", testLoadsPlugins),
|
||||
("testEnableAllRulesConfigurationWithPlugins", testEnableAllRulesConfigurationWithPlugins),
|
||||
("testWhitelistRulesWithPlugins", testWhitelistRulesWithPlugins),
|
||||
("testDisabledRulesWithPlugins", testDisabledRulesWithPlugins),
|
||||
("testMerge", testMerge),
|
||||
("testLevel0", testLevel0),
|
||||
("testLevel1", testLevel1),
|
||||
|
@ -1002,6 +1006,18 @@ extension RegionTests {
|
|||
]
|
||||
}
|
||||
|
||||
extension RemoteRuleResolverTests {
|
||||
static var allTests: [(String, (RemoteRuleResolverTests) -> () throws -> Void)] = [
|
||||
("testCreatesRemoteRule", testCreatesRemoteRule)
|
||||
]
|
||||
}
|
||||
|
||||
extension RemoteRuleTests {
|
||||
static var allTests: [(String, (RemoteRuleTests) -> () throws -> Void)] = [
|
||||
("testValidate", testValidate)
|
||||
]
|
||||
}
|
||||
|
||||
extension ReporterTests {
|
||||
static var allTests: [(String, (ReporterTests) -> () throws -> Void)] = [
|
||||
("testReporterFromString", testReporterFromString),
|
||||
|
@ -1071,6 +1087,12 @@ extension RuleConfigurationTests {
|
|||
]
|
||||
}
|
||||
|
||||
extension RuleDescriptionTests {
|
||||
static var allTests: [(String, (RuleDescriptionTests) -> () throws -> Void)] = [
|
||||
("testCodableWithMissingValues", testCodableWithMissingValues)
|
||||
]
|
||||
}
|
||||
|
||||
extension RuleTests {
|
||||
static var allTests: [(String, (RuleTests) -> () throws -> Void)] = [
|
||||
("testRuleIsEqualTo", testRuleIsEqualTo),
|
||||
|
@ -1518,10 +1540,13 @@ XCTMain([
|
|||
testCase(RedundantTypeAnnotationRuleTests.allTests),
|
||||
testCase(RedundantVoidReturnRuleTests.allTests),
|
||||
testCase(RegionTests.allTests),
|
||||
testCase(RemoteRuleResolverTests.allTests),
|
||||
testCase(RemoteRuleTests.allTests),
|
||||
testCase(ReporterTests.allTests),
|
||||
testCase(RequiredEnumCaseRuleTestCase.allTests),
|
||||
testCase(ReturnArrowWhitespaceRuleTests.allTests),
|
||||
testCase(RuleConfigurationTests.allTests),
|
||||
testCase(RuleDescriptionTests.allTests),
|
||||
testCase(RuleTests.allTests),
|
||||
testCase(RulesTests.allTests),
|
||||
testCase(ShorthandOperatorRuleTests.allTests),
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
@testable import SwiftLintFramework
|
||||
import XCTest
|
||||
|
||||
extension ConfigurationTests {
|
||||
func testLoadsPlugins() {
|
||||
let configurationJSON = ["plugins": ["path/to/plugin"], "test": 10] as [String: Any]
|
||||
let resolver = ResolverMock()
|
||||
let configuration = Configuration(dict: configurationJSON, remoteRulesResolver: resolver)!
|
||||
|
||||
XCTAssertEqual(configuration.plugins, ["path/to/plugin"])
|
||||
XCTAssertEqual(configuration.remoteRules.map { $0.ruleDescription.identifier }, ["test"])
|
||||
XCTAssertEqual(resolver.executable, "path/to/plugin")
|
||||
XCTAssertEqual(resolver.configuration?.bridge(), configurationJSON.bridge())
|
||||
}
|
||||
|
||||
func testEnableAllRulesConfigurationWithPlugins() {
|
||||
let configuration = Configuration(dict: ["plugins": ["path/to/plugin", "path/to/mock"]],
|
||||
ruleList: masterRuleList,
|
||||
enableAllRules: true, cachePath: nil,
|
||||
remoteRulesResolver: ResolverMock())!
|
||||
XCTAssertEqual(configuration.rules.count, masterRuleList.list.count)
|
||||
XCTAssertEqual(configuration.remoteRules.count, 2)
|
||||
}
|
||||
|
||||
func testWhitelistRulesWithPlugins() {
|
||||
let config = Configuration(dict: ["whitelist_rules": ["nesting", "test"],
|
||||
"plugins": ["path/to/plugin",
|
||||
"path/to/mock"]],
|
||||
remoteRulesResolver: ResolverMock())!
|
||||
let configuredIdentifiers = config.rules.map {
|
||||
type(of: $0).description.identifier
|
||||
}.sorted()
|
||||
|
||||
XCTAssertEqual(["nesting"], configuredIdentifiers)
|
||||
XCTAssertEqual(["test"], config.remoteRules.map { $0.ruleDescription.identifier })
|
||||
}
|
||||
|
||||
func testDisabledRulesWithPlugins() {
|
||||
let resolver = ResolverMock()
|
||||
let disabledConfig = Configuration(dict: ["disabled_rules": ["nesting", "test"],
|
||||
"plugins": ["path/to/plugin",
|
||||
"path/to/mock"]],
|
||||
remoteRulesResolver: resolver)!
|
||||
XCTAssertEqual(disabledConfig.disabledRules, ["nesting"])
|
||||
let expectedIdentifiers = Set(masterRuleList.list.keys
|
||||
.filter({ !(["nesting" ] + optInRules).contains($0) }))
|
||||
let configuredIdentifiers = Set(disabledConfig.rules.map {
|
||||
type(of: $0).description.identifier
|
||||
})
|
||||
|
||||
XCTAssertEqual(expectedIdentifiers, configuredIdentifiers)
|
||||
XCTAssertEqual(disabledConfig.remoteRules.identifiers, ["mock"])
|
||||
}
|
||||
}
|
||||
|
||||
private class ResolverMock: RemoteRuleResolverProtocol {
|
||||
private(set) var executable: String?
|
||||
private(set) var configuration: [String: Any]?
|
||||
|
||||
private let identifiers = ["path/to/plugin": "test",
|
||||
"path/to/mock": "mock"]
|
||||
|
||||
func remoteRule(forExecutable executable: String, configuration: [String: Any]?) throws -> RemoteRule {
|
||||
self.executable = executable
|
||||
self.configuration = configuration
|
||||
|
||||
let identifier = identifiers[executable] ?? ""
|
||||
let ruleDescription = RuleDescription(identifier: identifier, name: "Test",
|
||||
description: "", kind: .idiomatic)
|
||||
let pluginDescription = PluginDescription(ruleDescription: ruleDescription)
|
||||
return RemoteRule(description: pluginDescription, configuration: nil)
|
||||
}
|
||||
}
|
|
@ -3,9 +3,9 @@ import SourceKittenFramework
|
|||
@testable import SwiftLintFramework
|
||||
import XCTest
|
||||
|
||||
private let optInRules = masterRuleList.list.filter({ $0.1.init() is OptInRule }).map({ $0.0 })
|
||||
let optInRules = masterRuleList.list.filter({ $0.1.init() is OptInRule }).map({ $0.0 })
|
||||
|
||||
private extension Configuration {
|
||||
extension Configuration {
|
||||
var disabledRules: [String] {
|
||||
let configuredRuleIDs = rules.map({ type(of: $0).description.identifier })
|
||||
let defaultRuleIDs = Set(masterRuleList.list.values.filter({
|
||||
|
@ -34,6 +34,8 @@ class ConfigurationTests: XCTestCase {
|
|||
XCTAssertEqual(config.indentation, .spaces(count: 4))
|
||||
XCTAssertEqual(config.reporter, "xcode")
|
||||
XCTAssertEqual(reporterFrom(identifier: config.reporter).identifier, "xcode")
|
||||
XCTAssertEqual(config.plugins, [])
|
||||
XCTAssertTrue(config.remoteRules.isEmpty)
|
||||
}
|
||||
|
||||
func testInitWithRelativePathAndRootPath() {
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
import Foundation
|
||||
import SourceKittenFramework
|
||||
@testable import SwiftLintFramework
|
||||
import XCTest
|
||||
|
||||
class RemoteRuleResolverTests: XCTestCase {
|
||||
func testCreatesRemoteRule() throws {
|
||||
let ruleDescription = RuleDescription(identifier: "test", name: "Test", description: "Test", kind: .lint)
|
||||
let pluginDescription = PluginDescription(ruleDescription: ruleDescription, requiredInformation: [.structure])
|
||||
|
||||
let encoder = JSONEncoder()
|
||||
encoder.keyEncodingStrategy = .convertToSnakeCase
|
||||
|
||||
let jsonString = String(decoding: try encoder.encode(pluginDescription), as: UTF8.self)
|
||||
let scriptContent = """
|
||||
#!/bin/bash
|
||||
echo '\(jsonString)'
|
||||
"""
|
||||
let url = URL(fileURLWithPath: NSTemporaryDirectory())
|
||||
.appendingPathComponent(UUID().uuidString)
|
||||
try scriptContent.data(using: .utf8)!.write(to: url)
|
||||
try FileManager.default.setAttributes([.posixPermissions: 0o777], ofItemAtPath: url.path)
|
||||
|
||||
let ruleConfiguration = [
|
||||
"k1": "v1",
|
||||
"k2": 2
|
||||
] as [String: Any]
|
||||
let configuration = [
|
||||
"foo": 20,
|
||||
"test": ruleConfiguration
|
||||
] as [String: Any]
|
||||
|
||||
let resolver = RemoteRuleResolver()
|
||||
let remoteRule = try resolver.remoteRule(forExecutable: url.path,
|
||||
configuration: configuration)
|
||||
|
||||
XCTAssertEqual(remoteRule.ruleDescription.identifier, "test")
|
||||
XCTAssertEqual(remoteRule.description.requiredInformation, [.structure])
|
||||
|
||||
XCTAssertEqual(remoteRule.configuration as? NSDictionary, ruleConfiguration.bridge())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
import Foundation
|
||||
import SourceKittenFramework
|
||||
@testable import SwiftLintFramework
|
||||
import XCTest
|
||||
|
||||
class RemoteRuleTests: XCTestCase {
|
||||
func testValidate() {
|
||||
let ruleDescription = RuleDescription(identifier: "test", name: "Test", description: "Test", kind: .lint)
|
||||
let pluginDescription = PluginDescription(ruleDescription: ruleDescription, requiredInformation: [.structure])
|
||||
let configuration = ["key": "value"]
|
||||
let fileContents = "let x = 10"
|
||||
|
||||
let server = RemoteLintServer(socketPath: "/tmp/test.socket")
|
||||
let dispatchGroup = DispatchGroup()
|
||||
|
||||
let delegate = MockRemoteLintServerDelegate()
|
||||
delegate.dispatchGroup = dispatchGroup
|
||||
delegate.violationsToReturn = [
|
||||
StyleViolation(ruleDescription: ruleDescription, severity: .error,
|
||||
location: Location(file: nil, line: 1, character: 4),
|
||||
reason: "Test violation")
|
||||
]
|
||||
|
||||
server.delegate = delegate
|
||||
|
||||
dispatchGroup.enter()
|
||||
server.run()
|
||||
|
||||
// wait until the server has started
|
||||
dispatchGroup.wait()
|
||||
|
||||
let remoteRule = RemoteRule(description: pluginDescription, configuration: configuration)
|
||||
let violations = remoteRule.validate(file: File(contents: fileContents))
|
||||
|
||||
XCTAssertEqual(violations, delegate.violationsToReturn)
|
||||
XCTAssertEqual(delegate.payload?.contents.value, fileContents)
|
||||
XCTAssertNil(delegate.payload!.path)
|
||||
XCTAssertTrue(delegate.payload!.syntaxMap.value.isEmpty)
|
||||
XCTAssertFalse(delegate.payload!.structure.value.isEmpty)
|
||||
XCTAssertEqual(delegate.payload!.configuration as? [String: String], configuration)
|
||||
|
||||
server.shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
private class MockRemoteLintServerDelegate: RemoteLintServerDelegate {
|
||||
var payload: RemoteRulePayload?
|
||||
var dispatchGroup: DispatchGroup?
|
||||
var violationsToReturn: [StyleViolation] = []
|
||||
|
||||
func server(_ server: RemoteLintServer, didReceivePayload payload: RemoteRulePayload) -> [StyleViolation] {
|
||||
self.payload = payload
|
||||
return violationsToReturn
|
||||
}
|
||||
|
||||
func serverStartedListening(_ server: RemoteLintServer) {
|
||||
dispatchGroup?.leave()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
import Foundation
|
||||
import SwiftLintFramework
|
||||
import XCTest
|
||||
|
||||
class RuleDescriptionTests: XCTestCase {
|
||||
func testCodableWithMissingValues() throws {
|
||||
let json = [
|
||||
"identifier": "my_cool_rule",
|
||||
"name": "Cool Rule",
|
||||
"description": "Validates stuff",
|
||||
"kind": "style"
|
||||
]
|
||||
|
||||
let decoder = JSONDecoder()
|
||||
decoder.keyDecodingStrategy = .convertFromSnakeCase
|
||||
let data = try JSONSerialization.data(withJSONObject: json)
|
||||
let ruleDescription = try decoder.decode(RuleDescription.self, from: data)
|
||||
|
||||
let expectedDescription = RuleDescription(identifier: "my_cool_rule", name: "Cool Rule",
|
||||
description: "Validates stuff", kind: .style)
|
||||
|
||||
// Comparing field by field because RuleDescription's == only checks the identifier
|
||||
XCTAssertEqual(ruleDescription.identifier, expectedDescription.identifier)
|
||||
XCTAssertEqual(ruleDescription.name, expectedDescription.name)
|
||||
XCTAssertEqual(ruleDescription.description, expectedDescription.description)
|
||||
XCTAssertEqual(ruleDescription.kind, expectedDescription.kind)
|
||||
XCTAssertEqual(ruleDescription.triggeringExamples, expectedDescription.triggeringExamples)
|
||||
XCTAssertEqual(ruleDescription.nonTriggeringExamples, expectedDescription.nonTriggeringExamples)
|
||||
XCTAssertEqual(ruleDescription.corrections, expectedDescription.corrections)
|
||||
XCTAssertEqual(ruleDescription.deprecatedAliases, expectedDescription.deprecatedAliases)
|
||||
XCTAssertEqual(ruleDescription.minSwiftVersion, expectedDescription.minSwiftVersion)
|
||||
XCTAssertEqual(ruleDescription.requiresFileOnDisk, expectedDescription.requiresFileOnDisk)
|
||||
}
|
||||
}
|
|
@ -59,7 +59,7 @@ jobs:
|
|||
|
||||
linux_swift_4.2:
|
||||
docker:
|
||||
- image: norionomura/swift:42
|
||||
- image: norionomura/swift:421
|
||||
steps:
|
||||
- checkout
|
||||
- run: swift test --parallel
|
||||
|
|
Loading…
Reference in New Issue