Skip to content

Commit

Permalink
Rewrite void_return rule with SwiftSyntax (#5351)
Browse files Browse the repository at this point in the history
  • Loading branch information
SimplyDanny authored Nov 15, 2023
1 parent fb4ef95 commit 9fbc04e
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 20 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@

#### Enhancements

* Rewrite `void_return` rule with SwiftSyntax.
[SimplyDanny](https://github.com/SimplyDanny)

* Print invalid keys when configuration parsing fails.
[SimplyDanny](https://github.com/SimplyDanny)
[#5347](https://github.com/realm/SwiftLint/pull/5347)
Expand Down
49 changes: 29 additions & 20 deletions Source/SwiftLintBuiltInRules/Rules/Style/VoidReturnRule.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import Foundation
import SourceKittenFramework
import SwiftLintCore
import SwiftSyntax

struct VoidReturnRule: SubstitutionCorrectableRule {
@SwiftSyntaxRule(explicitRewriter: true)
struct VoidReturnRule: Rule {
var configuration = SeverityConfiguration<Self>(.warning)

static let description = RuleDescription(
Expand Down Expand Up @@ -46,29 +47,37 @@ struct VoidReturnRule: SubstitutionCorrectableRule {
Example("func foo() async throws -> ↓()"): Example("func foo() async throws -> Void")
]
)
}

func validate(file: SwiftLintFile) -> [StyleViolation] {
return violationRanges(in: file).map {
StyleViolation(ruleDescription: Self.description,
severity: configuration.severity,
location: Location(file: file, characterOffset: $0.location))
private extension VoidReturnRule {
final class Visitor: ViolationsSyntaxVisitor<ConfigurationType> {
override func visitPost(_ node: ReturnClauseSyntax) {
if node.violates {
violations.append(node.type.positionAfterSkippingLeadingTrivia)
}
}
}

func violationRanges(in file: SwiftLintFile) -> [NSRange] {
let kinds = SyntaxKind.commentAndStringKinds
let parensPattern = "\\(\\s*(?:Void)?\\s*\\)"
let pattern = "->\\s*\(parensPattern)\\s*(?!->)"
let excludingPattern = "(\(pattern))\\s*(async\\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
final class Rewriter: ViolationsSyntaxRewriter {
override func visit(_ node: ReturnClauseSyntax) -> ReturnClauseSyntax {
if node.violates {
correctionPositions.append(node.type.positionAfterSkippingLeadingTrivia)
let node = node
.with(\.type, TypeSyntax(IdentifierTypeSyntax(name: "Void")))
.with(\.trailingTrivia, node.type.trailingTrivia)
return super.visit(node)
}
return super.visit(node)
}
}
}

func substitution(for violationRange: NSRange, in file: SwiftLintFile) -> (NSRange, String)? {
return (violationRange, "Void")
private extension ReturnClauseSyntax {
var violates: Bool {
if let type = type.as(TupleTypeSyntax.self) {
let elements = type.elements
return elements.isEmpty || elements.onlyElement?.type.as(IdentifierTypeSyntax.self)?.name.text == "Void"
}
return false
}
}

0 comments on commit 9fbc04e

Please sign in to comment.