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