@@ -25,14 +25,82 @@ public final class ReturnVoidInsteadOfEmptyTuple: SyntaxFormatRule {
25
25
public override func visit( _ node: FunctionTypeSyntax ) -> TypeSyntax {
26
26
guard let returnType = node. returnType. as ( TupleTypeSyntax . self) ,
27
27
returnType. elements. count == 0
28
- else { return TypeSyntax ( node) }
29
- diagnose ( . returnVoid, on: node. returnType)
30
- let voidKeyword = SyntaxFactory . makeSimpleTypeIdentifier (
28
+ else {
29
+ return super. visit ( node)
30
+ }
31
+
32
+ diagnose ( . returnVoid, on: returnType)
33
+
34
+ // If the user has put non-whitespace trivia inside the empty tuple, like a comment, then we
35
+ // still diagnose it as a lint error but we don't replace it because it's not obvious where the
36
+ // comment should go.
37
+ if hasNonWhitespaceLeadingTrivia ( returnType. rightParen) {
38
+ return super. visit ( node)
39
+ }
40
+
41
+ // Make sure that function types nested in the argument list are also rewritten (for example,
42
+ // `(Int -> ()) -> ()` should become `(Int -> Void) -> Void`).
43
+ let arguments = visit ( node. arguments) . as ( TupleTypeElementListSyntax . self) !
44
+ let voidKeyword = makeVoidIdentifierType ( toReplace: returnType)
45
+ return TypeSyntax ( node. withArguments ( arguments) . withReturnType ( TypeSyntax ( voidKeyword) ) )
46
+ }
47
+
48
+ public override func visit( _ node: ClosureSignatureSyntax ) -> Syntax {
49
+ guard let output = node. output,
50
+ let returnType = output. returnType. as ( TupleTypeSyntax . self) ,
51
+ returnType. elements. count == 0
52
+ else {
53
+ return super. visit ( node)
54
+ }
55
+
56
+ diagnose ( . returnVoid, on: returnType)
57
+
58
+ // If the user has put non-whitespace trivia inside the empty tuple, like a comment, then we
59
+ // still diagnose it as a lint error but we don't replace it because it's not obvious where the
60
+ // comment should go.
61
+ if hasNonWhitespaceLeadingTrivia ( returnType. rightParen) {
62
+ return super. visit ( node)
63
+ }
64
+
65
+ let input : Syntax ?
66
+ if let parameterClause = node. input? . as ( ParameterClauseSyntax . self) {
67
+ // If the closure input is a complete parameter clause (variables and types), make sure that
68
+ // nested function types are also rewritten (for example, `label: (Int -> ()) -> ()` should
69
+ // become `label: (Int -> Void) -> Void`).
70
+ input = visit ( parameterClause)
71
+ } else {
72
+ // Otherwise, it's a simple signature (just variable names, no types), so there is nothing to
73
+ // rewrite.
74
+ input = node. input
75
+ }
76
+ let voidKeyword = makeVoidIdentifierType ( toReplace: returnType)
77
+ return Syntax ( node. withInput ( input) . withOutput ( output. withReturnType ( TypeSyntax ( voidKeyword) ) ) )
78
+ }
79
+
80
+ /// Returns a value indicating whether the leading trivia of the given token contained any
81
+ /// non-whitespace pieces.
82
+ private func hasNonWhitespaceLeadingTrivia( _ token: TokenSyntax ) -> Bool {
83
+ for piece in token. leadingTrivia {
84
+ switch piece {
85
+ case . blockComment, . docBlockComment, . docLineComment, . garbageText, . lineComment:
86
+ return true
87
+ default :
88
+ break
89
+ }
90
+ }
91
+ return false
92
+ }
93
+
94
+ /// Returns a type syntax node with the identifier `Void` whose leading and trailing trivia have
95
+ /// been copied from the tuple type syntax node it is replacing.
96
+ private func makeVoidIdentifierType( toReplace node: TupleTypeSyntax ) -> SimpleTypeIdentifierSyntax
97
+ {
98
+ return SyntaxFactory . makeSimpleTypeIdentifier (
31
99
name: SyntaxFactory . makeIdentifier (
32
100
" Void " ,
33
- trailingTrivia: returnType. rightParen. trailingTrivia) ,
101
+ leadingTrivia: node. firstToken? . leadingTrivia ?? [ ] ,
102
+ trailingTrivia: node. lastToken? . trailingTrivia ?? [ ] ) ,
34
103
genericArgumentClause: nil )
35
- return TypeSyntax ( node. withReturnType ( TypeSyntax ( voidKeyword) ) )
36
104
}
37
105
}
38
106
0 commit comments