mirror of
https://github.com/realm/SwiftLint.git
synced 2026-06-06 20:18:40 +00:00
With the binding of configurations to their associated rule types "unknown configuration" errors can be made more specific mentioning also the rule's identifier in the printed message.
62 lines
2.7 KiB
Swift
62 lines
2.7 KiB
Swift
import SwiftSyntax
|
|
|
|
struct ContainsOverFirstNotNilRule: SwiftSyntaxRule, OptInRule, ConfigurationProviderRule {
|
|
var configuration = SeverityConfiguration<Self>(.warning)
|
|
|
|
static let description = RuleDescription(
|
|
identifier: "contains_over_first_not_nil",
|
|
name: "Contains over First not Nil",
|
|
description: "Prefer `contains` over `first(where:) != nil` and `firstIndex(where:) != nil`.",
|
|
kind: .performance,
|
|
nonTriggeringExamples: ["first", "firstIndex"].flatMap { method in
|
|
return [
|
|
Example("let \(method) = myList.\(method)(where: { $0 % 2 == 0 })\n"),
|
|
Example("let \(method) = myList.\(method) { $0 % 2 == 0 }\n")
|
|
]
|
|
},
|
|
triggeringExamples: ["first", "firstIndex"].flatMap { method in
|
|
return ["!=", "=="].flatMap { comparison in
|
|
return [
|
|
Example("↓myList.\(method) { $0 % 2 == 0 } \(comparison) nil\n"),
|
|
Example("↓myList.\(method)(where: { $0 % 2 == 0 }) \(comparison) nil\n"),
|
|
Example("↓myList.map { $0 + 1 }.\(method)(where: { $0 % 2 == 0 }) \(comparison) nil\n"),
|
|
Example("↓myList.\(method)(where: someFunction) \(comparison) nil\n"),
|
|
Example("↓myList.map { $0 + 1 }.\(method) { $0 % 2 == 0 } \(comparison) nil\n"),
|
|
Example("(↓myList.\(method) { $0 % 2 == 0 }) \(comparison) nil\n")
|
|
]
|
|
}
|
|
}
|
|
)
|
|
|
|
func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor {
|
|
Visitor(viewMode: .sourceAccurate)
|
|
}
|
|
|
|
func preprocess(file: SwiftLintFile) -> SourceFileSyntax? {
|
|
file.foldedSyntaxTree
|
|
}
|
|
}
|
|
|
|
private extension ContainsOverFirstNotNilRule {
|
|
final class Visitor: ViolationsSyntaxVisitor {
|
|
override func visitPost(_ node: InfixOperatorExprSyntax) {
|
|
guard
|
|
let operatorNode = node.operatorOperand.as(BinaryOperatorExprSyntax.self),
|
|
operatorNode.operatorToken.tokenKind.isEqualityComparison,
|
|
node.rightOperand.is(NilLiteralExprSyntax.self),
|
|
let first = node.leftOperand.asFunctionCall,
|
|
let calledExpression = first.calledExpression.as(MemberAccessExprSyntax.self),
|
|
calledExpression.name.text == "first" || calledExpression.name.text == "firstIndex"
|
|
else {
|
|
return
|
|
}
|
|
|
|
let violation = ReasonedRuleViolation(
|
|
position: first.positionAfterSkippingLeadingTrivia,
|
|
reason: "Prefer `contains` over `\(calledExpression.name.text)(where:) != nil`"
|
|
)
|
|
violations.append(violation)
|
|
}
|
|
}
|
|
}
|