Author: Erick Velez Date: 2025-07-11T13:39:41-07:00 New Revision: 94bb9e12ec4ec243aac747910c5ae6359f354642
URL: https://github.com/llvm/llvm-project/commit/94bb9e12ec4ec243aac747910c5ae6359f354642 DIFF: https://github.com/llvm/llvm-project/commit/94bb9e12ec4ec243aac747910c5ae6359f354642.diff LOG: [clang-doc] Serialize record files with mangled name (#148021) This patch changes JSON file serialization. Now, files are serialized to a single directory instead of nesting them based on namespaces. The global namespace retains the "index.json" name. This solves the problem of class template specializations being serialized to the same file as its base template. This is also planned as part of future integration with the Mustache generator which will consume the JSON files. Added: clang-tools-extra/test/clang-doc/json/class-specialization.cpp Modified: clang-tools-extra/clang-doc/BitcodeReader.cpp clang-tools-extra/clang-doc/BitcodeWriter.cpp clang-tools-extra/clang-doc/BitcodeWriter.h clang-tools-extra/clang-doc/JSONGenerator.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/test/clang-doc/json/class-requires.cpp clang-tools-extra/test/clang-doc/json/class-template.cpp clang-tools-extra/test/clang-doc/json/class.cpp clang-tools-extra/test/clang-doc/json/compound-constraints.cpp clang-tools-extra/test/clang-doc/json/concept.cpp clang-tools-extra/test/clang-doc/json/function-requires.cpp clang-tools-extra/test/clang-doc/json/function-specifiers.cpp clang-tools-extra/test/clang-doc/json/method-template.cpp clang-tools-extra/test/clang-doc/json/namespace.cpp clang-tools-extra/test/clang-doc/json/nested-namespace.cpp clang-tools-extra/unittests/clang-doc/JSONGeneratorTest.cpp Removed: ################################################################################ diff --git a/clang-tools-extra/clang-doc/BitcodeReader.cpp b/clang-tools-extra/clang-doc/BitcodeReader.cpp index f756ae6d897c8..dce34a8434ff8 100644 --- a/clang-tools-extra/clang-doc/BitcodeReader.cpp +++ b/clang-tools-extra/clang-doc/BitcodeReader.cpp @@ -180,6 +180,8 @@ static llvm::Error parseRecord(const Record &R, unsigned ID, return decodeRecord(R, I->TagType, Blob); case RECORD_IS_TYPE_DEF: return decodeRecord(R, I->IsTypeDef, Blob); + case RECORD_MANGLED_NAME: + return decodeRecord(R, I->MangledName, Blob); default: return llvm::createStringError(llvm::inconvertibleErrorCode(), "invalid field for RecordInfo"); diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.cpp b/clang-tools-extra/clang-doc/BitcodeWriter.cpp index 3cc0d4ad332f0..eed23726e17bf 100644 --- a/clang-tools-extra/clang-doc/BitcodeWriter.cpp +++ b/clang-tools-extra/clang-doc/BitcodeWriter.cpp @@ -189,6 +189,7 @@ static const llvm::IndexedMap<RecordIdDsc, RecordIdToIndexFunctor> {RECORD_LOCATION, {"Location", &genLocationAbbrev}}, {RECORD_TAG_TYPE, {"TagType", &genIntAbbrev}}, {RECORD_IS_TYPE_DEF, {"IsTypeDef", &genBoolAbbrev}}, + {RECORD_MANGLED_NAME, {"MangledName", &genStringAbbrev}}, {BASE_RECORD_USR, {"USR", &genSymbolIdAbbrev}}, {BASE_RECORD_NAME, {"Name", &genStringAbbrev}}, {BASE_RECORD_PATH, {"Path", &genStringAbbrev}}, @@ -271,7 +272,8 @@ static const std::vector<std::pair<BlockId, std::vector<RecordId>>> // Record Block {BI_RECORD_BLOCK_ID, {RECORD_USR, RECORD_NAME, RECORD_PATH, RECORD_DEFLOCATION, - RECORD_LOCATION, RECORD_TAG_TYPE, RECORD_IS_TYPE_DEF}}, + RECORD_LOCATION, RECORD_TAG_TYPE, RECORD_IS_TYPE_DEF, + RECORD_MANGLED_NAME}}, // BaseRecord Block {BI_BASE_RECORD_BLOCK_ID, {BASE_RECORD_USR, BASE_RECORD_NAME, BASE_RECORD_PATH, @@ -616,6 +618,7 @@ void ClangDocBitcodeWriter::emitBlock(const RecordInfo &I) { emitRecord(I.USR, RECORD_USR); emitRecord(I.Name, RECORD_NAME); emitRecord(I.Path, RECORD_PATH); + emitRecord(I.MangledName, RECORD_MANGLED_NAME); for (const auto &N : I.Namespace) emitBlock(N, FieldId::F_namespace); for (const auto &CI : I.Description) diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.h b/clang-tools-extra/clang-doc/BitcodeWriter.h index d09ec4ca34006..501af12582a8e 100644 --- a/clang-tools-extra/clang-doc/BitcodeWriter.h +++ b/clang-tools-extra/clang-doc/BitcodeWriter.h @@ -126,6 +126,7 @@ enum RecordId { RECORD_LOCATION, RECORD_TAG_TYPE, RECORD_IS_TYPE_DEF, + RECORD_MANGLED_NAME, BASE_RECORD_USR, BASE_RECORD_NAME, BASE_RECORD_PATH, diff --git a/clang-tools-extra/clang-doc/JSONGenerator.cpp b/clang-tools-extra/clang-doc/JSONGenerator.cpp index 0e1a0cc347e45..6fdc7196e9095 100644 --- a/clang-tools-extra/clang-doc/JSONGenerator.cpp +++ b/clang-tools-extra/clang-doc/JSONGenerator.cpp @@ -386,6 +386,7 @@ static void serializeInfo(const RecordInfo &I, json::Object &Obj, Obj["FullName"] = I.FullName; Obj["TagType"] = getTagType(I.TagType); Obj["IsTypedef"] = I.IsTypeDef; + Obj["MangledName"] = I.MangledName; if (!I.Children.Functions.empty()) { json::Value PubFunctionsArray = Array(); @@ -491,6 +492,23 @@ static void serializeInfo(const NamespaceInfo &I, json::Object &Obj, serializeCommonChildren(I.Children, Obj, RepositoryUrl); } +static SmallString<16> determineFileName(Info *I, SmallString<128> &Path) { + SmallString<16> FileName; + if (I->IT == InfoType::IT_record) { + auto *RecordSymbolInfo = static_cast<SymbolInfo *>(I); + if (RecordSymbolInfo->MangledName.size() < 255) + FileName = RecordSymbolInfo->MangledName; + else + FileName = toStringRef(toHex(RecordSymbolInfo->USR)); + } else if (I->IT == InfoType::IT_namespace && I->Name != "") + // Serialize the global namespace as index.json + FileName = I->Name; + else + FileName = I->getFileBaseName(); + sys::path::append(Path, FileName + ".json"); + return FileName; +} + Error JSONGenerator::generateDocs( StringRef RootDir, llvm::StringMap<std::unique_ptr<doc::Info>> Infos, const ClangDocContext &CDCtx) { @@ -501,7 +519,6 @@ Error JSONGenerator::generateDocs( SmallString<128> Path; sys::path::native(RootDir, Path); - sys::path::append(Path, Info->getRelativeFilePath("")); if (!CreatedDirs.contains(Path)) { if (std::error_code Err = sys::fs::create_directories(Path); Err != std::error_code()) @@ -509,7 +526,7 @@ Error JSONGenerator::generateDocs( CreatedDirs.insert(Path); } - sys::path::append(Path, Info->getFileBaseName() + ".json"); + SmallString<16> FileName = determineFileName(Info, Path); FileToInfos[Path].push_back(Info); } diff --git a/clang-tools-extra/clang-doc/Representation.cpp b/clang-tools-extra/clang-doc/Representation.cpp index 422a76d99e5b3..beaf314a04ae1 100644 --- a/clang-tools-extra/clang-doc/Representation.cpp +++ b/clang-tools-extra/clang-doc/Representation.cpp @@ -290,6 +290,8 @@ void SymbolInfo::merge(SymbolInfo &&Other) { auto *Last = llvm::unique(Loc); Loc.erase(Last, Loc.end()); mergeBase(std::move(Other)); + if (MangledName.empty()) + MangledName = std::move(Other.MangledName); } NamespaceInfo::NamespaceInfo(SymbolID USR, StringRef Name, StringRef Path) diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h index fe5cc48069d58..23f0e90daa27f 100644 --- a/clang-tools-extra/clang-doc/Representation.h +++ b/clang-tools-extra/clang-doc/Representation.h @@ -377,6 +377,7 @@ struct SymbolInfo : public Info { std::optional<Location> DefLoc; // Location where this decl is defined. llvm::SmallVector<Location, 2> Loc; // Locations where this decl is declared. + SmallString<16> MangledName; bool IsStatic = false; }; diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp index 6cc372ce98a6d..7a0e00c6d9c2d 100644 --- a/clang-tools-extra/clang-doc/Serialize.cpp +++ b/clang-tools-extra/clang-doc/Serialize.cpp @@ -12,6 +12,7 @@ #include "clang/AST/Attr.h" #include "clang/AST/Comment.h" #include "clang/AST/DeclFriend.h" +#include "clang/AST/Mangle.h" #include "clang/Index/USRGeneration.h" #include "clang/Lex/Lexer.h" #include "llvm/ADT/StringExtras.h" @@ -767,6 +768,17 @@ static void populateSymbolInfo(SymbolInfo &I, const T *D, const FullComment *C, I.DefLoc = Loc; else I.Loc.emplace_back(Loc); + + auto *Mangler = ItaniumMangleContext::create( + D->getASTContext(), D->getASTContext().getDiagnostics()); + std::string MangledName; + llvm::raw_string_ostream MangledStream(MangledName); + if (auto *CXXD = dyn_cast<CXXRecordDecl>(D)) + Mangler->mangleCXXVTable(CXXD, MangledStream); + else + MangledStream << D->getNameAsString(); + I.MangledName = MangledName; + delete Mangler; } static void diff --git a/clang-tools-extra/test/clang-doc/json/class-requires.cpp b/clang-tools-extra/test/clang-doc/json/class-requires.cpp index 2dd25771d6d8b..213da93a1adfa 100644 --- a/clang-tools-extra/test/clang-doc/json/class-requires.cpp +++ b/clang-tools-extra/test/clang-doc/json/class-requires.cpp @@ -1,6 +1,6 @@ // RUN: rm -rf %t && mkdir -p %t // RUN: clang-doc --extra-arg -std=c++20 --output=%t --format=json --executor=standalone %s -// RUN: FileCheck %s < %t/GlobalNamespace/MyClass.json +// RUN: FileCheck %s < %t/_ZTV7MyClass.json template<typename T> concept Addable = requires(T a, T b) { diff --git a/clang-tools-extra/test/clang-doc/json/class-specialization.cpp b/clang-tools-extra/test/clang-doc/json/class-specialization.cpp new file mode 100644 index 0000000000000..e9259edad5cb8 --- /dev/null +++ b/clang-tools-extra/test/clang-doc/json/class-specialization.cpp @@ -0,0 +1,37 @@ +// RUN: rm -rf %t && mkdir -p %t +// RUN: clang-doc --output=%t --format=json --executor=standalone %s +// RUN: FileCheck %s < %t/_ZTV7MyClass.json --check-prefix=BASE +// RUN: FileCheck %s < %t/_ZTV7MyClassIiE.json --check-prefix=SPECIALIZATION + +template<typename T> struct MyClass {}; + +template<> struct MyClass<int> {}; + +// BASE: "MangledName": "_ZTV7MyClass", +// BASE-NEXT: "Name": "MyClass", +// BASE-NEXT: "Namespace": [ +// BASE-NEXT: "GlobalNamespace" +// BASE-NEXT: ], +// BASE-NEXT: "Path": "GlobalNamespace", +// BASE-NEXT: "TagType": "struct", +// BASE-NEXT: "Template": { +// BASE-NEXT: "Parameters": [ +// BASE-NEXT: "typename T" +// BASE-NEXT: ] +// BASE-NEXT: }, + +// SPECIALIZATION: "MangledName": "_ZTV7MyClassIiE", +// SPECIALIZATION-NEXT: "Name": "MyClass", +// SPECIALIZATION-NEXT: "Namespace": [ +// SPECIALIZATION-NEXT: "GlobalNamespace" +// SPECIALIZATION-NEXT: ], +// SPECIALIZATION-NEXT: "Path": "GlobalNamespace", +// SPECIALIZATION-NEXT: "TagType": "struct", +// SPECIALIZATION-NEXT: "Template": { +// SPECIALIZATION-NEXT: "Specialization": { +// SPECIALIZATION-NEXT: "Parameters": [ +// SPECIALIZATION-NEXT: "int" +// SPECIALIZATION-NEXT: ], +// SPECIALIZATION-NEXT: "SpecializationOf": "{{[0-9A-F]*}}" +// SPECIALIZATION-NEXT: } +// SPECIALIZATION-NEXT: }, diff --git a/clang-tools-extra/test/clang-doc/json/class-template.cpp b/clang-tools-extra/test/clang-doc/json/class-template.cpp index fb9c4c2f21c2e..6cdc3e9175278 100644 --- a/clang-tools-extra/test/clang-doc/json/class-template.cpp +++ b/clang-tools-extra/test/clang-doc/json/class-template.cpp @@ -1,6 +1,6 @@ // RUN: rm -rf %t && mkdir -p %t // RUN: clang-doc --output=%t --format=json --executor=standalone %s -// RUN: FileCheck %s < %t/GlobalNamespace/MyClass.json +// RUN: FileCheck %s < %t/_ZTV7MyClass.json template<typename T> struct MyClass { T MemberTemplate; diff --git a/clang-tools-extra/test/clang-doc/json/class.cpp b/clang-tools-extra/test/clang-doc/json/class.cpp index ae47da75edccb..d8317eafea91a 100644 --- a/clang-tools-extra/test/clang-doc/json/class.cpp +++ b/clang-tools-extra/test/clang-doc/json/class.cpp @@ -1,6 +1,6 @@ // RUN: rm -rf %t && mkdir -p %t // RUN: clang-doc --output=%t --format=json --executor=standalone %s -// RUN: FileCheck %s < %t/GlobalNamespace/MyClass.json +// RUN: FileCheck %s < %t/_ZTV7MyClass.json struct Foo; @@ -134,6 +134,7 @@ struct MyClass { // CHECK-NEXT: "Filename": "{{.*}}class.cpp", // CHECK-NEXT: "LineNumber": 10 // CHECK-NEXT: }, +// CHECK-NEXT: "MangledName": "_ZTV7MyClass", // CHECK-NEXT: "Name": "MyClass", // CHECK-NEXT: "Namespace": [ // CHECK-NEXT: "GlobalNamespace" diff --git a/clang-tools-extra/test/clang-doc/json/compound-constraints.cpp b/clang-tools-extra/test/clang-doc/json/compound-constraints.cpp index b49dec5cc78c5..34acb6808409d 100644 --- a/clang-tools-extra/test/clang-doc/json/compound-constraints.cpp +++ b/clang-tools-extra/test/clang-doc/json/compound-constraints.cpp @@ -1,6 +1,6 @@ // RUN: rm -rf %t && mkdir -p %t // RUN: clang-doc --extra-arg -std=c++20 --output=%t --format=json --executor=standalone %s -// RUN: FileCheck %s < %t/GlobalNamespace/index.json +// RUN: FileCheck %s < %t/index.json template<typename T> concept Incrementable = requires (T a) { a++; diff --git a/clang-tools-extra/test/clang-doc/json/concept.cpp b/clang-tools-extra/test/clang-doc/json/concept.cpp index 887c9d79146a0..b946393274c85 100644 --- a/clang-tools-extra/test/clang-doc/json/concept.cpp +++ b/clang-tools-extra/test/clang-doc/json/concept.cpp @@ -1,6 +1,6 @@ // RUN: rm -rf %t && mkdir -p %t // RUN: clang-doc --extra-arg -std=c++20 --output=%t --format=json --executor=standalone %s -// RUN: FileCheck %s < %t/GlobalNamespace/index.json +// RUN: FileCheck %s < %t/index.json // Requires that T suports post and pre-incrementing. template<typename T> diff --git a/clang-tools-extra/test/clang-doc/json/function-requires.cpp b/clang-tools-extra/test/clang-doc/json/function-requires.cpp index 4e8432e088c4f..08ac4c7ed2ca3 100644 --- a/clang-tools-extra/test/clang-doc/json/function-requires.cpp +++ b/clang-tools-extra/test/clang-doc/json/function-requires.cpp @@ -1,6 +1,6 @@ // RUN: rm -rf %t && mkdir -p %t // RUN: clang-doc --extra-arg -std=c++20 --output=%t --format=json --executor=standalone %s -// RUN: FileCheck %s < %t/GlobalNamespace/index.json +// RUN: FileCheck %s < %t/index.json template<typename T> concept Incrementable = requires(T x) { diff --git a/clang-tools-extra/test/clang-doc/json/function-specifiers.cpp b/clang-tools-extra/test/clang-doc/json/function-specifiers.cpp index 7005fb7b3e66e..b194e3371bf76 100644 --- a/clang-tools-extra/test/clang-doc/json/function-specifiers.cpp +++ b/clang-tools-extra/test/clang-doc/json/function-specifiers.cpp @@ -1,6 +1,6 @@ // RUN: rm -rf %t && mkdir -p %t // RUN: clang-doc --output=%t --format=json --executor=standalone %s -// RUN: FileCheck %s < %t/GlobalNamespace/index.json +// RUN: FileCheck %s < %t/index.json static void myFunction() {} diff --git a/clang-tools-extra/test/clang-doc/json/method-template.cpp b/clang-tools-extra/test/clang-doc/json/method-template.cpp index ea9110d6c2d1c..ac8450a72d3a7 100644 --- a/clang-tools-extra/test/clang-doc/json/method-template.cpp +++ b/clang-tools-extra/test/clang-doc/json/method-template.cpp @@ -1,6 +1,6 @@ // RUN: rm -rf %t && mkdir -p %t // RUN: clang-doc --output=%t --format=json --executor=standalone %s -// RUN: FileCheck %s < %t/GlobalNamespace/MyClass.json +// RUN: FileCheck %s < %t/_ZTV7MyClass.json struct MyClass { template<class T> T methodTemplate(T param) { diff --git a/clang-tools-extra/test/clang-doc/json/namespace.cpp b/clang-tools-extra/test/clang-doc/json/namespace.cpp index 6e4fc6938d856..779d7b49f5581 100644 --- a/clang-tools-extra/test/clang-doc/json/namespace.cpp +++ b/clang-tools-extra/test/clang-doc/json/namespace.cpp @@ -1,6 +1,6 @@ // RUN: rm -rf %t && mkdir -p %t // RUN: clang-doc --output=%t --format=json --executor=standalone %s -// RUN: FileCheck %s < %t/GlobalNamespace/index.json +// RUN: FileCheck %s < %t/index.json class MyClass {}; diff --git a/clang-tools-extra/test/clang-doc/json/nested-namespace.cpp b/clang-tools-extra/test/clang-doc/json/nested-namespace.cpp index 9b176feb67a00..54f95c4d041ca 100644 --- a/clang-tools-extra/test/clang-doc/json/nested-namespace.cpp +++ b/clang-tools-extra/test/clang-doc/json/nested-namespace.cpp @@ -1,7 +1,7 @@ // RUN: rm -rf %t && mkdir -p %t // RUN: clang-doc --output=%t --format=json --executor=standalone %s -// RUN: FileCheck %s < %t/nested/index.json --check-prefix=NESTED -// RUN: FileCheck %s < %t/nested/inner/index.json --check-prefix=INNER +// RUN: FileCheck %s < %t/nested.json --check-prefix=NESTED +// RUN: FileCheck %s < %t/inner.json --check-prefix=INNER namespace nested { int Global; diff --git a/clang-tools-extra/unittests/clang-doc/JSONGeneratorTest.cpp b/clang-tools-extra/unittests/clang-doc/JSONGeneratorTest.cpp index 09e522133d832..5927235b3bd93 100644 --- a/clang-tools-extra/unittests/clang-doc/JSONGeneratorTest.cpp +++ b/clang-tools-extra/unittests/clang-doc/JSONGeneratorTest.cpp @@ -67,6 +67,7 @@ TEST(JSONGeneratorTest, emitRecordJSON) { "IsParent": true, "IsTypedef": false, "IsVirtual": true, + "MangledName": "", "Name": "F", "Path": "path/to/F", "PublicFunctions": [ @@ -112,6 +113,7 @@ TEST(JSONGeneratorTest, emitRecordJSON) { "Filename": "main.cpp", "LineNumber": 1 }, + "MangledName": "", "Name": "Foo", "Namespace": [ "GlobalNamespace" _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits