Migrate `strong_iboutlet` to SwiftSyntax (#4277)
This commit is contained in:
parent
6bfecb8fcc
commit
c70510c3ea
|
@ -1,7 +1,6 @@
|
|||
import Foundation
|
||||
import SourceKittenFramework
|
||||
import SwiftSyntax
|
||||
|
||||
public struct StrongIBOutletRule: ConfigurationProviderRule, ASTRule, SubstitutionCorrectableASTRule, OptInRule {
|
||||
public struct StrongIBOutletRule: ConfigurationProviderRule, SwiftSyntaxCorrectableRule, OptInRule {
|
||||
public var configuration = SeverityConfiguration(.warning)
|
||||
|
||||
public init() {}
|
||||
|
@ -30,29 +29,88 @@ public struct StrongIBOutletRule: ConfigurationProviderRule, ASTRule, Substituti
|
|||
]
|
||||
)
|
||||
|
||||
public func validate(file: SwiftLintFile, kind: SwiftDeclarationKind,
|
||||
dictionary: SourceKittenDictionary) -> [StyleViolation] {
|
||||
return violationRanges(in: file, kind: kind, dictionary: dictionary).map {
|
||||
StyleViolation(ruleDescription: Self.description,
|
||||
severity: configuration.severity,
|
||||
location: Location(file: file, characterOffset: $0.location))
|
||||
public func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor? {
|
||||
Visitor(viewMode: .sourceAccurate)
|
||||
}
|
||||
|
||||
public func makeRewriter(file: SwiftLintFile) -> ViolationsSyntaxRewriter? {
|
||||
file.locationConverter.map { locationConverter in
|
||||
Rewriter(
|
||||
locationConverter: locationConverter,
|
||||
disabledRegions: disabledRegions(file: file)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension StrongIBOutletRule {
|
||||
final class Visitor: SyntaxVisitor, ViolationsSyntaxVisitor {
|
||||
private(set) var violationPositions: [AbsolutePosition] = []
|
||||
|
||||
override func visitPost(_ node: VariableDeclSyntax) {
|
||||
if let violationPosition = node.violationPosition {
|
||||
violationPositions.append(violationPosition)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func violationRanges(in file: SwiftLintFile, kind: SwiftDeclarationKind,
|
||||
dictionary: SourceKittenDictionary) -> [NSRange] {
|
||||
guard kind == .varInstance && dictionary.enclosedSwiftAttributes.contains(.iboutlet),
|
||||
let weakAttribute = dictionary.swiftAttributes.first(where: {
|
||||
$0.attribute == SwiftDeclarationAttributeKind.weak.rawValue
|
||||
}),
|
||||
let byteRange = weakAttribute.byteRange,
|
||||
let range = file.stringView.byteRangeToNSRange(byteRange)
|
||||
else { return [] }
|
||||
return [range]
|
||||
private final class Rewriter: SyntaxRewriter, ViolationsSyntaxRewriter {
|
||||
private(set) var correctionPositions: [AbsolutePosition] = []
|
||||
let locationConverter: SourceLocationConverter
|
||||
let disabledRegions: [SourceRange]
|
||||
|
||||
init(locationConverter: SourceLocationConverter, disabledRegions: [SourceRange]) {
|
||||
self.locationConverter = locationConverter
|
||||
self.disabledRegions = disabledRegions
|
||||
}
|
||||
|
||||
override func visit(_ node: VariableDeclSyntax) -> DeclSyntax {
|
||||
guard let violationPosition = node.violationPosition,
|
||||
let weakOrUnownedModifier = node.weakOrUnownedModifier,
|
||||
let modifiers = node.modifiers else {
|
||||
return super.visit(node)
|
||||
}
|
||||
|
||||
let isInDisabledRegion = disabledRegions.contains { region in
|
||||
region.contains(node.positionAfterSkippingLeadingTrivia, locationConverter: locationConverter)
|
||||
}
|
||||
|
||||
guard !isInDisabledRegion else {
|
||||
return super.visit(node)
|
||||
}
|
||||
|
||||
let newModifiers = ModifierListSyntax(modifiers.filter { $0 != weakOrUnownedModifier })
|
||||
let newNode = node.withModifiers(newModifiers)
|
||||
correctionPositions.append(violationPosition)
|
||||
return super.visit(newNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension VariableDeclSyntax {
|
||||
var violationPosition: AbsolutePosition? {
|
||||
guard let keyword = weakOrUnownedKeyword, isIBOutlet else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return keyword.positionAfterSkippingLeadingTrivia
|
||||
}
|
||||
|
||||
public func substitution(for violationRange: NSRange, in file: SwiftLintFile) -> (NSRange, String)? {
|
||||
return (NSRange(location: violationRange.location, length: violationRange.length + 1), "")
|
||||
var isIBOutlet: Bool {
|
||||
attributes?.contains { attr in
|
||||
attr.as(AttributeSyntax.self)?.attributeName.tokenKind == .identifier("IBOutlet")
|
||||
} ?? false
|
||||
}
|
||||
|
||||
var weakOrUnownedModifier: DeclModifierSyntax? {
|
||||
modifiers?.first { decl in
|
||||
decl.name.tokenKind == .contextualKeyword("weak") ||
|
||||
decl.name.tokenKind == .contextualKeyword("unowned")
|
||||
}
|
||||
}
|
||||
|
||||
var weakOrUnownedKeyword: TokenSyntax? {
|
||||
weakOrUnownedModifier?.name
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue