15188c4b9SDmitri Gribenko //===--- CommentBriefParser.cpp - Dumb comment parser ---------------------===//
25188c4b9SDmitri Gribenko //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65188c4b9SDmitri Gribenko //
75188c4b9SDmitri Gribenko //===----------------------------------------------------------------------===//
85188c4b9SDmitri Gribenko 
95188c4b9SDmitri Gribenko #include "clang/AST/CommentBriefParser.h"
10ca7f80adSDmitri Gribenko #include "clang/AST/CommentCommandTraits.h"
11*ea68ce2aSKazu Hirata #include "clang/Basic/CharInfo.h"
125188c4b9SDmitri Gribenko 
135188c4b9SDmitri Gribenko namespace clang {
145188c4b9SDmitri Gribenko namespace comments {
155188c4b9SDmitri Gribenko 
160743f946SDmitri Gribenko namespace {
1775eea899SDmitri Gribenko 
180743f946SDmitri Gribenko /// Convert all whitespace into spaces, remove leading and trailing spaces,
190743f946SDmitri Gribenko /// compress multiple spaces into one.
cleanupBrief(std::string & S)200743f946SDmitri Gribenko void cleanupBrief(std::string &S) {
210743f946SDmitri Gribenko   bool PrevWasSpace = true;
220743f946SDmitri Gribenko   std::string::iterator O = S.begin();
230743f946SDmitri Gribenko   for (std::string::iterator I = S.begin(), E = S.end();
240743f946SDmitri Gribenko        I != E; ++I) {
250743f946SDmitri Gribenko     const char C = *I;
26*ea68ce2aSKazu Hirata     if (clang::isWhitespace(C)) {
270743f946SDmitri Gribenko       if (!PrevWasSpace) {
280743f946SDmitri Gribenko         *O++ = ' ';
290743f946SDmitri Gribenko         PrevWasSpace = true;
300743f946SDmitri Gribenko       }
310743f946SDmitri Gribenko     } else {
320743f946SDmitri Gribenko       *O++ = C;
330743f946SDmitri Gribenko       PrevWasSpace = false;
340743f946SDmitri Gribenko     }
350743f946SDmitri Gribenko   }
360743f946SDmitri Gribenko   if (O != S.begin() && *(O - 1) == ' ')
370743f946SDmitri Gribenko     --O;
380743f946SDmitri Gribenko 
390743f946SDmitri Gribenko   S.resize(O - S.begin());
400743f946SDmitri Gribenko }
4175eea899SDmitri Gribenko 
isWhitespace(StringRef Text)4275eea899SDmitri Gribenko bool isWhitespace(StringRef Text) {
43*ea68ce2aSKazu Hirata   return llvm::all_of(Text, clang::isWhitespace);
4475eea899SDmitri Gribenko }
450743f946SDmitri Gribenko } // unnamed namespace
460743f946SDmitri Gribenko 
BriefParser(Lexer & L,const CommandTraits & Traits)47ca7f80adSDmitri Gribenko BriefParser::BriefParser(Lexer &L, const CommandTraits &Traits) :
48ca7f80adSDmitri Gribenko     L(L), Traits(Traits) {
49ca7f80adSDmitri Gribenko   // Get lookahead token.
50ca7f80adSDmitri Gribenko   ConsumeToken();
51ca7f80adSDmitri Gribenko }
52ca7f80adSDmitri Gribenko 
Parse()535188c4b9SDmitri Gribenko std::string BriefParser::Parse() {
5477369eeaSDmitri Gribenko   std::string FirstParagraphOrBrief;
5577369eeaSDmitri Gribenko   std::string ReturnsParagraph;
565188c4b9SDmitri Gribenko   bool InFirstParagraph = true;
575188c4b9SDmitri Gribenko   bool InBrief = false;
5877369eeaSDmitri Gribenko   bool InReturns = false;
595188c4b9SDmitri Gribenko 
605188c4b9SDmitri Gribenko   while (Tok.isNot(tok::eof)) {
615188c4b9SDmitri Gribenko     if (Tok.is(tok::text)) {
6299e0942cSDmitri Gribenko       if (InFirstParagraph || InBrief)
6377369eeaSDmitri Gribenko         FirstParagraphOrBrief += Tok.getText();
6477369eeaSDmitri Gribenko       else if (InReturns)
6577369eeaSDmitri Gribenko         ReturnsParagraph += Tok.getText();
665188c4b9SDmitri Gribenko       ConsumeToken();
675188c4b9SDmitri Gribenko       continue;
685188c4b9SDmitri Gribenko     }
695188c4b9SDmitri Gribenko 
70e400cb70SFariborz Jahanian     if (Tok.is(tok::backslash_command) || Tok.is(tok::at_command)) {
717acbf00fSDmitri Gribenko       const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID());
727acbf00fSDmitri Gribenko       if (Info->IsBriefCommand) {
7377369eeaSDmitri Gribenko         FirstParagraphOrBrief.clear();
745188c4b9SDmitri Gribenko         InBrief = true;
755188c4b9SDmitri Gribenko         ConsumeToken();
765188c4b9SDmitri Gribenko         continue;
775188c4b9SDmitri Gribenko       }
787acbf00fSDmitri Gribenko       if (Info->IsReturnsCommand) {
7977369eeaSDmitri Gribenko         InReturns = true;
8075eea899SDmitri Gribenko         InBrief = false;
8175eea899SDmitri Gribenko         InFirstParagraph = false;
8277369eeaSDmitri Gribenko         ReturnsParagraph += "Returns ";
8375eea899SDmitri Gribenko         ConsumeToken();
8475eea899SDmitri Gribenko         continue;
8577369eeaSDmitri Gribenko       }
86025d518eSDmitri Gribenko       // Block commands implicitly start a new paragraph.
877acbf00fSDmitri Gribenko       if (Info->IsBlockCommand) {
88a1e9c8e7SDmitri Gribenko         // We found an implicit paragraph end.
89a1e9c8e7SDmitri Gribenko         InFirstParagraph = false;
90767ea0ffSDmitri Gribenko         if (InBrief)
91a1e9c8e7SDmitri Gribenko           break;
92a1e9c8e7SDmitri Gribenko       }
93a1e9c8e7SDmitri Gribenko     }
945188c4b9SDmitri Gribenko 
955188c4b9SDmitri Gribenko     if (Tok.is(tok::newline)) {
9699e0942cSDmitri Gribenko       if (InFirstParagraph || InBrief)
9777369eeaSDmitri Gribenko         FirstParagraphOrBrief += ' ';
9877369eeaSDmitri Gribenko       else if (InReturns)
9977369eeaSDmitri Gribenko         ReturnsParagraph += ' ';
1005188c4b9SDmitri Gribenko       ConsumeToken();
1015188c4b9SDmitri Gribenko 
10275eea899SDmitri Gribenko       // If the next token is a whitespace only text, ignore it.  Thus we allow
10375eea899SDmitri Gribenko       // two paragraphs to be separated by line that has only whitespace in it.
10475eea899SDmitri Gribenko       //
10575eea899SDmitri Gribenko       // We don't need to add a space to the parsed text because we just added
10675eea899SDmitri Gribenko       // a space for the newline.
10775eea899SDmitri Gribenko       if (Tok.is(tok::text)) {
10875eea899SDmitri Gribenko         if (isWhitespace(Tok.getText()))
10975eea899SDmitri Gribenko           ConsumeToken();
11075eea899SDmitri Gribenko       }
11175eea899SDmitri Gribenko 
1125188c4b9SDmitri Gribenko       if (Tok.is(tok::newline)) {
1135188c4b9SDmitri Gribenko         ConsumeToken();
11475eea899SDmitri Gribenko         // We found a paragraph end.  This ends the brief description if
1159fc8faf9SAdrian Prantl         // \command or its equivalent was explicitly used.
1169fc8faf9SAdrian Prantl         // Stop scanning text because an explicit \paragraph is the
11760ab6861SNico Weber         // preferred one.
118767ea0ffSDmitri Gribenko         if (InBrief)
119a1e9c8e7SDmitri Gribenko           break;
12075eea899SDmitri Gribenko         // End first paragraph if we found some non-whitespace text.
12175eea899SDmitri Gribenko         if (InFirstParagraph && !isWhitespace(FirstParagraphOrBrief))
12275eea899SDmitri Gribenko           InFirstParagraph = false;
12375eea899SDmitri Gribenko         // End the \\returns paragraph because we found the paragraph end.
12475eea899SDmitri Gribenko         InReturns = false;
1255188c4b9SDmitri Gribenko       }
1265188c4b9SDmitri Gribenko       continue;
1275188c4b9SDmitri Gribenko     }
1285188c4b9SDmitri Gribenko 
1295188c4b9SDmitri Gribenko     // We didn't handle this token, so just drop it.
1305188c4b9SDmitri Gribenko     ConsumeToken();
1315188c4b9SDmitri Gribenko   }
1325188c4b9SDmitri Gribenko 
13377369eeaSDmitri Gribenko   cleanupBrief(FirstParagraphOrBrief);
13477369eeaSDmitri Gribenko   if (!FirstParagraphOrBrief.empty())
13577369eeaSDmitri Gribenko     return FirstParagraphOrBrief;
13677369eeaSDmitri Gribenko 
13777369eeaSDmitri Gribenko   cleanupBrief(ReturnsParagraph);
13877369eeaSDmitri Gribenko   return ReturnsParagraph;
1395188c4b9SDmitri Gribenko }
1405188c4b9SDmitri Gribenko 
1415188c4b9SDmitri Gribenko } // end namespace comments
1425188c4b9SDmitri Gribenko } // end namespace clang
1435188c4b9SDmitri Gribenko 
1445188c4b9SDmitri Gribenko 
145