Skip to content

[clang-doc] add support for enums comments in html generation #101282

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Aug 16, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions clang-tools-extra/clang-doc/BitcodeReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,10 @@ template <> llvm::Expected<CommentInfo *> getCommentInfo(TypedefInfo *I) {
return &I->Description.emplace_back();
}

template <> llvm::Expected<CommentInfo *> getCommentInfo(EnumValueInfo *I) {
return &I->Description.emplace_back();
}

template <> llvm::Expected<CommentInfo *> getCommentInfo(CommentInfo *I) {
I->Children.emplace_back(std::make_unique<CommentInfo>());
return I->Children.back().get();
Expand Down
2 changes: 2 additions & 0 deletions clang-tools-extra/clang-doc/BitcodeWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,8 @@ void ClangDocBitcodeWriter::emitBlock(const EnumValueInfo &I) {
emitRecord(I.Name, ENUM_VALUE_NAME);
emitRecord(I.Value, ENUM_VALUE_VALUE);
emitRecord(I.ValueExpr, ENUM_VALUE_EXPR);
for (const auto &CI : I.Description)
emitBlock(CI);
}

void ClangDocBitcodeWriter::emitBlock(const RecordInfo &I) {
Expand Down
92 changes: 75 additions & 17 deletions clang-tools-extra/clang-doc/HTMLGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ class HTMLTag {
TAG_SPAN,
TAG_TITLE,
TAG_UL,
TAG_TABLE,
TAG_THEAD,
TAG_TBODY,
TAG_TR,
TAG_TD,
TAG_TH
};

HTMLTag() = default;
Expand Down Expand Up @@ -133,6 +139,12 @@ bool HTMLTag::isSelfClosing() const {
case HTMLTag::TAG_SPAN:
case HTMLTag::TAG_TITLE:
case HTMLTag::TAG_UL:
case HTMLTag::TAG_TABLE:
case HTMLTag::TAG_THEAD:
case HTMLTag::TAG_TBODY:
case HTMLTag::TAG_TR:
case HTMLTag::TAG_TD:
case HTMLTag::TAG_TH:
return false;
}
llvm_unreachable("Unhandled HTMLTag::TagType");
Expand Down Expand Up @@ -174,6 +186,18 @@ StringRef HTMLTag::toString() const {
return "title";
case HTMLTag::TAG_UL:
return "ul";
case HTMLTag::TAG_TABLE:
return "table";
case HTMLTag::TAG_THEAD:
return "thead";
case HTMLTag::TAG_TBODY:
return "tbody";
case HTMLTag::TAG_TR:
return "tr";
case HTMLTag::TAG_TD:
return "td";
case HTMLTag::TAG_TH:
return "th";
}
llvm_unreachable("Unhandled HTMLTag::TagType");
}
Expand Down Expand Up @@ -352,6 +376,7 @@ genHTML(const EnumInfo &I, const ClangDocContext &CDCtx);
static std::vector<std::unique_ptr<TagNode>>
genHTML(const FunctionInfo &I, const ClangDocContext &CDCtx,
StringRef ParentInfoDir);
static std::unique_ptr<TagNode> genHTML(const std::vector<CommentInfo> &C);

static std::vector<std::unique_ptr<TagNode>>
genEnumsBlock(const std::vector<EnumInfo> &Enums,
Expand All @@ -376,10 +401,27 @@ genEnumMembersBlock(const llvm::SmallVector<EnumValueInfo, 4> &Members) {
if (Members.empty())
return nullptr;

auto List = std::make_unique<TagNode>(HTMLTag::TAG_UL);
for (const auto &M : Members)
List->Children.emplace_back(
std::make_unique<TagNode>(HTMLTag::TAG_LI, M.Name));
auto List = std::make_unique<TagNode>(HTMLTag::TAG_TBODY);

for (const auto &M : Members) {
auto TRNode = std::make_unique<TagNode>(HTMLTag::TAG_TR);
TRNode->Children.emplace_back(
std::make_unique<TagNode>(HTMLTag::TAG_TD, M.Name));
// Use user supplied value if it exists, otherwise use the value
if (!M.ValueExpr.empty()) {
TRNode->Children.emplace_back(
std::make_unique<TagNode>(HTMLTag::TAG_TD, M.ValueExpr));
} else {
TRNode->Children.emplace_back(
std::make_unique<TagNode>(HTMLTag::TAG_TD, M.Value));
}
if (!M.Description.empty()) {
auto TD = std::make_unique<TagNode>(HTMLTag::TAG_TD);
TD->Children.emplace_back(genHTML(M.Description));
TRNode->Children.emplace_back(std::move(TD));
}
List->Children.emplace_back(std::move(TRNode));
}
return List;
}

Expand Down Expand Up @@ -619,7 +661,7 @@ static std::unique_ptr<HTMLNode> genHTML(const CommentInfo &I) {
}
return std::move(FullComment);
}

if (I.Kind == "ParagraphComment") {
auto ParagraphComment = std::make_unique<TagNode>(HTMLTag::TAG_P);
for (const auto &Child : I.Children) {
Expand Down Expand Up @@ -653,22 +695,38 @@ static std::vector<std::unique_ptr<TagNode>>
genHTML(const EnumInfo &I, const ClangDocContext &CDCtx) {
std::vector<std::unique_ptr<TagNode>> Out;
std::string EnumType = I.Scoped ? "enum class " : "enum ";

Out.emplace_back(
std::make_unique<TagNode>(HTMLTag::TAG_H3, EnumType + I.Name));
Out.back()->Attributes.emplace_back("id",
llvm::toHex(llvm::toStringRef(I.USR)));
// Determine if enum members have comments attached
bool HasComments =
std::any_of(I.Members.begin(), I.Members.end(),
[](const EnumValueInfo &M) { return M.Description.empty(); });
std::unique_ptr<TagNode> Table =
std::make_unique<TagNode>(HTMLTag::TAG_TABLE);
std::unique_ptr<TagNode> THead =
std::make_unique<TagNode>(HTMLTag::TAG_THEAD);
std::unique_ptr<TagNode> TRow = std::make_unique<TagNode>(HTMLTag::TAG_TR);
std::unique_ptr<TagNode> TD =
std::make_unique<TagNode>(HTMLTag::TAG_TH, EnumType + I.Name);
// Span 3 columns if enum has comments
TD->Attributes.emplace_back("colspan", HasComments ? "3" : "2");

Table->Attributes.emplace_back("id", llvm::toHex(llvm::toStringRef(I.USR)));
TRow->Children.emplace_back(std::move(TD));
THead->Children.emplace_back(std::move(TRow));
Table->Children.emplace_back(std::move(THead));

std::unique_ptr<TagNode> Node = genEnumMembersBlock(I.Members);

if (Node)
Out.emplace_back(std::move(Node));
Table->Children.emplace_back(std::move(Node));

Out.emplace_back(std::move(Table));

if (I.DefLoc) {
if (!CDCtx.RepositoryUrl)
Out.emplace_back(writeFileDefinition(*I.DefLoc));
else
Out.emplace_back(writeFileDefinition(
*I.DefLoc, StringRef{*CDCtx.RepositoryUrl}));
Out.emplace_back(
writeFileDefinition(*I.DefLoc, StringRef{*CDCtx.RepositoryUrl}));
}

std::string Description;
Expand Down Expand Up @@ -716,8 +774,8 @@ genHTML(const FunctionInfo &I, const ClangDocContext &CDCtx,
if (!CDCtx.RepositoryUrl)
Out.emplace_back(writeFileDefinition(*I.DefLoc));
else
Out.emplace_back(writeFileDefinition(
*I.DefLoc, StringRef{*CDCtx.RepositoryUrl}));
Out.emplace_back(
writeFileDefinition(*I.DefLoc, StringRef{*CDCtx.RepositoryUrl}));
}

std::string Description;
Expand Down Expand Up @@ -783,8 +841,8 @@ genHTML(const RecordInfo &I, Index &InfoIndex, const ClangDocContext &CDCtx,
if (!CDCtx.RepositoryUrl)
Out.emplace_back(writeFileDefinition(*I.DefLoc));
else
Out.emplace_back(writeFileDefinition(
*I.DefLoc, StringRef{*CDCtx.RepositoryUrl}));
Out.emplace_back(
writeFileDefinition(*I.DefLoc, StringRef{*CDCtx.RepositoryUrl}));
}

std::string Description;
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-doc/Representation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ void SymbolInfo::merge(SymbolInfo &&Other) {
}

NamespaceInfo::NamespaceInfo(SymbolID USR, StringRef Name, StringRef Path)
: Info(InfoType::IT_namespace, USR, Name, Path) {}
: Info(InfoType::IT_namespace, USR, Name, Path) {}

void NamespaceInfo::merge(NamespaceInfo &&Other) {
assert(mergeable(Other));
Expand Down
2 changes: 2 additions & 0 deletions clang-tools-extra/clang-doc/Representation.h
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,8 @@ struct EnumValueInfo {
// Stores the user-supplied initialization expression for this enumeration
// constant. This will be empty for implicit enumeration values.
SmallString<16> ValueExpr;

std::vector<CommentInfo> Description; /// Comment description of this field.
};

// TODO: Expand to allow for documenting templating.
Expand Down
18 changes: 14 additions & 4 deletions clang-tools-extra/clang-doc/Serialize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -394,10 +394,20 @@ static void parseEnumerators(EnumInfo &I, const EnumDecl *D) {
std::string ValueExpr;
if (const Expr *InitExpr = E->getInitExpr())
ValueExpr = getSourceCode(D, InitExpr->getSourceRange());

SmallString<16> ValueStr;
E->getInitVal().toString(ValueStr);
I.Members.emplace_back(E->getNameAsString(), ValueStr, ValueExpr);
I.Members.emplace_back(E->getNameAsString(), ValueStr.str(), ValueExpr);
ASTContext &Context = E->getASTContext();
if (RawComment *Comment =
E->getASTContext().getRawCommentForDeclNoCache(E)) {
CommentInfo CInfo;
Comment->setAttached();
if (comments::FullComment *Fc = Comment->parse(Context, nullptr, E)) {
EnumValueInfo &Member = I.Members.back();
Member.Description.emplace_back();
parseFullComment(Fc, Member.Description.back());
}
}
}
}

Expand Down Expand Up @@ -558,15 +568,15 @@ static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D) {
assert(D && "Expect non-null FieldDecl in populateMemberTypeInfo");

ASTContext& Context = D->getASTContext();
ASTContext &Context = D->getASTContext();
// TODO investigate whether we can use ASTContext::getCommentForDecl instead
// of this logic. See also similar code in Mapper.cpp.
RawComment *Comment = Context.getRawCommentForDeclNoCache(D);
if (!Comment)
return;

Comment->setAttached();
if (comments::FullComment* fc = Comment->parse(Context, nullptr, D)) {
if (comments::FullComment *fc = Comment->parse(Context, nullptr, D)) {
I.Description.emplace_back();
parseFullComment(fc, I.Description.back());
}
Expand Down
Loading
Loading