@@ -4,17 +4,115 @@ import SwiftSyntax
44
55/// Documentation comments must use the `///` form.
66///
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 .
88///
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 .
1010///
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.
1614///
1715/// - 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+ }
1952
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 " )
20117}
118+
0 commit comments