71 lines
3.5 KiB
Swift
71 lines
3.5 KiB
Swift
import SwiftSyntax
|
|
|
|
struct SortedFirstLastRule: SwiftSyntaxRule, OptInRule, ConfigurationProviderRule {
|
|
var configuration = SeverityConfiguration<Self>(.warning)
|
|
|
|
static let description = RuleDescription(
|
|
identifier: "sorted_first_last",
|
|
name: "Min or Max over Sorted First or Last",
|
|
description: "Prefer using `min()` or `max()` over `sorted().first` or `sorted().last`",
|
|
kind: .performance,
|
|
nonTriggeringExamples: [
|
|
Example("let min = myList.min()\n"),
|
|
Example("let min = myList.min(by: { $0 < $1 })\n"),
|
|
Example("let min = myList.min(by: >)\n"),
|
|
Example("let max = myList.max()\n"),
|
|
Example("let max = myList.max(by: { $0 < $1 })\n"),
|
|
Example("let message = messages.sorted(byKeyPath: #keyPath(Message.timestamp)).last"),
|
|
Example(#"let message = messages.sorted(byKeyPath: "timestamp", ascending: false).first"#),
|
|
Example("myList.sorted().firstIndex(of: key)"),
|
|
Example("myList.sorted().lastIndex(of: key)"),
|
|
Example("myList.sorted().firstIndex(where: someFunction)"),
|
|
Example("myList.sorted().lastIndex(where: someFunction)"),
|
|
Example("myList.sorted().firstIndex { $0 == key }"),
|
|
Example("myList.sorted().lastIndex { $0 == key }"),
|
|
Example("myList.sorted().first(where: someFunction)"),
|
|
Example("myList.sorted().last(where: someFunction)"),
|
|
Example("myList.sorted().first { $0 == key }"),
|
|
Example("myList.sorted().last { $0 == key }")
|
|
],
|
|
triggeringExamples: [
|
|
Example("↓myList.sorted().first\n"),
|
|
Example("↓myList.sorted(by: { $0.description < $1.description }).first\n"),
|
|
Example("↓myList.sorted(by: >).first\n"),
|
|
Example("↓myList.map { $0 + 1 }.sorted().first\n"),
|
|
Example("↓myList.sorted(by: someFunction).first\n"),
|
|
Example("↓myList.map { $0 + 1 }.sorted { $0.description < $1.description }.first\n"),
|
|
Example("↓myList.sorted().last\n"),
|
|
Example("↓myList.sorted().last?.something()\n"),
|
|
Example("↓myList.sorted(by: { $0.description < $1.description }).last\n"),
|
|
Example("↓myList.map { $0 + 1 }.sorted().last\n"),
|
|
Example("↓myList.sorted(by: someFunction).last\n"),
|
|
Example("↓myList.map { $0 + 1 }.sorted { $0.description < $1.description }.last\n"),
|
|
Example("↓myList.map { $0 + 1 }.sorted { $0.first < $1.first }.last\n")
|
|
]
|
|
)
|
|
|
|
func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor {
|
|
Visitor(viewMode: .sourceAccurate)
|
|
}
|
|
}
|
|
|
|
private extension SortedFirstLastRule {
|
|
final class Visitor: ViolationsSyntaxVisitor {
|
|
override func visitPost(_ node: MemberAccessExprSyntax) {
|
|
guard
|
|
node.name.text == "first" || node.name.text == "last",
|
|
node.parent?.is(FunctionCallExprSyntax.self) != true,
|
|
let firstBase = node.base?.asFunctionCall,
|
|
let firstBaseCalledExpression = firstBase.calledExpression.as(MemberAccessExprSyntax.self),
|
|
firstBaseCalledExpression.name.text == "sorted",
|
|
case let argumentLabels = firstBase.argumentList.map({ $0.label?.text }),
|
|
argumentLabels.isEmpty || argumentLabels == ["by"]
|
|
else {
|
|
return
|
|
}
|
|
|
|
violations.append(node.positionAfterSkippingLeadingTrivia)
|
|
}
|
|
}
|
|
}
|