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