blob: 2a5f7452b776b0e3c8b9dc1a294ccc2d56e2935d [file] [log] [blame]
//===--- CommentBriefParser.cpp - Dumb comment parser ---------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "clang/AST/CommentBriefParser.h"
#include "clang/AST/CommentCommandTraits.h"
namespace clang {
namespace comments {
namespace {
inline bool isWhitespace(char C) {
return C == ' ' || C == '\n' || C == '\r' ||
C == '\t' || C == '\f' || C == '\v';
}
/// Convert all whitespace into spaces, remove leading and trailing spaces,
/// compress multiple spaces into one.
void cleanupBrief(std::string &S) {
bool PrevWasSpace = true;
std::string::iterator O = S.begin();
for (std::string::iterator I = S.begin(), E = S.end();
I != E; ++I) {
const char C = *I;
if (isWhitespace(C)) {
if (!PrevWasSpace) {
*O++ = ' ';
PrevWasSpace = true;
}
continue;
} else {
*O++ = C;
PrevWasSpace = false;
}
}
if (O != S.begin() && *(O - 1) == ' ')
--O;
S.resize(O - S.begin());
}
bool isWhitespace(StringRef Text) {
for (StringRef::const_iterator I = Text.begin(), E = Text.end();
I != E; ++I) {
if (!isWhitespace(*I))
return false;
}
return true;
}
} // unnamed namespace
BriefParser::BriefParser(Lexer &L, const CommandTraits &Traits) :
L(L), Traits(Traits) {
// Get lookahead token.
ConsumeToken();
}
std::string BriefParser::Parse() {
std::string FirstParagraphOrBrief;
std::string ReturnsParagraph;
bool InFirstParagraph = true;
bool InBrief = false;
bool InReturns = false;
while (Tok.isNot(tok::eof)) {
if (Tok.is(tok::text)) {
if (InFirstParagraph || InBrief)
FirstParagraphOrBrief += Tok.getText();
else if (InReturns)
ReturnsParagraph += Tok.getText();
ConsumeToken();
continue;
}
if (Tok.is(tok::backslash_command) || Tok.is(tok::at_command)) {
const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID());
if (Info->IsBriefCommand) {
FirstParagraphOrBrief.clear();
InBrief = true;
ConsumeToken();
continue;
}
if (Info->IsReturnsCommand) {
InReturns = true;
InBrief = false;
InFirstParagraph = false;
ReturnsParagraph += "Returns ";
ConsumeToken();
continue;
}
// Block commands implicitly start a new paragraph.
if (Info->IsBlockCommand) {
// We found an implicit paragraph end.
InFirstParagraph = false;
if (InBrief)
break;
}
}
if (Tok.is(tok::newline)) {
if (InFirstParagraph || InBrief)
FirstParagraphOrBrief += ' ';
else if (InReturns)
ReturnsParagraph += ' ';
ConsumeToken();
// If the next token is a whitespace only text, ignore it. Thus we allow
// two paragraphs to be separated by line that has only whitespace in it.
//
// We don't need to add a space to the parsed text because we just added
// a space for the newline.
if (Tok.is(tok::text)) {
if (isWhitespace(Tok.getText()))
ConsumeToken();
}
if (Tok.is(tok::newline)) {
ConsumeToken();
// We found a paragraph end. This ends the brief description if
// \command or its equivalent was explicitly used.
// Stop scanning text because an explicit \paragraph is the
// preferred one.
if (InBrief)
break;
// End first paragraph if we found some non-whitespace text.
if (InFirstParagraph && !isWhitespace(FirstParagraphOrBrief))
InFirstParagraph = false;
// End the \\returns paragraph because we found the paragraph end.
InReturns = false;
}
continue;
}
// We didn't handle this token, so just drop it.
ConsumeToken();
}
cleanupBrief(FirstParagraphOrBrief);
if (!FirstParagraphOrBrief.empty())
return FirstParagraphOrBrief;
cleanupBrief(ReturnsParagraph);
return ReturnsParagraph;
}
} // end namespace comments
} // end namespace clang