Skip to content

Commit

Permalink
Migrate anonymous_argument_in_multiline_closure to SwiftSyntax (rea…
Browse files Browse the repository at this point in the history
  • Loading branch information
marcelofabri authored Oct 5, 2022
1 parent 0f97059 commit 1cbf3ca
Showing 1 changed file with 26 additions and 35 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import Foundation
import SourceKittenFramework
import SwiftSyntax

public struct AnonymousArgumentInMultilineClosureRule: ASTRule, OptInRule, ConfigurationProviderRule {
public struct AnonymousArgumentInMultilineClosureRule: SwiftSyntaxRule, OptInRule, ConfigurationProviderRule {
public var configuration = SeverityConfiguration(.warning)

public init() {}
Expand Down Expand Up @@ -34,46 +33,38 @@ public struct AnonymousArgumentInMultilineClosureRule: ASTRule, OptInRule, Confi
]
)

public func validate(file: SwiftLintFile, kind: SwiftExpressionKind,
dictionary: SourceKittenDictionary) -> [StyleViolation] {
guard kind == .closure,
dictionary.enclosedVarParameters.isEmpty,
let range = dictionary.bodyByteRange,
let (initialLine, _) = file.stringView.lineAndCharacter(forByteOffset: range.lowerBound),
let (finalLine, _) = file.stringView.lineAndCharacter(forByteOffset: range.upperBound),
initialLine != finalLine,
let bodyNSRange = file.stringView.byteRangeToNSRange(range) else {
return []
public func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor? {
file.locationConverter.map {
Visitor(locationConverter: $0)
}
}
}

let matches = file.match(pattern: "\\$0", with: [.identifier], range: bodyNSRange).filter { range in
guard range.length == 2,
let byteRange = file.stringView.NSRangeToByteRange(range) else {
return false
}
private extension AnonymousArgumentInMultilineClosureRule {
final class Visitor: SyntaxVisitor, ViolationsSyntaxVisitor {
private(set) var violationPositions: [AbsolutePosition] = []
private let locationConverter: SourceLocationConverter

// do not trigger for nested closures
let expressions = closureExpressions(forByteOffset: byteRange.location, structureDictionary: dictionary)
return expressions.isEmpty
init(locationConverter: SourceLocationConverter) {
self.locationConverter = locationConverter
super.init(viewMode: .sourceAccurate)
}

return matches.map {
StyleViolation(ruleDescription: Self.description,
severity: configuration.severity,
location: Location(file: file, characterOffset: $0.location))
override func visit(_ node: ClosureExprSyntax) -> SyntaxVisitorContinueKind {
let startLocation = locationConverter.location(for: node.leftBrace.positionAfterSkippingLeadingTrivia)
let endLocation = locationConverter.location(for: node.rightBrace.endPositionBeforeTrailingTrivia)

guard let startLine = startLocation.line, let endLine = endLocation.line, startLine != endLine else {
return .skipChildren
}

return .visitChildren
}
}

private func closureExpressions(forByteOffset byteOffset: ByteCount,
structureDictionary: SourceKittenDictionary) -> [SourceKittenDictionary] {
return structureDictionary.traverseBreadthFirst { dictionary in
guard dictionary.expressionKind == .closure,
let byteRange = dictionary.byteRange,
byteRange.contains(byteOffset)
else {
return nil
override func visitPost(_ node: IdentifierExprSyntax) {
if case .dollarIdentifier = node.identifier.tokenKind {
violationPositions.append(node.positionAfterSkippingLeadingTrivia)
}
return [dictionary]
}
}
}

0 comments on commit 1cbf3ca

Please sign in to comment.