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     } else {
32       *O++ = C;
33       PrevWasSpace = false;
34     }
35   }
36   if (O != S.begin() && *(O - 1) == ' ')
37     --O;
38 
39   S.resize(O - S.begin());
40 }
41 
42 bool isWhitespace(StringRef Text) {
43   return llvm::all_of(Text, 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(tok::eof)) {
61     if (Tok.is(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(tok::backslash_command) || Tok.is(tok::at_command)) {
71       const CommandInfo *Info = Traits.getCommandInfo(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(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(tok::text)) {
108         if (isWhitespace(Tok.getText()))
109           ConsumeToken();
110       }
111 
112       if (Tok.is(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(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(FirstParagraphOrBrief);
134   if (!FirstParagraphOrBrief.empty())
135     return FirstParagraphOrBrief;
136 
137   cleanupBrief(ReturnsParagraph);
138   return ReturnsParagraph;
139 }
140 
141 } // end namespace comments
142 } // end namespace clang
143 
144 
145