mirror of
https://github.com/realm/SwiftLint.git
synced 2026-05-07 20:12:49 +00:00
47335d7f95
Report number of corrections per file instead.
71 lines
2.8 KiB
Swift
71 lines
2.8 KiB
Swift
import SwiftLintCore
|
|
import SwiftSyntax
|
|
|
|
@SwiftSyntaxRule(explicitRewriter: true, optIn: true)
|
|
struct FinalTestCaseRule: Rule {
|
|
var configuration = FinalTestCaseConfiguration()
|
|
|
|
static let description = RuleDescription(
|
|
identifier: "final_test_case",
|
|
name: "Final Test Case",
|
|
description: "Test cases should be final",
|
|
kind: .performance,
|
|
nonTriggeringExamples: [
|
|
Example("final class Test: XCTestCase {}"),
|
|
Example("open class Test: XCTestCase {}"),
|
|
Example("public final class Test: QuickSpec {}"),
|
|
Example("class Test: MyTestCase {}"),
|
|
Example("struct Test: MyTestCase {}", configuration: ["test_parent_classes": "MyTestCase"]),
|
|
],
|
|
triggeringExamples: [
|
|
Example("class ↓Test: XCTestCase {}"),
|
|
Example("public class ↓Test: QuickSpec {}"),
|
|
Example("class ↓Test: MyTestCase {}", configuration: ["test_parent_classes": "MyTestCase"]),
|
|
],
|
|
corrections: [
|
|
Example("class ↓Test: XCTestCase {}"):
|
|
Example("final class Test: XCTestCase {}"),
|
|
Example("internal class ↓Test: XCTestCase {}"):
|
|
Example("internal final class Test: XCTestCase {}"),
|
|
]
|
|
)
|
|
}
|
|
|
|
private extension FinalTestCaseRule {
|
|
final class Visitor: ViolationsSyntaxVisitor<ConfigurationType> {
|
|
override func visitPost(_ node: ClassDeclSyntax) {
|
|
if node.isNonFinalTestClass(parentClasses: configuration.testParentClasses) {
|
|
violations.append(node.name.positionAfterSkippingLeadingTrivia)
|
|
}
|
|
}
|
|
}
|
|
|
|
final class Rewriter: ViolationsSyntaxRewriter<ConfigurationType> {
|
|
override func visit(_ node: ClassDeclSyntax) -> DeclSyntax {
|
|
var newNode = node
|
|
if node.isNonFinalTestClass(parentClasses: configuration.testParentClasses) {
|
|
numberOfCorrections += 1
|
|
let finalModifier = DeclModifierSyntax(name: .keyword(.final))
|
|
newNode =
|
|
if node.modifiers.isEmpty {
|
|
node
|
|
.with(\.modifiers, [finalModifier.with(\.leadingTrivia, node.classKeyword.leadingTrivia)])
|
|
.with(\.classKeyword.leadingTrivia, .space)
|
|
} else {
|
|
node
|
|
.with(\.modifiers, node.modifiers + [finalModifier.with(\.trailingTrivia, .space)])
|
|
}
|
|
}
|
|
return super.visit(newNode)
|
|
}
|
|
}
|
|
}
|
|
|
|
private extension ClassDeclSyntax {
|
|
func isNonFinalTestClass(parentClasses: Set<String>) -> Bool {
|
|
inheritanceClause.containsInheritedType(inheritedTypes: parentClasses)
|
|
&& !modifiers.contains(keyword: .open)
|
|
&& !modifiers.contains(keyword: .final)
|
|
}
|
|
}
|