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 {
isWhitespace(char C)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.
cleanupBrief(std::string & S)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 
isWhitespace(StringRef Text)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 
BriefParser(Lexer & L,const CommandTraits & Traits)56 BriefParser::BriefParser(Lexer &L, const CommandTraits &Traits) :
57     L(L), Traits(Traits) {
58   // Get lookahead token.
59   ConsumeToken();
60 }
61 
Parse()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