Files
SwiftLint/Source/SwiftLintFramework/Rules/Lint/TestCaseAccessibilityRule.swift
T
Danny Mösch 449190d324 Verify examples in rules by default and enforce explicit exclusion (#4065)
A rule must conform to ManuallyTestedExamplesRule to skip generation of a test for its examples.
2022-08-09 22:32:09 +02:00

69 lines
2.9 KiB
Swift

import Foundation
import SourceKittenFramework
public struct TestCaseAccessibilityRule: Rule, OptInRule, ConfigurationProviderRule,
SubstitutionCorrectableRule {
public var configuration = TestCaseAccessibilityConfiguration()
public init() {}
public static let description = RuleDescription(
identifier: "test_case_accessibility",
name: "Test case accessibility",
description: "Test cases should only contain private non-test members.",
kind: .lint,
nonTriggeringExamples: TestCaseAccessibilityRuleExamples.nonTriggeringExamples,
triggeringExamples: TestCaseAccessibilityRuleExamples.triggeringExamples,
corrections: TestCaseAccessibilityRuleExamples.corrections
)
public func validate(file: SwiftLintFile) -> [StyleViolation] {
return testClasses(in: file).flatMap { dictionary in
violationRanges(in: file, for: dictionary).map { range in
return StyleViolation(ruleDescription: Self.description,
severity: configuration.severity,
location: Location(file: file, characterOffset: range.location))
}
}
}
// MARK: - SubstitutionCorrectableRule
public func violationRanges(in file: SwiftLintFile) -> [NSRange] {
return testClasses(in: file).flatMap { violationRanges(in: file, for: $0) }
}
public func substitution(for violationRange: NSRange, in file: SwiftLintFile) -> (NSRange, String)? {
return (violationRange, "private ")
}
// MARK: - Private
private func testClasses(in file: SwiftLintFile) -> [SourceKittenDictionary] {
return file.structureDictionary.substructure.filter { dictionary in
dictionary.declarationKind == .class && dictionary.inheritedTypes.contains("XCTestCase")
}
}
private func violationRanges(in file: SwiftLintFile,
for dictionary: SourceKittenDictionary) -> [NSRange] {
return dictionary.substructure.compactMap { subDictionary -> NSRange? in
guard
let kind = subDictionary.declarationKind,
kind != .varLocal,
let name = subDictionary.name,
!isXCTestMember(kind: kind, name: name, dictionary: subDictionary),
let offset = subDictionary.offset,
subDictionary.accessibility?.isPrivate != true else { return nil }
return file.stringView.byteRangeToNSRange(ByteRange(location: offset, length: 0))
}
}
private func isXCTestMember(kind: SwiftDeclarationKind, name: String,
dictionary: SourceKittenDictionary) -> Bool {
return XCTestHelpers.isXCTestMember(kind: kind, name: name, dictionary: dictionary)
|| configuration.allowedPrefixes.contains(where: name.hasPrefix)
}
}