Migrate `strong_iboutlet` to SwiftSyntax (#4277)

This commit is contained in:
Marcelo Fabri 2022-10-04 10:03:33 -07:00 committed by GitHub
parent 6bfecb8fcc
commit c70510c3ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 79 additions and 21 deletions

View File

@ -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
}
}