Compare commits
2 Commits
main
...
marcelo/vo
Author | SHA1 | Date |
---|---|---|
![]() |
5e82f7999b | |
![]() |
200496710d |
|
@ -1,7 +1,6 @@
|
|||
import Foundation
|
||||
import SourceKittenFramework
|
||||
import SwiftSyntax
|
||||
|
||||
public struct VoidReturnRule: ConfigurationProviderRule, SubstitutionCorrectableRule {
|
||||
public struct VoidReturnRule: ConfigurationProviderRule, SwiftSyntaxCorrectableRule {
|
||||
public var configuration = SeverityConfiguration(.warning)
|
||||
|
||||
public init() {}
|
||||
|
@ -41,28 +40,83 @@ public struct VoidReturnRule: ConfigurationProviderRule, SubstitutionCorrectable
|
|||
]
|
||||
)
|
||||
|
||||
public func validate(file: SwiftLintFile) -> [StyleViolation] {
|
||||
return violationRanges(in: file).map {
|
||||
StyleViolation(ruleDescription: Self.description,
|
||||
severity: configuration.severity,
|
||||
location: Location(file: file, characterOffset: $0.location))
|
||||
}
|
||||
public func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor? {
|
||||
VoidReturnRuleVisitor()
|
||||
}
|
||||
|
||||
public func violationRanges(in file: SwiftLintFile) -> [NSRange] {
|
||||
let kinds = SyntaxKind.commentAndStringKinds
|
||||
let parensPattern = "\\(\\s*(?:Void)?\\s*\\)"
|
||||
let pattern = "->\\s*\(parensPattern)\\s*(?!->)"
|
||||
let excludingPattern = "(\(pattern))\\s*(throws\\s+)?->"
|
||||
|
||||
return file.match(pattern: pattern, excludingSyntaxKinds: kinds, excludingPattern: excludingPattern,
|
||||
exclusionMapping: { $0.range(at: 1) }).compactMap {
|
||||
let parensRegex = regex(parensPattern)
|
||||
return parensRegex.firstMatch(in: file.contents, options: [], range: $0)?.range
|
||||
public func makeRewriter(file: SwiftLintFile) -> ViolationsSyntaxRewriter? {
|
||||
file.locationConverter.map { locationConverter in
|
||||
VoidReturnRuleRewriter(
|
||||
locationConverter: locationConverter,
|
||||
disabledRegions: disabledRegions(file: file)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
public func substitution(for violationRange: NSRange, in file: SwiftLintFile) -> (NSRange, String)? {
|
||||
return (violationRange, "Void")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - VoidReturnRuleVisitor
|
||||
|
||||
private final class VoidReturnRuleVisitor: SyntaxVisitor, ViolationsSyntaxVisitor {
|
||||
private(set) var violationPositions: [AbsolutePosition] = []
|
||||
|
||||
override func visitPost(_ node: FunctionTypeSyntax) {
|
||||
guard let tuple = node.returnType.as(TupleTypeSyntax.self),
|
||||
tuple.shouldReturnVoid else {
|
||||
return
|
||||
}
|
||||
|
||||
violationPositions.append(tuple.positionAfterSkippingLeadingTrivia)
|
||||
}
|
||||
}
|
||||
|
||||
private extension TupleTypeSyntax {
|
||||
var shouldReturnVoid: Bool {
|
||||
if elements.isEmpty {
|
||||
return true
|
||||
}
|
||||
|
||||
if elements.count == 1,
|
||||
let identifier = elements.first?.type.as(SimpleTypeIdentifierSyntax.self),
|
||||
identifier.name.text == "Void" {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - VoidReturnRuleRewriter
|
||||
|
||||
private final class VoidReturnRuleRewriter: 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: FunctionTypeSyntax) -> TypeSyntax {
|
||||
guard let tuple = node.returnType.as(TupleTypeSyntax.self),
|
||||
tuple.shouldReturnVoid else {
|
||||
return super.visit(node)
|
||||
}
|
||||
|
||||
let isInDisabledRegion = disabledRegions.contains { region in
|
||||
region.contains(node.positionAfterSkippingLeadingTrivia, locationConverter: locationConverter)
|
||||
}
|
||||
|
||||
guard !isInDisabledRegion else {
|
||||
return super.visit(node)
|
||||
}
|
||||
|
||||
correctionPositions.append(tuple.positionAfterSkippingLeadingTrivia)
|
||||
|
||||
var returnType = SyntaxFactory.makeTypeIdentifier("Void")
|
||||
returnType.leadingTrivia = tuple.leadingTrivia
|
||||
returnType.trailingTrivia = tuple.trailingTrivia
|
||||
|
||||
return super.visit(node.withReturnType(returnType))
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue