Files
SwiftLint/Source/SwiftLintFramework/Rules/Idiomatic/PatternMatchingKeywordsRule.swift
T
Zev Eisenberg fcf848608e Add Inline test failure messages (#3040)
* Add Example wrapper in order to display test failures inline when running in Xcode.
* Stop using Swift 5.1-only features so we can compile on Xcode 10.2.
* Wrap strings in Example.
* Add Changelog entry.
* Wrap all examples in Example struct.
* Better and more complete capturing of line numbers.
* Fix broken test.
* Better test traceability.
* Address or disable linting warnings.
* Add documentation comments.
* Disable linter for a few cases.
* Limit mutability and add copy-and-mutate utility functions.
* Limit scope of mutability.
2020-02-02 10:35:37 +02:00

79 lines
2.9 KiB
Swift

import Foundation
import SourceKittenFramework
public struct PatternMatchingKeywordsRule: ASTRule, ConfigurationProviderRule, OptInRule, AutomaticTestableRule {
public var configuration = SeverityConfiguration(.warning)
public init() {}
public static let description = RuleDescription(
identifier: "pattern_matching_keywords",
name: "Pattern Matching Keywords",
description: "Combine multiple pattern matching bindings by moving keywords out of tuples.",
kind: .idiomatic,
nonTriggeringExamples: [
Example("default"),
Example("case 1"),
Example("case bar"),
Example("case let (x, y)"),
Example("case .foo(let x)"),
Example("case let .foo(x, y)"),
Example("case .foo(let x), .bar(let x)"),
Example("case .foo(let x, var y)"),
Example("case var (x, y)"),
Example("case .foo(var x)"),
Example("case var .foo(x, y)")
].map(wrapInSwitch),
triggeringExamples: [
Example("case (↓let x, ↓let y)"),
Example("case .foo(↓let x, ↓let y)"),
Example("case (.yamlParsing(↓let x), .yamlParsing(↓let y))"),
Example("case (↓var x, ↓var y)"),
Example("case .foo(↓var x, ↓var y)"),
Example("case (.yamlParsing(↓var x), .yamlParsing(↓var y))")
].map(wrapInSwitch)
)
public func validate(file: SwiftLintFile, kind: StatementKind,
dictionary: SourceKittenDictionary) -> [StyleViolation] {
guard kind == .case else {
return []
}
let contents = file.stringView
return dictionary.elements.flatMap { subDictionary -> [StyleViolation] in
guard subDictionary.kind == "source.lang.swift.structure.elem.pattern",
let caseByteRange = subDictionary.byteRange,
let caseRange = contents.byteRangeToNSRange(caseByteRange)
else {
return []
}
let letMatches = file.match(pattern: "\\blet\\b", with: [.keyword], range: caseRange)
let varMatches = file.match(pattern: "\\bvar\\b", with: [.keyword], range: caseRange)
if !letMatches.isEmpty && !varMatches.isEmpty {
return []
}
guard letMatches.count > 1 || varMatches.count > 1 else {
return []
}
return (letMatches + varMatches).map {
StyleViolation(ruleDescription: type(of: self).description,
severity: configuration.severity,
location: Location(file: file, characterOffset: $0.location))
}
}
}
}
private func wrapInSwitch(_ example: Example) -> Example {
return example.with(code: """
switch foo {
\(example.code): break
}
""")
}