[clang-tools-extra] [clang-doc] Use llvm RTTI over handrolled casting (PR #202059)
via cfe-commits
cfe-commits at lists.llvm.org
Sat Jun 6 09:35:23 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-tools-extra
Author: Paul Kirth (ilovepi)
<details>
<summary>Changes</summary>
Clang-Doc has a limited amount of polymorphism over Info types.
Historically, these have just been cast directly in a few places, but we
can use the existing llvm RTTI implementation to more rigorously
dispatch and query the types involved with only limited extra code.
This should make future changes a bit harder to get wrong.
---
Full diff: https://github.com/llvm/llvm-project/pull/202059.diff
8 Files Affected:
- (modified) clang-tools-extra/clang-doc/BitcodeWriter.cpp (+8-8)
- (modified) clang-tools-extra/clang-doc/JSONGenerator.cpp (+5-5)
- (modified) clang-tools-extra/clang-doc/MDGenerator.cpp (+5-5)
- (modified) clang-tools-extra/clang-doc/Representation.cpp (+17-17)
- (modified) clang-tools-extra/clang-doc/Representation.h (+48)
- (modified) clang-tools-extra/clang-doc/Serialize.cpp (+6-6)
- (modified) clang-tools-extra/clang-doc/YAMLGenerator.cpp (+5-5)
- (modified) clang-tools-extra/clang-doc/tool/ClangDocMain.cpp (+2-6)
``````````diff
diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.cpp b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
index 2ab1bbf4a8a16..ee5114b1af880 100644
--- a/clang-tools-extra/clang-doc/BitcodeWriter.cpp
+++ b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
@@ -768,28 +768,28 @@ void ClangDocBitcodeWriter::emitBlock(const VarInfo &I) {
bool ClangDocBitcodeWriter::dispatchInfoForWrite(Info *I) {
switch (I->IT) {
case InfoType::IT_namespace:
- emitBlock(*static_cast<NamespaceInfo *>(I));
+ emitBlock(*cast<NamespaceInfo>(I));
break;
case InfoType::IT_record:
- emitBlock(*static_cast<RecordInfo *>(I));
+ emitBlock(*cast<RecordInfo>(I));
break;
case InfoType::IT_enum:
- emitBlock(*static_cast<EnumInfo *>(I));
+ emitBlock(*cast<EnumInfo>(I));
break;
case InfoType::IT_function:
- emitBlock(*static_cast<FunctionInfo *>(I));
+ emitBlock(*cast<FunctionInfo>(I));
break;
case InfoType::IT_typedef:
- emitBlock(*static_cast<TypedefInfo *>(I));
+ emitBlock(*cast<TypedefInfo>(I));
break;
case InfoType::IT_concept:
- emitBlock(*static_cast<ConceptInfo *>(I));
+ emitBlock(*cast<ConceptInfo>(I));
break;
case InfoType::IT_variable:
- emitBlock(*static_cast<VarInfo *>(I));
+ emitBlock(*cast<VarInfo>(I));
break;
case InfoType::IT_friend:
- emitBlock(*static_cast<FriendInfo *>(I));
+ emitBlock(*cast<FriendInfo>(I));
break;
case InfoType::IT_default:
unsigned ID = Diags.getCustomDiagID(DiagnosticsEngine::Error,
diff --git a/clang-tools-extra/clang-doc/JSONGenerator.cpp b/clang-tools-extra/clang-doc/JSONGenerator.cpp
index 0ab71b4a5b5c9..fc629dc8449e0 100644
--- a/clang-tools-extra/clang-doc/JSONGenerator.cpp
+++ b/clang-tools-extra/clang-doc/JSONGenerator.cpp
@@ -451,7 +451,7 @@ void JSONGenerator::serializeCommonAttributes(const Info &I,
// Namespaces aren't SymbolInfos, so they dont have a DefLoc
if (I.IT != InfoType::IT_namespace) {
- const auto *Symbol = static_cast<const SymbolInfo *>(&I);
+ const auto *Symbol = cast<SymbolInfo>(&I);
if (Symbol->DefLoc)
Obj["Location"] = serializeLocation(Symbol->DefLoc.value());
}
@@ -491,7 +491,7 @@ void JSONGenerator::serializeClassSpecializations(SymbolID ClassUSR,
auto *Class = Infos->lookup(toHex(ClassUSR));
if (!Class || Class->IT != InfoType::IT_record)
return;
- RecordInfo *ClassInfo = static_cast<RecordInfo *>(Class);
+ RecordInfo *ClassInfo = cast<RecordInfo>(Class);
if (!ClassInfo->Template || !ClassInfo->Template->Specialization)
return;
serializeTemplateSpecialization(ClassInfo->Template.value(), ReferenceObj);
@@ -865,7 +865,7 @@ SmallString<16> JSONGenerator::determineFileName(Info *I,
SmallString<128> &Path) {
SmallString<16> FileName;
if (I->IT == InfoType::IT_record) {
- auto *RecordSymbolInfo = static_cast<SymbolInfo *>(I);
+ auto *RecordSymbolInfo = cast<SymbolInfo>(I);
FileName = RecordSymbolInfo->MangledName;
} else if (I->IT == InfoType::IT_namespace) {
FileName = "index";
@@ -1052,10 +1052,10 @@ Error JSONGenerator::generateDocForInfo(Info *I, raw_ostream &OS,
switch (I->IT) {
case InfoType::IT_namespace:
- serializeInfo(*static_cast<NamespaceInfo *>(I), Obj);
+ serializeInfo(*cast<NamespaceInfo>(I), Obj);
break;
case InfoType::IT_record:
- serializeInfo(*static_cast<RecordInfo *>(I), Obj);
+ serializeInfo(*cast<RecordInfo>(I), Obj);
break;
case InfoType::IT_concept:
case InfoType::IT_enum:
diff --git a/clang-tools-extra/clang-doc/MDGenerator.cpp b/clang-tools-extra/clang-doc/MDGenerator.cpp
index df1ca6b868d43..4d412cde557ed 100644
--- a/clang-tools-extra/clang-doc/MDGenerator.cpp
+++ b/clang-tools-extra/clang-doc/MDGenerator.cpp
@@ -561,19 +561,19 @@ llvm::Error MDGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
const ClangDocContext &CDCtx) {
switch (I->IT) {
case InfoType::IT_namespace:
- genMarkdown(CDCtx, *static_cast<clang::doc::NamespaceInfo *>(I), OS);
+ genMarkdown(CDCtx, *cast<NamespaceInfo>(I), OS);
break;
case InfoType::IT_record:
- genMarkdown(CDCtx, *static_cast<clang::doc::RecordInfo *>(I), OS);
+ genMarkdown(CDCtx, *cast<RecordInfo>(I), OS);
break;
case InfoType::IT_enum:
- genMarkdown(CDCtx, *static_cast<clang::doc::EnumInfo *>(I), OS);
+ genMarkdown(CDCtx, *cast<EnumInfo>(I), OS);
break;
case InfoType::IT_function:
- genMarkdown(CDCtx, *static_cast<clang::doc::FunctionInfo *>(I), OS);
+ genMarkdown(CDCtx, *cast<FunctionInfo>(I), OS);
break;
case InfoType::IT_typedef:
- genMarkdown(CDCtx, *static_cast<clang::doc::TypedefInfo *>(I), OS);
+ genMarkdown(CDCtx, *cast<TypedefInfo>(I), OS);
break;
case InfoType::IT_concept:
case InfoType::IT_variable:
diff --git a/clang-tools-extra/clang-doc/Representation.cpp b/clang-tools-extra/clang-doc/Representation.cpp
index bfa1150f9bc35..0284f5a70615f 100644
--- a/clang-tools-extra/clang-doc/Representation.cpp
+++ b/clang-tools-extra/clang-doc/Representation.cpp
@@ -108,7 +108,7 @@ static llvm::Expected<Info *> reduce(SmallVectorImpl<Info *> &Values) {
"no value to reduce");
T *Merged = allocateTransient<T>(Values[0]->USR);
for (auto &I : Values)
- Merged->merge(std::move(*static_cast<T *>(I)));
+ Merged->merge(std::move(*cast<T>(I)));
return Merged;
}
@@ -220,36 +220,36 @@ llvm::Error mergeSingleInfo(doc::Info *&Reduced, doc::Info *NewInfo,
switch (Reduced->IT) {
case InfoType::IT_namespace:
- static_cast<NamespaceInfo *>(Reduced)->merge(
- std::move(*static_cast<NamespaceInfo *>(NewInfo)));
+ cast<NamespaceInfo>(Reduced)->merge(
+ std::move(*cast<NamespaceInfo>(NewInfo)));
break;
case InfoType::IT_record:
- static_cast<RecordInfo *>(Reduced)->merge(
- std::move(*static_cast<RecordInfo *>(NewInfo)));
+ cast<RecordInfo>(Reduced)->merge(
+ std::move(*cast<RecordInfo>(NewInfo)));
break;
case InfoType::IT_enum:
- static_cast<EnumInfo *>(Reduced)->merge(
- std::move(*static_cast<EnumInfo *>(NewInfo)));
+ cast<EnumInfo>(Reduced)->merge(
+ std::move(*cast<EnumInfo>(NewInfo)));
break;
case InfoType::IT_function:
- static_cast<FunctionInfo *>(Reduced)->merge(
- std::move(*static_cast<FunctionInfo *>(NewInfo)));
+ cast<FunctionInfo>(Reduced)->merge(
+ std::move(*cast<FunctionInfo>(NewInfo)));
break;
case InfoType::IT_typedef:
- static_cast<TypedefInfo *>(Reduced)->merge(
- std::move(*static_cast<TypedefInfo *>(NewInfo)));
+ cast<TypedefInfo>(Reduced)->merge(
+ std::move(*cast<TypedefInfo>(NewInfo)));
break;
case InfoType::IT_concept:
- static_cast<ConceptInfo *>(Reduced)->merge(
- std::move(*static_cast<ConceptInfo *>(NewInfo)));
+ cast<ConceptInfo>(Reduced)->merge(
+ std::move(*cast<ConceptInfo>(NewInfo)));
break;
case InfoType::IT_variable:
- static_cast<VarInfo *>(Reduced)->merge(
- std::move(*static_cast<VarInfo *>(NewInfo)));
+ cast<VarInfo>(Reduced)->merge(
+ std::move(*cast<VarInfo>(NewInfo)));
break;
case InfoType::IT_friend:
- static_cast<FriendInfo *>(Reduced)->merge(
- std::move(*static_cast<FriendInfo *>(NewInfo)));
+ cast<FriendInfo>(Reduced)->merge(
+ std::move(*cast<FriendInfo>(NewInfo)));
break;
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h
index c7921ff9de875..d135ba0cd09fa 100644
--- a/clang-tools-extra/clang-doc/Representation.h
+++ b/clang-tools-extra/clang-doc/Representation.h
@@ -23,6 +23,7 @@
#include "llvm/ADT/ilist_node.h"
#include "llvm/ADT/simple_ilist.h"
#include "llvm/Support/Allocator.h"
+#include "llvm/Support/Casting.h"
#include "llvm/Support/Mutex.h"
#include "llvm/Support/StringSaver.h"
#include <array>
@@ -585,6 +586,10 @@ struct NamespaceInfo : public Info {
NamespaceInfo(SymbolID USR = SymbolID(), StringRef Name = StringRef(),
StringRef Path = StringRef());
+ static bool classof(const Info *I) {
+ return I->IT == InfoType::IT_namespace;
+ }
+
void merge(NamespaceInfo &&I);
ScopeChildren Children;
@@ -598,6 +603,23 @@ struct SymbolInfo : public Info {
SymbolInfo(const SymbolInfo &Other, llvm::BumpPtrAllocator &Arena);
+ // SymbolInfo is an intermediate base with no InfoType of its own; it covers
+ // every kind that isn't a namespace.
+ static bool classof(const Info *I) {
+ switch (I->IT) {
+ case InfoType::IT_record:
+ case InfoType::IT_function:
+ case InfoType::IT_enum:
+ case InfoType::IT_typedef:
+ case InfoType::IT_concept:
+ case InfoType::IT_variable:
+ case InfoType::IT_friend:
+ return true;
+ default:
+ return false;
+ }
+ }
+
void merge(SymbolInfo &&I);
bool operator<(const SymbolInfo &Other) const {
@@ -627,6 +649,9 @@ struct FriendInfo : public SymbolInfo {
const StringRef Name = StringRef())
: SymbolInfo(IT, USR, Name) {}
FriendInfo(const FriendInfo &Other, llvm::BumpPtrAllocator &Arena);
+
+ static bool classof(const Info *I) { return I->IT == InfoType::IT_friend; }
+
bool mergeable(const FriendInfo &Other);
void merge(FriendInfo &&Other);
@@ -641,6 +666,10 @@ struct VarInfo : public SymbolInfo {
VarInfo() : SymbolInfo(InfoType::IT_variable) {}
explicit VarInfo(SymbolID USR) : SymbolInfo(InfoType::IT_variable, USR) {}
+ static bool classof(const Info *I) {
+ return I->IT == InfoType::IT_variable;
+ }
+
void merge(VarInfo &&I);
TypeInfo Type;
@@ -652,6 +681,10 @@ struct FunctionInfo : public SymbolInfo {
FunctionInfo(SymbolID USR = SymbolID())
: SymbolInfo(InfoType::IT_function, USR) {}
+ static bool classof(const Info *I) {
+ return I->IT == InfoType::IT_function;
+ }
+
void merge(FunctionInfo &&I);
Reference Parent;
@@ -680,6 +713,11 @@ struct RecordInfo : public SymbolInfo {
RecordInfo(const RecordInfo &Other, llvm::BumpPtrAllocator &Arena);
+ // BaseRecordInfo also carries IT_record, so isa<RecordInfo> is true for it as
+ // well. BaseRecordInfo is never stored polymorphically as an Info*, so it has
+ // no classof of its own.
+ static bool classof(const Info *I) { return I->IT == InfoType::IT_record; }
+
void merge(RecordInfo &&I);
// Type of this record (struct, class, union, interface).
@@ -715,6 +753,10 @@ struct TypedefInfo : public SymbolInfo {
TypedefInfo(SymbolID USR = SymbolID())
: SymbolInfo(InfoType::IT_typedef, USR) {}
+ static bool classof(const Info *I) {
+ return I->IT == InfoType::IT_typedef;
+ }
+
void merge(TypedefInfo &&I);
TypeInfo Underlying = {};
@@ -782,6 +824,8 @@ struct EnumInfo : public SymbolInfo {
EnumInfo() : SymbolInfo(InfoType::IT_enum) {}
EnumInfo(SymbolID USR) : SymbolInfo(InfoType::IT_enum, USR) {}
+ static bool classof(const Info *I) { return I->IT == InfoType::IT_enum; }
+
void merge(EnumInfo &&I);
// Indicates whether this enum is scoped (e.g. enum class).
@@ -799,6 +843,10 @@ struct ConceptInfo : public SymbolInfo {
ConceptInfo() : SymbolInfo(InfoType::IT_concept) {}
ConceptInfo(SymbolID USR) : SymbolInfo(InfoType::IT_concept, USR) {}
+ static bool classof(const Info *I) {
+ return I->IT == InfoType::IT_concept;
+ }
+
void merge(ConceptInfo &&I);
bool IsType = false;
diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp
index c7481fa5bada6..50118e0472075 100644
--- a/clang-tools-extra/clang-doc/Serialize.cpp
+++ b/clang-tools-extra/clang-doc/Serialize.cpp
@@ -363,17 +363,17 @@ static std::string serialize(const T &I, DiagnosticsEngine &Diags) {
std::string serialize(const Info &I, DiagnosticsEngine &Diags) {
switch (I.IT) {
case InfoType::IT_namespace:
- return serialize(static_cast<const NamespaceInfo &>(I), Diags);
+ return serialize(cast<NamespaceInfo>(I), Diags);
case InfoType::IT_record:
- return serialize(static_cast<const RecordInfo &>(I), Diags);
+ return serialize(cast<RecordInfo>(I), Diags);
case InfoType::IT_enum:
- return serialize(static_cast<const EnumInfo &>(I), Diags);
+ return serialize(cast<EnumInfo>(I), Diags);
case InfoType::IT_function:
- return serialize(static_cast<const FunctionInfo &>(I), Diags);
+ return serialize(cast<FunctionInfo>(I), Diags);
case InfoType::IT_concept:
- return serialize(static_cast<const ConceptInfo &>(I), Diags);
+ return serialize(cast<ConceptInfo>(I), Diags);
case InfoType::IT_variable:
- return serialize(static_cast<const VarInfo &>(I), Diags);
+ return serialize(cast<VarInfo>(I), Diags);
case InfoType::IT_friend:
case InfoType::IT_typedef:
case InfoType::IT_default:
diff --git a/clang-tools-extra/clang-doc/YAMLGenerator.cpp b/clang-tools-extra/clang-doc/YAMLGenerator.cpp
index 3d2bb97335b6f..5a1d822526daa 100644
--- a/clang-tools-extra/clang-doc/YAMLGenerator.cpp
+++ b/clang-tools-extra/clang-doc/YAMLGenerator.cpp
@@ -567,19 +567,19 @@ llvm::Error YAMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
llvm::yaml::Output InfoYAML(OS);
switch (I->IT) {
case InfoType::IT_namespace:
- InfoYAML << *static_cast<clang::doc::NamespaceInfo *>(I);
+ InfoYAML << *cast<NamespaceInfo>(I);
break;
case InfoType::IT_record:
- InfoYAML << *static_cast<clang::doc::RecordInfo *>(I);
+ InfoYAML << *cast<RecordInfo>(I);
break;
case InfoType::IT_enum:
- InfoYAML << *static_cast<clang::doc::EnumInfo *>(I);
+ InfoYAML << *cast<EnumInfo>(I);
break;
case InfoType::IT_function:
- InfoYAML << *static_cast<clang::doc::FunctionInfo *>(I);
+ InfoYAML << *cast<FunctionInfo>(I);
break;
case InfoType::IT_typedef:
- InfoYAML << *static_cast<clang::doc::TypedefInfo *>(I);
+ InfoYAML << *cast<TypedefInfo>(I);
break;
case InfoType::IT_concept:
case InfoType::IT_variable:
diff --git a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
index 5d1d88a228cc6..38002f5111a9f 100644
--- a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
+++ b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
@@ -231,14 +231,10 @@ static llvm::Error getMdFiles(const char *Argv0,
static void sortUsrToInfo(llvm::StringMap<doc::Info *> &USRToInfo) {
for (auto &I : USRToInfo) {
auto &Info = I.second;
- if (Info->IT == doc::InfoType::IT_namespace) {
- auto *Namespace = static_cast<clang::doc::NamespaceInfo *>(Info);
+ if (auto *Namespace = dyn_cast<doc::NamespaceInfo>(Info))
Namespace->Children.sort();
- }
- if (Info->IT == doc::InfoType::IT_record) {
- auto *Record = static_cast<clang::doc::RecordInfo *>(Info);
+ else if (auto *Record = dyn_cast<doc::RecordInfo>(Info))
Record->Children.sort();
- }
}
}
``````````
</details>
https://github.com/llvm/llvm-project/pull/202059
More information about the cfe-commits
mailing list