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