1 | //===--- CommentBriefParser.cpp - Dumb comment parser ---------------------===// |
---|---|
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "clang/AST/CommentBriefParser.h" |
10 | #include "clang/AST/CommentCommandTraits.h" |
11 | #include "clang/Basic/CharInfo.h" |
12 | |
13 | namespace clang { |
14 | namespace comments { |
15 | |
16 | namespace { |
17 | |
18 | /// Convert all whitespace into spaces, remove leading and trailing spaces, |
19 | /// compress multiple spaces into one. |
20 | void cleanupBrief(std::string &S) { |
21 | bool PrevWasSpace = true; |
22 | std::string::iterator O = S.begin(); |
23 | for (std::string::iterator I = S.begin(), E = S.end(); |
24 | I != E; ++I) { |
25 | const char C = *I; |
26 | if (clang::isWhitespace(c: C)) { |
27 | if (!PrevWasSpace) { |
28 | *O++ = ' '; |
29 | PrevWasSpace = true; |
30 | } |
31 | } else { |
32 | *O++ = C; |
33 | PrevWasSpace = false; |
34 | } |
35 | } |
36 | if (O != S.begin() && *(O - 1) == ' ') |
37 | --O; |
38 | |
39 | S.resize(n: O - S.begin()); |
40 | } |
41 | |
42 | bool isWhitespace(StringRef Text) { |
43 | return llvm::all_of(Range&: Text, P: clang::isWhitespace); |
44 | } |
45 | } // unnamed namespace |
46 | |
47 | BriefParser::BriefParser(Lexer &L, const CommandTraits &Traits) : |
48 | L(L), Traits(Traits) { |
49 | // Get lookahead token. |
50 | ConsumeToken(); |
51 | } |
52 | |
53 | std::string BriefParser::Parse() { |
54 | std::string FirstParagraphOrBrief; |
55 | std::string ReturnsParagraph; |
56 | bool InFirstParagraph = true; |
57 | bool InBrief = false; |
58 | bool InReturns = false; |
59 | |
60 | while (Tok.isNot(K: tok::eof)) { |
61 | if (Tok.is(K: tok::text)) { |
62 | if (InFirstParagraph || InBrief) |
63 | FirstParagraphOrBrief += Tok.getText(); |
64 | else if (InReturns) |
65 | ReturnsParagraph += Tok.getText(); |
66 | ConsumeToken(); |
67 | continue; |
68 | } |
69 | |
70 | if (Tok.is(K: tok::backslash_command) || Tok.is(K: tok::at_command)) { |
71 | const CommandInfo *Info = Traits.getCommandInfo(CommandID: Tok.getCommandID()); |
72 | if (Info->IsBriefCommand) { |
73 | FirstParagraphOrBrief.clear(); |
74 | InBrief = true; |
75 | ConsumeToken(); |
76 | continue; |
77 | } |
78 | if (Info->IsReturnsCommand) { |
79 | InReturns = true; |
80 | InBrief = false; |
81 | InFirstParagraph = false; |
82 | ReturnsParagraph += "Returns "; |
83 | ConsumeToken(); |
84 | continue; |
85 | } |
86 | // Block commands implicitly start a new paragraph. |
87 | if (Info->IsBlockCommand) { |
88 | // We found an implicit paragraph end. |
89 | InFirstParagraph = false; |
90 | if (InBrief) |
91 | break; |
92 | } |
93 | } |
94 | |
95 | if (Tok.is(K: tok::newline)) { |
96 | if (InFirstParagraph || InBrief) |
97 | FirstParagraphOrBrief += ' '; |
98 | else if (InReturns) |
99 | ReturnsParagraph += ' '; |
100 | ConsumeToken(); |
101 | |
102 | // If the next token is a whitespace only text, ignore it. Thus we allow |
103 | // two paragraphs to be separated by line that has only whitespace in it. |
104 | // |
105 | // We don't need to add a space to the parsed text because we just added |
106 | // a space for the newline. |
107 | if (Tok.is(K: tok::text)) { |
108 | if (isWhitespace(Text: Tok.getText())) |
109 | ConsumeToken(); |
110 | } |
111 | |
112 | if (Tok.is(K: tok::newline)) { |
113 | ConsumeToken(); |
114 | // We found a paragraph end. This ends the brief description if |
115 | // \command or its equivalent was explicitly used. |
116 | // Stop scanning text because an explicit \paragraph is the |
117 | // preferred one. |
118 | if (InBrief) |
119 | break; |
120 | // End first paragraph if we found some non-whitespace text. |
121 | if (InFirstParagraph && !isWhitespace(Text: FirstParagraphOrBrief)) |
122 | InFirstParagraph = false; |
123 | // End the \\returns paragraph because we found the paragraph end. |
124 | InReturns = false; |
125 | } |
126 | continue; |
127 | } |
128 | |
129 | // We didn't handle this token, so just drop it. |
130 | ConsumeToken(); |
131 | } |
132 | |
133 | cleanupBrief(S&: FirstParagraphOrBrief); |
134 | if (!FirstParagraphOrBrief.empty()) |
135 | return FirstParagraphOrBrief; |
136 | |
137 | cleanupBrief(S&: ReturnsParagraph); |
138 | return ReturnsParagraph; |
139 | } |
140 | |
141 | } // end namespace comments |
142 | } // end namespace clang |
143 | |
144 | |
145 |