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