xref: /freebsd-src/contrib/llvm-project/clang/lib/AST/CommentBriefParser.cpp (revision 0eae32dcef82f6f06de6419a0d623d7def0cc8f6)
10b57cec5SDimitry Andric //===--- CommentBriefParser.cpp - Dumb comment parser ---------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric 
90b57cec5SDimitry Andric #include "clang/AST/CommentBriefParser.h"
100b57cec5SDimitry Andric #include "clang/AST/CommentCommandTraits.h"
11*0eae32dcSDimitry Andric #include "clang/Basic/CharInfo.h"
120b57cec5SDimitry Andric 
130b57cec5SDimitry Andric namespace clang {
140b57cec5SDimitry Andric namespace comments {
150b57cec5SDimitry Andric 
160b57cec5SDimitry Andric namespace {
170b57cec5SDimitry Andric 
180b57cec5SDimitry Andric /// Convert all whitespace into spaces, remove leading and trailing spaces,
190b57cec5SDimitry Andric /// compress multiple spaces into one.
cleanupBrief(std::string & S)200b57cec5SDimitry Andric void cleanupBrief(std::string &S) {
210b57cec5SDimitry Andric   bool PrevWasSpace = true;
220b57cec5SDimitry Andric   std::string::iterator O = S.begin();
230b57cec5SDimitry Andric   for (std::string::iterator I = S.begin(), E = S.end();
240b57cec5SDimitry Andric        I != E; ++I) {
250b57cec5SDimitry Andric     const char C = *I;
26*0eae32dcSDimitry Andric     if (clang::isWhitespace(C)) {
270b57cec5SDimitry Andric       if (!PrevWasSpace) {
280b57cec5SDimitry Andric         *O++ = ' ';
290b57cec5SDimitry Andric         PrevWasSpace = true;
300b57cec5SDimitry Andric       }
310b57cec5SDimitry Andric     } else {
320b57cec5SDimitry Andric       *O++ = C;
330b57cec5SDimitry Andric       PrevWasSpace = false;
340b57cec5SDimitry Andric     }
350b57cec5SDimitry Andric   }
360b57cec5SDimitry Andric   if (O != S.begin() && *(O - 1) == ' ')
370b57cec5SDimitry Andric     --O;
380b57cec5SDimitry Andric 
390b57cec5SDimitry Andric   S.resize(O - S.begin());
400b57cec5SDimitry Andric }
410b57cec5SDimitry Andric 
isWhitespace(StringRef Text)420b57cec5SDimitry Andric bool isWhitespace(StringRef Text) {
43*0eae32dcSDimitry Andric   return llvm::all_of(Text, clang::isWhitespace);
440b57cec5SDimitry Andric }
450b57cec5SDimitry Andric } // unnamed namespace
460b57cec5SDimitry Andric 
BriefParser(Lexer & L,const CommandTraits & Traits)470b57cec5SDimitry Andric BriefParser::BriefParser(Lexer &L, const CommandTraits &Traits) :
480b57cec5SDimitry Andric     L(L), Traits(Traits) {
490b57cec5SDimitry Andric   // Get lookahead token.
500b57cec5SDimitry Andric   ConsumeToken();
510b57cec5SDimitry Andric }
520b57cec5SDimitry Andric 
Parse()530b57cec5SDimitry Andric std::string BriefParser::Parse() {
540b57cec5SDimitry Andric   std::string FirstParagraphOrBrief;
550b57cec5SDimitry Andric   std::string ReturnsParagraph;
560b57cec5SDimitry Andric   bool InFirstParagraph = true;
570b57cec5SDimitry Andric   bool InBrief = false;
580b57cec5SDimitry Andric   bool InReturns = false;
590b57cec5SDimitry Andric 
600b57cec5SDimitry Andric   while (Tok.isNot(tok::eof)) {
610b57cec5SDimitry Andric     if (Tok.is(tok::text)) {
620b57cec5SDimitry Andric       if (InFirstParagraph || InBrief)
630b57cec5SDimitry Andric         FirstParagraphOrBrief += Tok.getText();
640b57cec5SDimitry Andric       else if (InReturns)
650b57cec5SDimitry Andric         ReturnsParagraph += Tok.getText();
660b57cec5SDimitry Andric       ConsumeToken();
670b57cec5SDimitry Andric       continue;
680b57cec5SDimitry Andric     }
690b57cec5SDimitry Andric 
700b57cec5SDimitry Andric     if (Tok.is(tok::backslash_command) || Tok.is(tok::at_command)) {
710b57cec5SDimitry Andric       const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID());
720b57cec5SDimitry Andric       if (Info->IsBriefCommand) {
730b57cec5SDimitry Andric         FirstParagraphOrBrief.clear();
740b57cec5SDimitry Andric         InBrief = true;
750b57cec5SDimitry Andric         ConsumeToken();
760b57cec5SDimitry Andric         continue;
770b57cec5SDimitry Andric       }
780b57cec5SDimitry Andric       if (Info->IsReturnsCommand) {
790b57cec5SDimitry Andric         InReturns = true;
800b57cec5SDimitry Andric         InBrief = false;
810b57cec5SDimitry Andric         InFirstParagraph = false;
820b57cec5SDimitry Andric         ReturnsParagraph += "Returns ";
830b57cec5SDimitry Andric         ConsumeToken();
840b57cec5SDimitry Andric         continue;
850b57cec5SDimitry Andric       }
860b57cec5SDimitry Andric       // Block commands implicitly start a new paragraph.
870b57cec5SDimitry Andric       if (Info->IsBlockCommand) {
880b57cec5SDimitry Andric         // We found an implicit paragraph end.
890b57cec5SDimitry Andric         InFirstParagraph = false;
900b57cec5SDimitry Andric         if (InBrief)
910b57cec5SDimitry Andric           break;
920b57cec5SDimitry Andric       }
930b57cec5SDimitry Andric     }
940b57cec5SDimitry Andric 
950b57cec5SDimitry Andric     if (Tok.is(tok::newline)) {
960b57cec5SDimitry Andric       if (InFirstParagraph || InBrief)
970b57cec5SDimitry Andric         FirstParagraphOrBrief += ' ';
980b57cec5SDimitry Andric       else if (InReturns)
990b57cec5SDimitry Andric         ReturnsParagraph += ' ';
1000b57cec5SDimitry Andric       ConsumeToken();
1010b57cec5SDimitry Andric 
1020b57cec5SDimitry Andric       // If the next token is a whitespace only text, ignore it.  Thus we allow
1030b57cec5SDimitry Andric       // two paragraphs to be separated by line that has only whitespace in it.
1040b57cec5SDimitry Andric       //
1050b57cec5SDimitry Andric       // We don't need to add a space to the parsed text because we just added
1060b57cec5SDimitry Andric       // a space for the newline.
1070b57cec5SDimitry Andric       if (Tok.is(tok::text)) {
1080b57cec5SDimitry Andric         if (isWhitespace(Tok.getText()))
1090b57cec5SDimitry Andric           ConsumeToken();
1100b57cec5SDimitry Andric       }
1110b57cec5SDimitry Andric 
1120b57cec5SDimitry Andric       if (Tok.is(tok::newline)) {
1130b57cec5SDimitry Andric         ConsumeToken();
1140b57cec5SDimitry Andric         // We found a paragraph end.  This ends the brief description if
1150b57cec5SDimitry Andric         // \command or its equivalent was explicitly used.
1160b57cec5SDimitry Andric         // Stop scanning text because an explicit \paragraph is the
117349cc55cSDimitry Andric         // preferred one.
1180b57cec5SDimitry Andric         if (InBrief)
1190b57cec5SDimitry Andric           break;
1200b57cec5SDimitry Andric         // End first paragraph if we found some non-whitespace text.
1210b57cec5SDimitry Andric         if (InFirstParagraph && !isWhitespace(FirstParagraphOrBrief))
1220b57cec5SDimitry Andric           InFirstParagraph = false;
1230b57cec5SDimitry Andric         // End the \\returns paragraph because we found the paragraph end.
1240b57cec5SDimitry Andric         InReturns = false;
1250b57cec5SDimitry Andric       }
1260b57cec5SDimitry Andric       continue;
1270b57cec5SDimitry Andric     }
1280b57cec5SDimitry Andric 
1290b57cec5SDimitry Andric     // We didn't handle this token, so just drop it.
1300b57cec5SDimitry Andric     ConsumeToken();
1310b57cec5SDimitry Andric   }
1320b57cec5SDimitry Andric 
1330b57cec5SDimitry Andric   cleanupBrief(FirstParagraphOrBrief);
1340b57cec5SDimitry Andric   if (!FirstParagraphOrBrief.empty())
1350b57cec5SDimitry Andric     return FirstParagraphOrBrief;
1360b57cec5SDimitry Andric 
1370b57cec5SDimitry Andric   cleanupBrief(ReturnsParagraph);
1380b57cec5SDimitry Andric   return ReturnsParagraph;
1390b57cec5SDimitry Andric }
1400b57cec5SDimitry Andric 
1410b57cec5SDimitry Andric } // end namespace comments
1420b57cec5SDimitry Andric } // end namespace clang
1430b57cec5SDimitry Andric 
1440b57cec5SDimitry Andric 
145