Allow configuring `xct_specific_matcher` with matchers (#4905)

So that either `one-argument-asserts` or `two-argument-asserts` or both
can be enabled.

The following configuration effectively reverts back to the rule
behavior prior to https://github.com/realm/SwiftLint/pull/3858:

```yaml
xct_specific_matcher:
  matchers:
    - two-argument-asserts
```
This commit is contained in:
JP Simard 2023-04-18 16:50:58 -04:00 committed by GitHub
parent 8b72eb0406
commit fbbccf9db8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 63 additions and 5 deletions

View File

@ -19,8 +19,11 @@
[kimdv](https://github.com/kimdv)
* Extend `xct_specific_matcher` rule to check for boolean asserts on (un)equal
comparisons.
comparisons. The rule can be configured with the matchers that should trigger
rule violations. By default, all matchers trigger, but that can be limited to
just `one-argument-asserts` or `two-argument-asserts`.
[SimplyDanny](https://github.com/SimplyDanny)
[JP Simard](https://github.com/jpsim)
[#3726](https://github.com/realm/SwiftLint/issues/3726)
* Trigger `prefer_self_in_static_references` rule on more type references.

View File

@ -2,7 +2,7 @@ import SwiftOperators
import SwiftSyntax
struct XCTSpecificMatcherRule: SwiftSyntaxRule, OptInRule, ConfigurationProviderRule {
var configuration = SeverityConfiguration(.warning)
var configuration = XCTSpecificMatcherRuleConfiguration()
init() {}
@ -16,14 +16,28 @@ struct XCTSpecificMatcherRule: SwiftSyntaxRule, OptInRule, ConfigurationProvider
)
func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor {
Visitor(viewMode: .sourceAccurate)
Visitor(configuration: configuration)
}
}
private extension XCTSpecificMatcherRule {
final class Visitor: ViolationsSyntaxVisitor {
let configuration: XCTSpecificMatcherRuleConfiguration
init(configuration: XCTSpecificMatcherRuleConfiguration) {
self.configuration = configuration
super.init(viewMode: .sourceAccurate)
}
override func visitPost(_ node: FunctionCallExprSyntax) {
if let suggestion = TwoArgsXCTAssert.violations(in: node) ?? OneArgXCTAssert.violations(in: node) {
if configuration.matchers.contains(.twoArgumentAsserts),
let suggestion = TwoArgsXCTAssert.violations(in: node) {
violations.append(ReasonedRuleViolation(
position: node.positionAfterSkippingLeadingTrivia,
reason: "Prefer the specific matcher '\(suggestion)' instead"
))
} else if configuration.matchers.contains(.oneArgumentAsserts),
let suggestion = OneArgXCTAssert.violations(in: node) {
violations.append(ReasonedRuleViolation(
position: node.positionAfterSkippingLeadingTrivia,
reason: "Prefer the specific matcher '\(suggestion)' instead"

View File

@ -50,7 +50,13 @@ internal struct XCTSpecificMatcherRuleExamples {
Example("XCTAssertEqual(foo?.bar, toto())"),
Example("XCTAssertEqual(foo?.bar, .toto(.zoo))"),
Example("XCTAssertEqual(toto(), foo?.bar)"),
Example("XCTAssertEqual(.toto(.zoo), foo?.bar)")
Example("XCTAssertEqual(.toto(.zoo), foo?.bar)"),
// Configurations Disabled
Example("XCTAssertEqual(foo, true)",
configuration: ["matchers": ["one-argument-asserts"]]),
Example("XCTAssert(foo == bar)",
configuration: ["matchers": ["two-argument-asserts"]])
]
static let triggeringExamples = [

View File

@ -0,0 +1,35 @@
struct XCTSpecificMatcherRuleConfiguration: SeverityBasedRuleConfiguration, Equatable {
private(set) var severityConfiguration = SeverityConfiguration(.warning)
private(set) var matchers = Set(Matcher.allCases)
enum Matcher: String, Hashable, CaseIterable {
case oneArgumentAsserts = "one-argument-asserts"
case twoArgumentAsserts = "two-argument-asserts"
}
private enum ConfigurationKey: String {
case severity
case matchers
}
var consoleDescription: String {
return [
"severity: \(severityConfiguration.consoleDescription)",
"\(ConfigurationKey.matchers): \(matchers.map(\.rawValue).sorted().joined(separator: ", "))"
].joined(separator: ", ")
}
mutating func apply(configuration: Any) throws {
guard let configuration = configuration as? [String: Any] else {
throw ConfigurationError.unknownConfiguration
}
if let severityString = configuration[ConfigurationKey.severity.rawValue] as? String {
try severityConfiguration.apply(configuration: severityString)
}
if let matchers = configuration[ConfigurationKey.matchers.rawValue] as? [String] {
self.matchers = Set(matchers.compactMap(Matcher.init(rawValue:)))
}
}
}