@@ -4,17 +4,115 @@ import SwiftSyntax
4
4
5
5
/// Documentation comments must use the `///` form.
6
6
///
7
- /// Flag comments (e.g. `// TODO(username):`) are exempted from this rule .
7
+ /// This is similar to `NoBlockComments` but is meant to prevent documentation block comments .
8
8
///
9
- /// This is similar to `NoBlockComments` but is meant to prevent multi-line comments that use `//` .
9
+ /// Lint: If a doc block comment appears, a lint error is raised .
10
10
///
11
- /// Lint: If a declaration has a multi-line comment preceding it and that comment is not in `///`
12
- /// form, a lint error is raised.
13
- ///
14
- /// Format: If a declaration has a multi-line comment preceding it and that comment is not in `///`
15
- /// form, it is converted to the `///` form.
11
+ /// Format: If a doc block comment appears on its own on a line, or if a doc block comment spans multiple
12
+ /// lines without appearing on the same line as code, it will be replaced with multiple
13
+ /// doc line comments.
16
14
///
17
15
/// - SeeAlso: https://google.github.io/swift#general-format
18
- public final class UseTripleSlashForDocumentationComments {
16
+ public final class UseTripleSlashForDocumentationComments : SyntaxFormatRule {
17
+ public override func visit( _ node: FunctionDeclSyntax ) -> DeclSyntax {
18
+ return convertDocBlockCommentToDocLineComment ( node)
19
+ }
20
+
21
+ public override func visit( _ node: EnumDeclSyntax ) -> DeclSyntax {
22
+ return convertDocBlockCommentToDocLineComment ( node)
23
+ }
24
+
25
+ public override func visit( _ node: InitializerDeclSyntax ) -> DeclSyntax {
26
+ return convertDocBlockCommentToDocLineComment ( node)
27
+ }
28
+
29
+ public override func visit( _ node: DeinitializerDeclSyntax ) -> DeclSyntax {
30
+ return convertDocBlockCommentToDocLineComment ( node)
31
+ }
32
+
33
+ public override func visit( _ node: SubscriptDeclSyntax ) -> DeclSyntax {
34
+ return convertDocBlockCommentToDocLineComment ( node)
35
+ }
36
+
37
+ public override func visit( _ node: ClassDeclSyntax ) -> DeclSyntax {
38
+ return convertDocBlockCommentToDocLineComment ( node)
39
+ }
40
+
41
+ public override func visit( _ node: VariableDeclSyntax ) -> DeclSyntax {
42
+ return convertDocBlockCommentToDocLineComment ( node)
43
+ }
44
+
45
+ public override func visit( _ node: StructDeclSyntax ) -> DeclSyntax {
46
+ return convertDocBlockCommentToDocLineComment ( node)
47
+ }
48
+
49
+ public override func visit( _ node: ProtocolDeclSyntax ) -> DeclSyntax {
50
+ return convertDocBlockCommentToDocLineComment ( node)
51
+ }
19
52
53
+ public override func visit( _ node: TypealiasDeclSyntax ) -> DeclSyntax {
54
+ return convertDocBlockCommentToDocLineComment ( node)
55
+ }
56
+
57
+ public override func visit( _ node: ExtensionDeclSyntax ) -> DeclSyntax {
58
+ return convertDocBlockCommentToDocLineComment ( node)
59
+ }
60
+
61
+ /// In the case the given declaration has a docBlockComment as it's documentation
62
+ /// comment. Returns the declaration with the docBlockComment converted to
63
+ /// a docLineComment.
64
+ func convertDocBlockCommentToDocLineComment( _ decl: DeclSyntax ) -> DeclSyntax {
65
+ guard let commentText = decl. docComment else { return decl }
66
+ guard let declLeadinTrivia = decl. leadingTrivia else { return decl }
67
+ let docComments = commentText. components ( separatedBy: " \n " )
68
+ var pieces = [ TriviaPiece] ( )
69
+
70
+ // Ensures the documentation comment is a docLineComment.
71
+ var hasFoundDocComment = false
72
+ for piece in declLeadinTrivia. reversed ( ) {
73
+ if case . docBlockComment( _) = piece, !hasFoundDocComment {
74
+ hasFoundDocComment = true
75
+ diagnose ( . avoidDocBlockComment, on: decl)
76
+ pieces. append ( contentsOf: separateDocBlockIntoPieces ( docComments) . reversed ( ) )
77
+ }
78
+ else {
79
+ pieces. append ( piece)
80
+ }
81
+ }
82
+
83
+ return !hasFoundDocComment ? decl :
84
+ replaceTrivia (
85
+ on: decl,
86
+ token: decl. firstToken,
87
+ leadingTrivia: Trivia ( pieces: pieces. reversed ( ) )
88
+ ) as! DeclSyntax
89
+ }
90
+
91
+ /// Breaks down the docBlock comment into the correct trivia pieces
92
+ /// for a docLineComment.
93
+ func separateDocBlockIntoPieces( _ docComments: [ String ] ) -> [ TriviaPiece ]
94
+ {
95
+ var pieces = [ TriviaPiece] ( )
96
+ for lineText in docComments. dropLast ( ) {
97
+ // Adds an space as indentation for the lines that needed it.
98
+ let docLineMark = lineText. first == " " ||
99
+ lineText. trimmingCharacters ( in: . whitespaces) == " " ? " /// " : " /// "
100
+ pieces. append ( . docLineComment( docLineMark + lineText) )
101
+ pieces. append ( . newlines( 1 ) )
102
+ }
103
+
104
+ // The last piece doesn't need a newline after it.
105
+ if docComments. last!. trimmingCharacters ( in: . whitespaces) != " " {
106
+ let docLineMark = docComments. last!. first == " " ||
107
+ docComments. last!. trimmingCharacters ( in: . whitespaces) == " " ? " /// " : " /// "
108
+ pieces. append ( . docLineComment( docLineMark + docComments. last!) )
109
+ }
110
+ return pieces
111
+ }
112
+ }
113
+
114
+ extension Diagnostic . Message {
115
+ static let avoidDocBlockComment =
116
+ Diagnostic . Message ( . warning, " Documentation block comments are not allowed " )
20
117
}
118
+
0 commit comments