Migrate `strong_iboutlet` to SwiftSyntax (#4277)
This commit is contained in:
parent
6bfecb8fcc
commit
c70510c3ea
|
@ -1,7 +1,6 @@
|
||||||
import Foundation
|
import SwiftSyntax
|
||||||
import SourceKittenFramework
|
|
||||||
|
|
||||||
public struct StrongIBOutletRule: ConfigurationProviderRule, ASTRule, SubstitutionCorrectableASTRule, OptInRule {
|
public struct StrongIBOutletRule: ConfigurationProviderRule, SwiftSyntaxCorrectableRule, OptInRule {
|
||||||
public var configuration = SeverityConfiguration(.warning)
|
public var configuration = SeverityConfiguration(.warning)
|
||||||
|
|
||||||
public init() {}
|
public init() {}
|
||||||
|
@ -30,29 +29,88 @@ public struct StrongIBOutletRule: ConfigurationProviderRule, ASTRule, Substituti
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
public func validate(file: SwiftLintFile, kind: SwiftDeclarationKind,
|
public func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor? {
|
||||||
dictionary: SourceKittenDictionary) -> [StyleViolation] {
|
Visitor(viewMode: .sourceAccurate)
|
||||||
return violationRanges(in: file, kind: kind, dictionary: dictionary).map {
|
}
|
||||||
StyleViolation(ruleDescription: Self.description,
|
|
||||||
severity: configuration.severity,
|
public func makeRewriter(file: SwiftLintFile) -> ViolationsSyntaxRewriter? {
|
||||||
location: Location(file: file, characterOffset: $0.location))
|
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,
|
private final class Rewriter: SyntaxRewriter, ViolationsSyntaxRewriter {
|
||||||
dictionary: SourceKittenDictionary) -> [NSRange] {
|
private(set) var correctionPositions: [AbsolutePosition] = []
|
||||||
guard kind == .varInstance && dictionary.enclosedSwiftAttributes.contains(.iboutlet),
|
let locationConverter: SourceLocationConverter
|
||||||
let weakAttribute = dictionary.swiftAttributes.first(where: {
|
let disabledRegions: [SourceRange]
|
||||||
$0.attribute == SwiftDeclarationAttributeKind.weak.rawValue
|
|
||||||
}),
|
init(locationConverter: SourceLocationConverter, disabledRegions: [SourceRange]) {
|
||||||
let byteRange = weakAttribute.byteRange,
|
self.locationConverter = locationConverter
|
||||||
let range = file.stringView.byteRangeToNSRange(byteRange)
|
self.disabledRegions = disabledRegions
|
||||||
else { return [] }
|
}
|
||||||
return [range]
|
|
||||||
|
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)? {
|
var isIBOutlet: Bool {
|
||||||
return (NSRange(location: violationRange.location, length: violationRange.length + 1), "")
|
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