import SwiftSyntax @SwiftSyntaxRule(explicitRewriter: true) struct ClosingBraceRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( identifier: "closing_brace", name: "Closing Brace Spacing", description: "Closing brace with closing parenthesis should not have any whitespaces in the middle", kind: .style, nonTriggeringExamples: [ Example("[].map({ })"), Example("[].map(\n { }\n)"), ], triggeringExamples: [ Example("[].map({ ↓} )"), Example("[].map({ ↓}\t)"), ], corrections: [ Example("[].map({ ↓} )"): Example("[].map({ })") ] ) } private extension ClosingBraceRule { final class Visitor: ViolationsSyntaxVisitor { override func visitPost(_ node: TokenSyntax) { if node.hasClosingBraceViolation { violations.append(node.positionAfterSkippingLeadingTrivia) } } } final class Rewriter: ViolationsSyntaxRewriter { override func visit(_ node: TokenSyntax) -> TokenSyntax { guard node.hasClosingBraceViolation else { return super.visit(node) } numberOfCorrections += 1 return super.visit(node.with(\.trailingTrivia, Trivia())) } } } private extension TokenSyntax { var hasClosingBraceViolation: Bool { guard tokenKind == .rightBrace, let nextToken = nextToken(viewMode: .sourceAccurate), nextToken.tokenKind == .rightParen else { return false } let isImmediatelyNext = positionAfterSkippingLeadingTrivia == nextToken.positionAfterSkippingLeadingTrivia - SourceLength(utf8Length: 1) if isImmediatelyNext || nextToken.hasLeadingNewline { return false } return true } private var hasLeadingNewline: Bool { leadingTrivia.contains { piece in if case .newlines = piece { return true } return false } } }