Author: Paul Kirth
Date: 2026-06-06T13:45:58-07:00
New Revision: 35823d13a8460f57be6b73f3dc4f3b93acbcf79d

URL: 
https://github.com/llvm/llvm-project/commit/35823d13a8460f57be6b73f3dc4f3b93acbcf79d
DIFF: 
https://github.com/llvm/llvm-project/commit/35823d13a8460f57be6b73f3dc4f3b93acbcf79d.diff

LOG: [clang-doc] Use llvm RTTI over handrolled casting (#202059)

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.

Added: 
    

Modified: 
    clang-tools-extra/clang-doc/BitcodeWriter.cpp
    clang-tools-extra/clang-doc/JSONGenerator.cpp
    clang-tools-extra/clang-doc/MDGenerator.cpp
    clang-tools-extra/clang-doc/Representation.cpp
    clang-tools-extra/clang-doc/Representation.h
    clang-tools-extra/clang-doc/Serialize.cpp
    clang-tools-extra/clang-doc/YAMLGenerator.cpp
    clang-tools-extra/clang-doc/tool/ClangDocMain.cpp

Removed: 
    


################################################################################
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..9c13f6bfa566d 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,29 @@ 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..e3ad7fa3f375d 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,8 @@ 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 +601,24 @@ struct SymbolInfo : public Info {
 
   SymbolInfo(const SymbolInfo &Other, llvm::BumpPtrAllocator &Arena);
 
+  // SymbolInfo is an intermediate base without its own InfoType. It covers
+  // every Info* kind that relates to symbols, which mostly just excludes
+  // Namespace in the current schema.
+  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 +648,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 +665,8 @@ 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 +678,8 @@ 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 +708,8 @@ struct RecordInfo : public SymbolInfo {
 
   RecordInfo(const RecordInfo &Other, llvm::BumpPtrAllocator &Arena);
 
+  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 +745,8 @@ 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 +814,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 +833,8 @@ 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();
-    }
   }
 }
 


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to