mirror of
https://github.com/realm/SwiftLint.git
synced 2026-06-06 20:18:40 +00:00
* [bazel] Remove custom SwiftSyntax BUILD file Something similar to this has been merged upstream instead now. This also renames the repo name to SwiftSyntax in preparation for it being in the BCR * [SwiftSyntax] Update to latest 509.0.0 tag https://github.com/apple/swift-syntax/releases/tag/509.0.0-swift-DEVELOPMENT-SNAPSHOT-2023-04-25-b
112 lines
4.4 KiB
Swift
112 lines
4.4 KiB
Swift
import Foundation
|
|
import SourceKittenFramework
|
|
import SwiftSyntax
|
|
|
|
struct CommaInheritanceRule: OptInRule, SubstitutionCorrectableRule, ConfigurationProviderRule,
|
|
SourceKitFreeRule {
|
|
var configuration = SeverityConfiguration(.warning)
|
|
|
|
static let description = RuleDescription(
|
|
identifier: "comma_inheritance",
|
|
name: "Comma Inheritance Rule",
|
|
description: "Use commas to separate types in inheritance lists",
|
|
kind: .style,
|
|
nonTriggeringExamples: [
|
|
Example("struct A: Codable, Equatable {}"),
|
|
Example("enum B: Codable, Equatable {}"),
|
|
Example("class C: Codable, Equatable {}"),
|
|
Example("protocol D: Codable, Equatable {}"),
|
|
Example("typealias E = Equatable & Codable"),
|
|
Example("func foo<T: Equatable & Codable>(_ param: T) {}"),
|
|
Example("""
|
|
protocol G {
|
|
associatedtype Model: Codable, Equatable
|
|
}
|
|
""")
|
|
],
|
|
triggeringExamples: [
|
|
Example("struct A: Codable↓ & Equatable {}"),
|
|
Example("struct A: Codable↓ & Equatable {}"),
|
|
Example("struct A: Codable↓&Equatable {}"),
|
|
Example("struct A: Codable↓& Equatable {}"),
|
|
Example("enum B: Codable↓ & Equatable {}"),
|
|
Example("class C: Codable↓ & Equatable {}"),
|
|
Example("protocol D: Codable↓ & Equatable {}"),
|
|
Example("""
|
|
protocol G {
|
|
associatedtype Model: Codable↓ & Equatable
|
|
}
|
|
""")
|
|
],
|
|
corrections: [
|
|
Example("struct A: Codable↓ & Equatable {}"): Example("struct A: Codable, Equatable {}"),
|
|
Example("struct A: Codable↓ & Equatable {}"): Example("struct A: Codable, Equatable {}"),
|
|
Example("struct A: Codable↓&Equatable {}"): Example("struct A: Codable, Equatable {}"),
|
|
Example("struct A: Codable↓& Equatable {}"): Example("struct A: Codable, Equatable {}"),
|
|
Example("enum B: Codable↓ & Equatable {}"): Example("enum B: Codable, Equatable {}"),
|
|
Example("class C: Codable↓ & Equatable {}"): Example("class C: Codable, Equatable {}"),
|
|
Example("protocol D: Codable↓ & Equatable {}"): Example("protocol D: Codable, Equatable {}"),
|
|
Example("""
|
|
protocol G {
|
|
associatedtype Model: Codable↓ & Equatable
|
|
}
|
|
"""): Example("""
|
|
protocol G {
|
|
associatedtype Model: Codable, Equatable
|
|
}
|
|
""")
|
|
]
|
|
)
|
|
|
|
// MARK: - Rule
|
|
|
|
func validate(file: SwiftLintFile) -> [StyleViolation] {
|
|
return violationRanges(in: file).map {
|
|
StyleViolation(ruleDescription: Self.description,
|
|
severity: configuration.severity,
|
|
location: Location(file: file, characterOffset: $0.location))
|
|
}
|
|
}
|
|
|
|
// MARK: - SubstitutionCorrectableRule
|
|
|
|
func substitution(for violationRange: NSRange, in file: SwiftLintFile) -> (NSRange, String)? {
|
|
return (violationRange, ", ")
|
|
}
|
|
|
|
func violationRanges(in file: SwiftLintFile) -> [NSRange] {
|
|
let visitor = CommaInheritanceRuleVisitor(viewMode: .sourceAccurate)
|
|
return visitor.walk(file: file) { visitor -> [ByteRange] in
|
|
visitor.violationRanges
|
|
}.compactMap {
|
|
file.stringView.byteRangeToNSRange($0)
|
|
}
|
|
}
|
|
}
|
|
|
|
private final class CommaInheritanceRuleVisitor: SyntaxVisitor {
|
|
private(set) var violationRanges: [ByteRange] = []
|
|
|
|
override func visitPost(_ node: InheritedTypeSyntax) {
|
|
for type in node.children(viewMode: .sourceAccurate) {
|
|
guard let composition = type.as(CompositionTypeSyntax.self) else {
|
|
continue
|
|
}
|
|
|
|
for ampersand in composition.elements.compactMap(\.ampersand) {
|
|
let position: AbsolutePosition
|
|
if let previousToken = ampersand.previousToken(viewMode: .sourceAccurate) {
|
|
position = previousToken.endPositionBeforeTrailingTrivia
|
|
} else {
|
|
position = ampersand.position
|
|
}
|
|
|
|
violationRanges.append(ByteRange(
|
|
location: ByteCount(position),
|
|
length: ByteCount(ampersand.endPosition.utf8Offset - position.utf8Offset)
|
|
))
|
|
}
|
|
}
|
|
}
|
|
}
|