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