https://github.com/aviralg updated https://github.com/llvm/llvm-project/pull/182961
>From 08408536ac3e3d70878a13beda9ddb3df686b483 Mon Sep 17 00:00:00 2001 From: Aviral Goel <[email protected]> Date: Mon, 23 Feb 2026 15:52:32 -0800 Subject: [PATCH 1/5] Add JSONFormat serialization and deserialization support for linkage table --- .../Scalable/Serialization/JSONFormat.h | 17 + .../Scalable/Serialization/JSONFormat.cpp | 231 ++++++- .../JSONFormatTest/TUSummaryTest.cpp | 596 +++++++++++++++++- 3 files changed, 842 insertions(+), 2 deletions(-) diff --git a/clang/include/clang/Analysis/Scalable/Serialization/JSONFormat.h b/clang/include/clang/Analysis/Scalable/Serialization/JSONFormat.h index 052aa2641dbce..25fdd07b7f469 100644 --- a/clang/include/clang/Analysis/Scalable/Serialization/JSONFormat.h +++ b/clang/include/clang/Analysis/Scalable/Serialization/JSONFormat.h @@ -13,12 +13,15 @@ #ifndef CLANG_ANALYSIS_SCALABLE_SERIALIZATION_JSONFORMAT_H #define CLANG_ANALYSIS_SCALABLE_SERIALIZATION_JSONFORMAT_H +#include "clang/Analysis/Scalable/Model/EntityLinkage.h" #include "clang/Analysis/Scalable/Serialization/SerializationFormat.h" #include "clang/Support/Compiler.h" #include "llvm/ADT/STLFunctionalExtras.h" #include "llvm/Support/JSON.h" #include "llvm/Support/Registry.h" +#include <set> + namespace clang::ssaf { class EntityIdTable; @@ -69,6 +72,20 @@ class JSONFormat final : public SerializationFormat { llvm::Expected<BuildNamespaceKind> buildNamespaceKindFromJSON(llvm::StringRef BuildNamespaceKindStr) const; + llvm::Expected<EntityLinkage> + entityLinkageFromJSON(const Object &EntityLinkageObject) const; + Object entityLinkageToJSON(const EntityLinkage &EL) const; + + llvm::Expected<std::pair<EntityId, EntityLinkage>> + linkageTableEntryFromJSON(const Object &LinkageTableEntryObject) const; + Object linkageTableEntryToJSON(EntityId EI, const EntityLinkage &EL) const; + + llvm::Expected<std::map<EntityId, EntityLinkage>> + linkageTableFromJSON(const Array &LinkageTableArray, + std::set<EntityId> EntityIds) const; + Array linkageTableToJSON( + const std::map<EntityId, EntityLinkage> &LinkageTable) const; + llvm::Expected<BuildNamespace> buildNamespaceFromJSON(const Object &BuildNamespaceObject) const; Object buildNamespaceToJSON(const BuildNamespace &BN) const; diff --git a/clang/lib/Analysis/Scalable/Serialization/JSONFormat.cpp b/clang/lib/Analysis/Scalable/Serialization/JSONFormat.cpp index d6c6e5223ea0f..55243c08133c0 100644 --- a/clang/lib/Analysis/Scalable/Serialization/JSONFormat.cpp +++ b/clang/lib/Analysis/Scalable/Serialization/JSONFormat.cpp @@ -1,8 +1,11 @@ #include "clang/Analysis/Scalable/Serialization/JSONFormat.h" +#include "clang/Analysis/Scalable/Model/EntityLinkage.h" #include "clang/Analysis/Scalable/Support/ErrorBuilder.h" #include "clang/Analysis/Scalable/TUSummary/TUSummary.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/JSON.h" @@ -72,6 +75,15 @@ constexpr const char *FailedToSerializeEntitySummary = constexpr const char *InvalidBuildNamespaceKind = "invalid 'kind' BuildNamespaceKind value '{0}'"; +constexpr const char *InvalidEntityLinkageType = + "invalid 'linkage' EntityLinkage value '{0}'"; + +constexpr const char *LinkageTableExtraId = + "linkage_table contains EntityId '{0}' not present in id_table"; + +constexpr const char *LinkageTableMissingId = + "linkage_table is missing EntityId '{0}' present in id_table"; + } // namespace ErrorMessages } // namespace @@ -234,7 +246,195 @@ llvm::StringRef buildNamespaceKindToJSON(BuildNamespaceKind BNK) { } // namespace //---------------------------------------------------------------------------- -// BuildNamespace +// EntityLinkageType +//---------------------------------------------------------------------------- + +namespace { + +std::optional<EntityLinkage::LinkageType> +parseEntityLinkageType(llvm::StringRef S) { + if (S == "none") + return EntityLinkage::LinkageType::None; + if (S == "internal") + return EntityLinkage::LinkageType::Internal; + if (S == "external") + return EntityLinkage::LinkageType::External; + return std::nullopt; +} + +llvm::StringRef entityLinkageTypeToJSON(EntityLinkage::LinkageType LT) { + switch (LT) { + case EntityLinkage::LinkageType::None: + return "none"; + case EntityLinkage::LinkageType::Internal: + return "internal"; + case EntityLinkage::LinkageType::External: + return "external"; + } + llvm_unreachable("Unhandled EntityLinkage::LinkageType variant"); +} + +} // namespace + +//---------------------------------------------------------------------------- +// EntityLinkage +//---------------------------------------------------------------------------- + +llvm::Expected<EntityLinkage> +JSONFormat::entityLinkageFromJSON(const Object &EntityLinkageObject) const { + auto OptLinkageStr = EntityLinkageObject.getString("type"); + if (!OptLinkageStr) { + return ErrorBuilder::create(std::errc::invalid_argument, + ErrorMessages::FailedToReadObjectAtField, + "EntityLinkageType", "type", "string") + .build(); + } + + auto OptLinkageType = parseEntityLinkageType(*OptLinkageStr); + if (!OptLinkageType) { + return ErrorBuilder::create(std::errc::invalid_argument, + ErrorMessages::InvalidEntityLinkageType, + *OptLinkageStr) + .build(); + } + + return EntityLinkage(*OptLinkageType); +} + +Object JSONFormat::entityLinkageToJSON(const EntityLinkage &EL) const { + Object Result; + Result["type"] = entityLinkageTypeToJSON(getLinkage(EL)); + return Result; +} + +//---------------------------------------------------------------------------- +// LinkageTableEntry +//---------------------------------------------------------------------------- + +llvm::Expected<std::pair<EntityId, EntityLinkage>> +JSONFormat::linkageTableEntryFromJSON( + const Object &LinkageTableEntryObject) const { + const Value *EntityIdIntValue = LinkageTableEntryObject.get("id"); + if (!EntityIdIntValue) { + return ErrorBuilder::create(std::errc::invalid_argument, + ErrorMessages::FailedToReadObjectAtField, + "EntityId", "id", + "number (unsigned 64-bit integer)") + .build(); + } + + const std::optional<uint64_t> OptEntityIdInt = + EntityIdIntValue->getAsUINT64(); + if (!OptEntityIdInt) { + return ErrorBuilder::create(std::errc::invalid_argument, + ErrorMessages::FailedToReadObjectAtField, + "EntityId", "id", + "number (unsigned 64-bit integer)") + .build(); + } + + EntityId EI = entityIdFromJSON(*OptEntityIdInt); + + const Object *OptEntityLinkageObject = + LinkageTableEntryObject.getObject("linkage"); + if (!OptEntityLinkageObject) { + return ErrorBuilder::create(std::errc::invalid_argument, + ErrorMessages::FailedToReadObjectAtField, + "EntityLinkage", "linkage", "object") + .build(); + } + + auto ExpectedEntityLinkage = entityLinkageFromJSON(*OptEntityLinkageObject); + if (!ExpectedEntityLinkage) { + return ErrorBuilder::wrap(ExpectedEntityLinkage.takeError()) + .context(ErrorMessages::ReadingFromField, "EntityLinkage", "linkage") + .build(); + } + + return std::make_pair(std::move(EI), std::move(*ExpectedEntityLinkage)); +} + +Object JSONFormat::linkageTableEntryToJSON(EntityId EI, + const EntityLinkage &EL) const { + Object Entry; + Entry["id"] = entityIdToJSON(EI); + Entry["linkage"] = entityLinkageToJSON(EL); + return Entry; +} + +//---------------------------------------------------------------------------- +// LinkageTable +//---------------------------------------------------------------------------- + +llvm::Expected<std::map<EntityId, EntityLinkage>> +JSONFormat::linkageTableFromJSON(const Array &LinkageTableArray, + std::set<EntityId> EntityIds) const { + std::map<EntityId, EntityLinkage> LinkageTable; + + for (const auto &[Index, LinkageTableEntryValue] : + llvm::enumerate(LinkageTableArray)) { + + const Object *OptLinkageTableEntryObject = + LinkageTableEntryValue.getAsObject(); + if (!OptLinkageTableEntryObject) { + return ErrorBuilder::create(std::errc::invalid_argument, + ErrorMessages::FailedToReadObjectAtIndex, + "LinkageTable entry", Index, "object") + .build(); + } + + auto ExpectedLinkageTableEntry = + linkageTableEntryFromJSON(*OptLinkageTableEntryObject); + if (!ExpectedLinkageTableEntry) + return ErrorBuilder::wrap(ExpectedLinkageTableEntry.takeError()) + .context(ErrorMessages::ReadingFromIndex, "LinkageTable entry", Index) + .build(); + + const EntityId EI = ExpectedLinkageTableEntry->first; + + auto [It, Inserted] = + LinkageTable.insert(std::move(*ExpectedLinkageTableEntry)); + if (!Inserted) { + return ErrorBuilder::create(std::errc::invalid_argument, + ErrorMessages::FailedInsertionOnDuplication, + "LinkageTable entry", Index, "EntityId", + getIndex(It->first)) + .build(); + } + + if (EntityIds.erase(EI) == 0) { + return ErrorBuilder::create(std::errc::invalid_argument, + ErrorMessages::LinkageTableExtraId, + getIndex(EI)) + .context(ErrorMessages::ReadingFromIndex, "LinkageTable entry", Index) + .build(); + } + } + + if (!EntityIds.empty()) { + return ErrorBuilder::create(std::errc::invalid_argument, + ErrorMessages::LinkageTableMissingId, + getIndex(*EntityIds.begin())) + .build(); + } + + return LinkageTable; +} + +Array JSONFormat::linkageTableToJSON( + const std::map<EntityId, EntityLinkage> &LinkageTable) const { + Array Result; + Result.reserve(LinkageTable.size()); + + for (const auto &[EI, EL] : LinkageTable) { + Result.push_back(linkageTableEntryToJSON(EI, EL)); + } + + return Result; +} + +//---------------------------------------------------------------------------- +// NestedBuildNamespace //---------------------------------------------------------------------------- llvm::Expected<BuildNamespace> @@ -840,6 +1040,33 @@ llvm::Expected<TUSummary> JSONFormat::readTUSummary(llvm::StringRef Path) { getIdTable(Summary) = std::move(*ExpectedIdTable); } + { + const Array *LinkageTableArray = RootObject.getArray("linkage_table"); + if (!LinkageTableArray) { + return ErrorBuilder::create(std::errc::invalid_argument, + ErrorMessages::FailedToReadObjectAtField, + "LinkageTable", "linkage_table", "array") + .context(ErrorMessages::ReadingFromFile, "TUSummary", Path) + .build(); + } + + auto EntityIdRange = + llvm::make_second_range(getEntities(getIdTable(Summary))); + std::set<EntityId> EntityIds(EntityIdRange.begin(), EntityIdRange.end()); + + auto ExpectedLinkageTable = + linkageTableFromJSON(*LinkageTableArray, std::move(EntityIds)); + if (!ExpectedLinkageTable) { + return ErrorBuilder::wrap(ExpectedLinkageTable.takeError()) + .context(ErrorMessages::ReadingFromField, "LinkageTable", + "linkage_table") + .context(ErrorMessages::ReadingFromFile, "TUSummary", Path) + .build(); + } + + getLinkageTable(Summary) = std::move(*ExpectedLinkageTable); + } + { const Array *SummaryDataArray = RootObject.getArray("data"); if (!SummaryDataArray) { @@ -874,6 +1101,8 @@ llvm::Error JSONFormat::writeTUSummary(const TUSummary &S, RootObject["id_table"] = entityIdTableToJSON(getIdTable(S)); + RootObject["linkage_table"] = linkageTableToJSON(getLinkageTable(S)); + auto ExpectedDataObject = summaryDataMapToJSON(getData(S)); if (!ExpectedDataObject) { return ErrorBuilder::wrap(ExpectedDataObject.takeError()) diff --git a/clang/unittests/Analysis/Scalable/Serialization/JSONFormatTest/TUSummaryTest.cpp b/clang/unittests/Analysis/Scalable/Serialization/JSONFormatTest/TUSummaryTest.cpp index 2cdbec9675662..1fb9a966217c8 100644 --- a/clang/unittests/Analysis/Scalable/Serialization/JSONFormatTest/TUSummaryTest.cpp +++ b/clang/unittests/Analysis/Scalable/Serialization/JSONFormatTest/TUSummaryTest.cpp @@ -312,6 +312,7 @@ TEST_F(JSONFormatTUSummaryTest, NoReadPermission) { "name": "test.cpp" }, "id_table": [], + "linkage_table": [], "data": [] })", FileName); @@ -349,6 +350,286 @@ TEST_F(JSONFormatTUSummaryTest, NotObject) { HasSubstr("expected JSON object")))); } +// ============================================================================ +// JSONFormat::entityLinkageFromJSON() Error Tests +// ============================================================================ + +TEST_F(JSONFormatTUSummaryTest, LinkageTableEntryLinkageMissingType) { + auto Result = readTUSummaryFromString(R"({ + "tu_namespace": { + "kind": "compilation_unit", + "name": "test.cpp" + }, + "id_table": [], + "linkage_table": [ + { + "id": 0, + "linkage": {} + } + ], + "data": [] + })"); + + EXPECT_THAT_EXPECTED( + Result, + FailedWithMessage( + AllOf(HasSubstr("reading TUSummary from file"), + HasSubstr("reading LinkageTable from field 'linkage_table'"), + HasSubstr("reading LinkageTable entry from index '0'"), + HasSubstr("reading EntityLinkage from field 'linkage'"), + HasSubstr("failed to read EntityLinkageType from field 'type'"), + HasSubstr("expected JSON string")))); +} + +TEST_F(JSONFormatTUSummaryTest, LinkageTableEntryLinkageInvalidType) { + auto Result = readTUSummaryFromString(R"({ + "tu_namespace": { + "kind": "compilation_unit", + "name": "test.cpp" + }, + "id_table": [], + "linkage_table": [ + { + "id": 0, + "linkage": { "type": "invalid_type" } + } + ], + "data": [] + })"); + + EXPECT_THAT_EXPECTED( + Result, + FailedWithMessage(AllOf( + HasSubstr("reading TUSummary from file"), + HasSubstr("reading LinkageTable from field 'linkage_table'"), + HasSubstr("reading LinkageTable entry from index '0'"), + HasSubstr("reading EntityLinkage from field 'linkage'"), + HasSubstr("invalid 'linkage' EntityLinkage value 'invalid_type'")))); +} + +// ============================================================================ +// JSONFormat::linkageTableEntryFromJSON() Error Tests +// ============================================================================ + +TEST_F(JSONFormatTUSummaryTest, LinkageTableEntryMissingId) { + auto Result = readTUSummaryFromString(R"({ + "tu_namespace": { + "kind": "compilation_unit", + "name": "test.cpp" + }, + "id_table": [], + "linkage_table": [ + { + "linkage": { "type": "external" } + } + ], + "data": [] + })"); + + EXPECT_THAT_EXPECTED( + Result, + FailedWithMessage( + AllOf(HasSubstr("reading TUSummary from file"), + HasSubstr("reading LinkageTable from field 'linkage_table'"), + HasSubstr("reading LinkageTable entry from index '0'"), + HasSubstr("failed to read EntityId from field 'id'"), + HasSubstr("expected JSON number (unsigned 64-bit integer)")))); +} + +TEST_F(JSONFormatTUSummaryTest, LinkageTableEntryIdNotUInt64) { + auto Result = readTUSummaryFromString(R"({ + "tu_namespace": { + "kind": "compilation_unit", + "name": "test.cpp" + }, + "id_table": [], + "linkage_table": [ + { + "id": "not_a_number", + "linkage": { "type": "external" } + } + ], + "data": [] + })"); + + EXPECT_THAT_EXPECTED( + Result, + FailedWithMessage( + AllOf(HasSubstr("reading TUSummary from file"), + HasSubstr("reading LinkageTable from field 'linkage_table'"), + HasSubstr("reading LinkageTable entry from index '0'"), + HasSubstr("failed to read EntityId from field 'id'"), + HasSubstr("expected JSON number (unsigned 64-bit integer)")))); +} + +TEST_F(JSONFormatTUSummaryTest, LinkageTableEntryMissingLinkage) { + auto Result = readTUSummaryFromString(R"({ + "tu_namespace": { + "kind": "compilation_unit", + "name": "test.cpp" + }, + "id_table": [], + "linkage_table": [ + { + "id": 0 + } + ], + "data": [] + })"); + + EXPECT_THAT_EXPECTED( + Result, + FailedWithMessage( + AllOf(HasSubstr("reading TUSummary from file"), + HasSubstr("reading LinkageTable from field 'linkage_table'"), + HasSubstr("reading LinkageTable entry from index '0'"), + HasSubstr("failed to read EntityLinkage from field 'linkage'"), + HasSubstr("expected JSON object")))); +} + +// ============================================================================ +// JSONFormat::linkageTableFromJSON() Error Tests +// ============================================================================ + +TEST_F(JSONFormatTUSummaryTest, LinkageTableNotArray) { + auto Result = readTUSummaryFromString(R"({ + "tu_namespace": { + "kind": "compilation_unit", + "name": "test.cpp" + }, + "id_table": [], + "linkage_table": {}, + "data": [] + })"); + + EXPECT_THAT_EXPECTED( + Result, + FailedWithMessage(AllOf( + HasSubstr("reading TUSummary from file"), + HasSubstr("failed to read LinkageTable from field 'linkage_table'"), + HasSubstr("expected JSON array")))); +} + +TEST_F(JSONFormatTUSummaryTest, LinkageTableElementNotObject) { + auto Result = readTUSummaryFromString(R"({ + "tu_namespace": { + "kind": "compilation_unit", + "name": "test.cpp" + }, + "id_table": [], + "linkage_table": ["invalid"], + "data": [] + })"); + + EXPECT_THAT_EXPECTED( + Result, FailedWithMessage(AllOf( + HasSubstr("reading TUSummary from file"), + HasSubstr("reading LinkageTable from field 'linkage_table'"), + HasSubstr("failed to read LinkageTable entry from index '0'"), + HasSubstr("expected JSON object")))); +} + +TEST_F(JSONFormatTUSummaryTest, LinkageTableExtraId) { + auto Result = readTUSummaryFromString(R"({ + "tu_namespace": { + "kind": "compilation_unit", + "name": "test.cpp" + }, + "id_table": [], + "linkage_table": [ + { + "id": 0, + "linkage": { "type": "external" } + } + ], + "data": [] + })"); + + EXPECT_THAT_EXPECTED( + Result, + FailedWithMessage(AllOf( + HasSubstr("reading TUSummary from file"), + HasSubstr("reading LinkageTable from field 'linkage_table'"), + HasSubstr("reading LinkageTable entry from index '0'"), + HasSubstr( + "linkage_table contains EntityId '0' not present in id_table")))); +} + +TEST_F(JSONFormatTUSummaryTest, LinkageTableMissingId) { + auto Result = readTUSummaryFromString(R"({ + "tu_namespace": { + "kind": "compilation_unit", + "name": "test.cpp" + }, + "id_table": [ + { + "id": 0, + "name": { + "usr": "c:@F@foo", + "suffix": "", + "namespace": [ + { + "kind": "compilation_unit", + "name": "test.cpp" + } + ] + } + } + ], + "linkage_table": [], + "data": [] + })"); + + EXPECT_THAT_EXPECTED( + Result, + FailedWithMessage(AllOf( + HasSubstr("reading TUSummary from file"), + HasSubstr( + "linkage_table is missing EntityId '0' present in id_table")))); +} + +TEST_F(JSONFormatTUSummaryTest, LinkageTableDuplicateId) { + auto Result = readTUSummaryFromString(R"({ + "tu_namespace": { + "kind": "compilation_unit", + "name": "test.cpp" + }, + "id_table": [ + { + "id": 0, + "name": { + "usr": "c:@F@foo", + "suffix": "", + "namespace": [ + { + "kind": "compilation_unit", + "name": "test.cpp" + } + ] + } + } + ], + "linkage_table": [ + { + "id": 0, + "linkage": { "type": "external" } + }, + { + "id": 0, + "linkage": { "type": "internal" } + } + ], + "data": [] + })"); + + EXPECT_THAT_EXPECTED( + Result, FailedWithMessage(AllOf( + HasSubstr("reading TUSummary from file"), + HasSubstr("reading LinkageTable from field 'linkage_table'"), + HasSubstr("failed to insert LinkageTable entry at index '1'"), + HasSubstr("encountered duplicate EntityId '0'")))); +} + // ============================================================================ // JSONFormat::buildNamespaceKindFromJSON() Error Tests // ============================================================================ @@ -360,6 +641,7 @@ TEST_F(JSONFormatTUSummaryTest, InvalidKind) { "name": "test.cpp" }, "id_table": [], + "linkage_table": [], "data": [] })"); @@ -383,6 +665,7 @@ TEST_F(JSONFormatTUSummaryTest, MissingKind) { "name": "test.cpp" }, "id_table": [], + "linkage_table": [], "data": [] })"); @@ -401,6 +684,7 @@ TEST_F(JSONFormatTUSummaryTest, MissingName) { "kind": "compilation_unit" }, "id_table": [], + "linkage_table": [], "data": [] })"); @@ -433,6 +717,12 @@ TEST_F(JSONFormatTUSummaryTest, NamespaceElementNotObject) { } } ], + "linkage_table": [ + { + "id": 0, + "linkage": { "type": "none" } + } + ], "data": [] })"); @@ -468,6 +758,12 @@ TEST_F(JSONFormatTUSummaryTest, NamespaceElementMissingKind) { } } ], + "linkage_table": [ + { + "id": 0, + "linkage": { "type": "internal" } + } + ], "data": [] })"); @@ -505,6 +801,12 @@ TEST_F(JSONFormatTUSummaryTest, NamespaceElementInvalidKind) { } } ], + "linkage_table": [ + { + "id": 0, + "linkage": { "type": "external" } + } + ], "data": [] })"); @@ -542,6 +844,12 @@ TEST_F(JSONFormatTUSummaryTest, NamespaceElementMissingName) { } } ], + "linkage_table": [ + { + "id": 0, + "linkage": { "type": "none" } + } + ], "data": [] })"); @@ -577,6 +885,12 @@ TEST_F(JSONFormatTUSummaryTest, EntityNameMissingUSR) { } } ], + "linkage_table": [ + { + "id": 0, + "linkage": { "type": "internal" } + } + ], "data": [] })"); @@ -605,6 +919,12 @@ TEST_F(JSONFormatTUSummaryTest, EntityNameMissingSuffix) { } } ], + "linkage_table": [ + { + "id": 0, + "linkage": { "type": "external" } + } + ], "data": [] })"); @@ -633,6 +953,12 @@ TEST_F(JSONFormatTUSummaryTest, EntityNameMissingNamespace) { } } ], + "linkage_table": [ + { + "id": 0, + "linkage": { "type": "none" } + } + ], "data": [] })"); @@ -667,6 +993,7 @@ TEST_F(JSONFormatTUSummaryTest, IDTableEntryMissingID) { } } ], + "linkage_table": [], "data": [] })"); @@ -691,6 +1018,12 @@ TEST_F(JSONFormatTUSummaryTest, IDTableEntryMissingName) { "id": 0 } ], + "linkage_table": [ + { + "id": 0, + "linkage": { "type": "none" } + } + ], "data": [] })"); @@ -719,6 +1052,7 @@ TEST_F(JSONFormatTUSummaryTest, IDTableEntryIDNotUInt64) { } } ], + "linkage_table": [], "data": [] })"); @@ -743,6 +1077,7 @@ TEST_F(JSONFormatTUSummaryTest, IDTableNotArray) { "name": "test.cpp" }, "id_table": {}, + "linkage_table": [], "data": [] })"); @@ -760,6 +1095,7 @@ TEST_F(JSONFormatTUSummaryTest, IDTableElementNotObject) { "name": "test.cpp" }, "id_table": [123], + "linkage_table": [], "data": [] })"); @@ -806,6 +1142,16 @@ TEST_F(JSONFormatTUSummaryTest, DuplicateEntity) { } } ], + "linkage_table": [ + { + "id": 0, + "linkage": { "type": "none" } + }, + { + "id": 1, + "linkage": { "type": "none" } + } + ], "data": [] })"); @@ -829,6 +1175,7 @@ TEST_F(JSONFormatTUSummaryTest, EntitySummaryNoFormatInfo) { "name": "test.cpp" }, "id_table": [], + "linkage_table": [], "data": [ { "summary_name": "unknown_summary_type", @@ -868,6 +1215,7 @@ TEST_F(JSONFormatTUSummaryTest, "name": "test.cpp" }, "id_table": [], + "linkage_table": [], "data": [ { "summary_name": "PairsEntitySummaryForJSONFormatTest", @@ -901,6 +1249,7 @@ TEST_F(JSONFormatTUSummaryTest, "name": "test.cpp" }, "id_table": [], + "linkage_table": [], "data": [ { "summary_name": "PairsEntitySummaryForJSONFormatTest", @@ -936,6 +1285,7 @@ TEST_F(JSONFormatTUSummaryTest, "name": "test.cpp" }, "id_table": [], + "linkage_table": [], "data": [ { "summary_name": "PairsEntitySummaryForJSONFormatTest", @@ -971,6 +1321,7 @@ TEST_F(JSONFormatTUSummaryTest, "name": "test.cpp" }, "id_table": [], + "linkage_table": [], "data": [ { "summary_name": "PairsEntitySummaryForJSONFormatTest", @@ -1010,6 +1361,7 @@ TEST_F(JSONFormatTUSummaryTest, "name": "test.cpp" }, "id_table": [], + "linkage_table": [], "data": [ { "summary_name": "PairsEntitySummaryForJSONFormatTest", @@ -1050,6 +1402,7 @@ TEST_F(JSONFormatTUSummaryTest, "name": "test.cpp" }, "id_table": [], + "linkage_table": [], "data": [ { "summary_name": "PairsEntitySummaryForJSONFormatTest", @@ -1089,6 +1442,7 @@ TEST_F(JSONFormatTUSummaryTest, "name": "test.cpp" }, "id_table": [], + "linkage_table": [], "data": [ { "summary_name": "PairsEntitySummaryForJSONFormatTest", @@ -1132,6 +1486,7 @@ TEST_F(JSONFormatTUSummaryTest, EntityDataMissingEntityID) { "name": "test.cpp" }, "id_table": [], + "linkage_table": [], "data": [ { "summary_name": "PairsEntitySummaryForJSONFormatTest", @@ -1163,6 +1518,7 @@ TEST_F(JSONFormatTUSummaryTest, EntityDataMissingEntitySummary) { "name": "test.cpp" }, "id_table": [], + "linkage_table": [], "data": [ { "summary_name": "PairsEntitySummaryForJSONFormatTest", @@ -1194,6 +1550,7 @@ TEST_F(JSONFormatTUSummaryTest, EntityIDNotUInt64) { "name": "test.cpp" }, "id_table": [], + "linkage_table": [], "data": [ { "summary_name": "PairsEntitySummaryForJSONFormatTest", @@ -1230,6 +1587,7 @@ TEST_F(JSONFormatTUSummaryTest, EntityDataElementNotObject) { "name": "test.cpp" }, "id_table": [], + "linkage_table": [], "data": [ { "summary_name": "PairsEntitySummaryForJSONFormatTest", @@ -1265,6 +1623,14 @@ TEST_F(JSONFormatTUSummaryTest, DuplicateEntityIdInDataMap) { } } ], + "linkage_table": [ + { + "id": 0, + "linkage": { + "type": "none" + } + } + ], "data": [ { "summary_name": "PairsEntitySummaryForJSONFormatTest", @@ -1308,6 +1674,7 @@ TEST_F(JSONFormatTUSummaryTest, DataEntryMissingSummaryName) { "name": "test.cpp" }, "id_table": [], + "linkage_table": [], "data": [ { "summary_data": [] @@ -1332,6 +1699,7 @@ TEST_F(JSONFormatTUSummaryTest, DataEntryMissingData) { "name": "test.cpp" }, "id_table": [], + "linkage_table": [], "data": [ { "summary_name": "PairsEntitySummaryForJSONFormatTest" @@ -1361,6 +1729,7 @@ TEST_F(JSONFormatTUSummaryTest, DataNotArray) { "name": "test.cpp" }, "id_table": [], + "linkage_table": [], "data": {} })"); @@ -1379,6 +1748,7 @@ TEST_F(JSONFormatTUSummaryTest, DataElementNotObject) { "name": "test.cpp" }, "id_table": [], + "linkage_table": [], "data": ["invalid"] })"); @@ -1397,6 +1767,7 @@ TEST_F(JSONFormatTUSummaryTest, DuplicateSummaryName) { "name": "test.cpp" }, "id_table": [], + "linkage_table": [], "data": [ { "summary_name": "PairsEntitySummaryForJSONFormatTest", @@ -1425,6 +1796,7 @@ TEST_F(JSONFormatTUSummaryTest, DuplicateSummaryName) { TEST_F(JSONFormatTUSummaryTest, MissingTUNamespace) { auto Result = readTUSummaryFromString(R"({ "id_table": [], + "linkage_table": [], "data": [] })"); @@ -1452,13 +1824,32 @@ TEST_F(JSONFormatTUSummaryTest, MissingIDTable) { HasSubstr("expected JSON array")))); } +TEST_F(JSONFormatTUSummaryTest, MissingLinkageTable) { + auto Result = readTUSummaryFromString(R"({ + "tu_namespace": { + "kind": "compilation_unit", + "name": "test.cpp" + }, + "id_table": [], + "data": [] + })"); + + EXPECT_THAT_EXPECTED( + Result, + FailedWithMessage(AllOf( + HasSubstr("reading TUSummary from file"), + HasSubstr("failed to read LinkageTable from field 'linkage_table'"), + HasSubstr("expected JSON array")))); +} + TEST_F(JSONFormatTUSummaryTest, MissingData) { auto Result = readTUSummaryFromString(R"({ "tu_namespace": { "kind": "compilation_unit", "name": "test.cpp" }, - "id_table": [] + "id_table": [], + "linkage_table": [] })"); EXPECT_THAT_EXPECTED( @@ -1563,6 +1954,7 @@ TEST_F(JSONFormatTUSummaryTest, Empty) { "name": "test.cpp" }, "id_table": [], + "linkage_table": [], "data": [] })"); } @@ -1574,6 +1966,7 @@ TEST_F(JSONFormatTUSummaryTest, LinkUnit) { "name": "libtest.so" }, "id_table": [], + "linkage_table": [], "data": [] })"); } @@ -1616,6 +2009,16 @@ TEST_F(JSONFormatTUSummaryTest, WithIDTable) { } } ], + "linkage_table": [ + { + "id": 0, + "linkage": { "type": "none" } + }, + { + "id": 1, + "linkage": { "type": "internal" } + } + ], "data": [] })"); } @@ -1627,6 +2030,7 @@ TEST_F(JSONFormatTUSummaryTest, WithEmptyDataEntry) { "name": "test.cpp" }, "id_table": [], + "linkage_table": [], "data": [ { "summary_name": "PairsEntitySummaryForJSONFormatTest", @@ -1657,6 +2061,12 @@ TEST_F(JSONFormatTUSummaryTest, RoundTripWithIDTable) { } } ], + "linkage_table": [ + { + "id": 0, + "linkage": { "type": "none" } + } + ], "data": [] })"); } @@ -1708,6 +2118,20 @@ TEST_F(JSONFormatTUSummaryTest, RoundTripPairsEntitySummaryForJSONFormatTest) { } } ], + "linkage_table": [ + { + "id": 0, + "linkage": { "type": "none" } + }, + { + "id": 1, + "linkage": { "type": "internal" } + }, + { + "id": 2, + "linkage": { "type": "external" } + } + ], "data": [ { "summary_name": "PairsEntitySummaryForJSONFormatTest", @@ -1733,4 +2157,174 @@ TEST_F(JSONFormatTUSummaryTest, RoundTripPairsEntitySummaryForJSONFormatTest) { })"); } +TEST_F(JSONFormatTUSummaryTest, EmptyLinkageTable) { + readWriteCompareTUSummary(R"({ + "tu_namespace": { + "kind": "compilation_unit", + "name": "test.cpp" + }, + "id_table": [], + "linkage_table": [], + "data": [] + })"); +} + +TEST_F(JSONFormatTUSummaryTest, LinkageTableWithNoneLinkage) { + readWriteCompareTUSummary(R"({ + "tu_namespace": { + "kind": "compilation_unit", + "name": "test.cpp" + }, + "id_table": [ + { + "id": 0, + "name": { + "usr": "c:@F@foo", + "suffix": "", + "namespace": [ + { + "kind": "compilation_unit", + "name": "test.cpp" + } + ] + } + } + ], + "linkage_table": [ + { + "id": 0, + "linkage": { "type": "none" } + } + ], + "data": [] + })"); +} + +TEST_F(JSONFormatTUSummaryTest, LinkageTableWithInternalLinkage) { + readWriteCompareTUSummary(R"({ + "tu_namespace": { + "kind": "compilation_unit", + "name": "test.cpp" + }, + "id_table": [ + { + "id": 0, + "name": { + "usr": "c:@F@bar", + "suffix": "", + "namespace": [ + { + "kind": "compilation_unit", + "name": "test.cpp" + } + ] + } + } + ], + "linkage_table": [ + { + "id": 0, + "linkage": { "type": "internal" } + } + ], + "data": [] + })"); +} + +TEST_F(JSONFormatTUSummaryTest, LinkageTableWithExternalLinkage) { + readWriteCompareTUSummary(R"({ + "tu_namespace": { + "kind": "compilation_unit", + "name": "test.cpp" + }, + "id_table": [ + { + "id": 0, + "name": { + "usr": "c:@F@baz", + "suffix": "", + "namespace": [ + { + "kind": "compilation_unit", + "name": "test.cpp" + } + ] + } + } + ], + "linkage_table": [ + { + "id": 0, + "linkage": { "type": "external" } + } + ], + "data": [] + })"); +} + +TEST_F(JSONFormatTUSummaryTest, LinkageTableWithMultipleEntries) { + readWriteCompareTUSummary(R"({ + "tu_namespace": { + "kind": "compilation_unit", + "name": "test.cpp" + }, + "id_table": [ + { + "id": 0, + "name": { + "usr": "c:@F@foo", + "suffix": "", + "namespace": [ + { + "kind": "compilation_unit", + "name": "test.cpp" + } + ] + } + }, + { + "id": 1, + "name": { + "usr": "c:@F@bar", + "suffix": "", + "namespace": [ + { + "kind": "compilation_unit", + "name": "test.cpp" + } + ] + } + }, + { + "id": 2, + "name": { + "usr": "c:@F@baz", + "suffix": "", + "namespace": [ + { + "kind": "compilation_unit", + "name": "test.cpp" + } + ] + } + } + ], + "linkage_table": [ + { + "id": 0, + "linkage": { "type": "none" } + }, + { + "id": 1, + "linkage": { "type": "internal" } + }, + { + "id": 2, + "linkage": { "type": "external" } + } + ], + "data": [] + })"); +} + } // anonymous namespace >From 20c01095d4399519b721c954268a56b905ac0012 Mon Sep 17 00:00:00 2001 From: Aviral Goel <[email protected]> Date: Mon, 23 Feb 2026 16:21:07 -0800 Subject: [PATCH 2/5] Error message fix --- .../Scalable/Serialization/JSONFormat.cpp | 27 ++++++++++------- .../JSONFormatTest/TUSummaryTest.cpp | 29 +++++++++---------- 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/clang/lib/Analysis/Scalable/Serialization/JSONFormat.cpp b/clang/lib/Analysis/Scalable/Serialization/JSONFormat.cpp index 55243c08133c0..efb8ea54fcca1 100644 --- a/clang/lib/Analysis/Scalable/Serialization/JSONFormat.cpp +++ b/clang/lib/Analysis/Scalable/Serialization/JSONFormat.cpp @@ -76,13 +76,15 @@ constexpr const char *InvalidBuildNamespaceKind = "invalid 'kind' BuildNamespaceKind value '{0}'"; constexpr const char *InvalidEntityLinkageType = - "invalid 'linkage' EntityLinkage value '{0}'"; + "invalid 'type' EntityLinkageType value '{0}'"; -constexpr const char *LinkageTableExtraId = - "linkage_table contains EntityId '{0}' not present in id_table"; +constexpr const char *FailedToDeserializeLinkageTableExtraId = + "failed to deserialize LinkageTable: extra EntityId '{0}' not present in " + "IdTable"; -constexpr const char *LinkageTableMissingId = - "linkage_table is missing EntityId '{0}' present in id_table"; +constexpr const char *FailedToDeserializeLinkageTableMissingId = + "failed to deserialize LinkageTable: missing EntityId '{0}' present in " + "IdTable"; } // namespace ErrorMessages @@ -399,22 +401,25 @@ JSONFormat::linkageTableFromJSON(const Array &LinkageTableArray, ErrorMessages::FailedInsertionOnDuplication, "LinkageTable entry", Index, "EntityId", getIndex(It->first)) + .context(ErrorMessages::ReadingFromIndex, "LinkageTable entry", Index) .build(); } if (EntityIds.erase(EI) == 0) { - return ErrorBuilder::create(std::errc::invalid_argument, - ErrorMessages::LinkageTableExtraId, - getIndex(EI)) + return ErrorBuilder::create( + std::errc::invalid_argument, + ErrorMessages::FailedToDeserializeLinkageTableExtraId, + getIndex(EI)) .context(ErrorMessages::ReadingFromIndex, "LinkageTable entry", Index) .build(); } } if (!EntityIds.empty()) { - return ErrorBuilder::create(std::errc::invalid_argument, - ErrorMessages::LinkageTableMissingId, - getIndex(*EntityIds.begin())) + return ErrorBuilder::create( + std::errc::invalid_argument, + ErrorMessages::FailedToDeserializeLinkageTableMissingId, + getIndex(*EntityIds.begin())) .build(); } diff --git a/clang/unittests/Analysis/Scalable/Serialization/JSONFormatTest/TUSummaryTest.cpp b/clang/unittests/Analysis/Scalable/Serialization/JSONFormatTest/TUSummaryTest.cpp index 1fb9a966217c8..ee20a48a321cb 100644 --- a/clang/unittests/Analysis/Scalable/Serialization/JSONFormatTest/TUSummaryTest.cpp +++ b/clang/unittests/Analysis/Scalable/Serialization/JSONFormatTest/TUSummaryTest.cpp @@ -404,7 +404,7 @@ TEST_F(JSONFormatTUSummaryTest, LinkageTableEntryLinkageInvalidType) { HasSubstr("reading LinkageTable from field 'linkage_table'"), HasSubstr("reading LinkageTable entry from index '0'"), HasSubstr("reading EntityLinkage from field 'linkage'"), - HasSubstr("invalid 'linkage' EntityLinkage value 'invalid_type'")))); + HasSubstr("invalid 'type' EntityLinkageType value 'invalid_type'")))); } // ============================================================================ @@ -546,13 +546,12 @@ TEST_F(JSONFormatTUSummaryTest, LinkageTableExtraId) { })"); EXPECT_THAT_EXPECTED( - Result, - FailedWithMessage(AllOf( - HasSubstr("reading TUSummary from file"), - HasSubstr("reading LinkageTable from field 'linkage_table'"), - HasSubstr("reading LinkageTable entry from index '0'"), - HasSubstr( - "linkage_table contains EntityId '0' not present in id_table")))); + Result, FailedWithMessage(AllOf( + HasSubstr("reading TUSummary from file"), + HasSubstr("reading LinkageTable from field 'linkage_table'"), + HasSubstr("reading LinkageTable entry from index '0'"), + HasSubstr("failed to deserialize LinkageTable"), + HasSubstr("extra EntityId '0' not present in IdTable")))); } TEST_F(JSONFormatTUSummaryTest, LinkageTableMissingId) { @@ -581,11 +580,11 @@ TEST_F(JSONFormatTUSummaryTest, LinkageTableMissingId) { })"); EXPECT_THAT_EXPECTED( - Result, - FailedWithMessage(AllOf( - HasSubstr("reading TUSummary from file"), - HasSubstr( - "linkage_table is missing EntityId '0' present in id_table")))); + Result, FailedWithMessage(AllOf( + HasSubstr("reading TUSummary from file"), + HasSubstr("reading LinkageTable from field 'linkage_table'"), + HasSubstr("failed to deserialize LinkageTable"), + HasSubstr("missing EntityId '0' present in IdTable")))); } TEST_F(JSONFormatTUSummaryTest, LinkageTableDuplicateId) { @@ -1145,11 +1144,11 @@ TEST_F(JSONFormatTUSummaryTest, DuplicateEntity) { "linkage_table": [ { "id": 0, - "linkage": { "type": "none" } + "linkage": { "type": "internal" } }, { "id": 1, - "linkage": { "type": "none" } + "linkage": { "type": "external" } } ], "data": [] >From 7be5cf0829c250c6284196f74c2a9ba65eaa6daf Mon Sep 17 00:00:00 2001 From: Aviral Goel <[email protected]> Date: Mon, 23 Feb 2026 16:25:03 -0800 Subject: [PATCH 3/5] Reorder --- .../Scalable/Serialization/JSONFormat.cpp | 382 +++++++++--------- 1 file changed, 191 insertions(+), 191 deletions(-) diff --git a/clang/lib/Analysis/Scalable/Serialization/JSONFormat.cpp b/clang/lib/Analysis/Scalable/Serialization/JSONFormat.cpp index efb8ea54fcca1..1c79f41df11a3 100644 --- a/clang/lib/Analysis/Scalable/Serialization/JSONFormat.cpp +++ b/clang/lib/Analysis/Scalable/Serialization/JSONFormat.cpp @@ -247,197 +247,6 @@ llvm::StringRef buildNamespaceKindToJSON(BuildNamespaceKind BNK) { } // namespace -//---------------------------------------------------------------------------- -// EntityLinkageType -//---------------------------------------------------------------------------- - -namespace { - -std::optional<EntityLinkage::LinkageType> -parseEntityLinkageType(llvm::StringRef S) { - if (S == "none") - return EntityLinkage::LinkageType::None; - if (S == "internal") - return EntityLinkage::LinkageType::Internal; - if (S == "external") - return EntityLinkage::LinkageType::External; - return std::nullopt; -} - -llvm::StringRef entityLinkageTypeToJSON(EntityLinkage::LinkageType LT) { - switch (LT) { - case EntityLinkage::LinkageType::None: - return "none"; - case EntityLinkage::LinkageType::Internal: - return "internal"; - case EntityLinkage::LinkageType::External: - return "external"; - } - llvm_unreachable("Unhandled EntityLinkage::LinkageType variant"); -} - -} // namespace - -//---------------------------------------------------------------------------- -// EntityLinkage -//---------------------------------------------------------------------------- - -llvm::Expected<EntityLinkage> -JSONFormat::entityLinkageFromJSON(const Object &EntityLinkageObject) const { - auto OptLinkageStr = EntityLinkageObject.getString("type"); - if (!OptLinkageStr) { - return ErrorBuilder::create(std::errc::invalid_argument, - ErrorMessages::FailedToReadObjectAtField, - "EntityLinkageType", "type", "string") - .build(); - } - - auto OptLinkageType = parseEntityLinkageType(*OptLinkageStr); - if (!OptLinkageType) { - return ErrorBuilder::create(std::errc::invalid_argument, - ErrorMessages::InvalidEntityLinkageType, - *OptLinkageStr) - .build(); - } - - return EntityLinkage(*OptLinkageType); -} - -Object JSONFormat::entityLinkageToJSON(const EntityLinkage &EL) const { - Object Result; - Result["type"] = entityLinkageTypeToJSON(getLinkage(EL)); - return Result; -} - -//---------------------------------------------------------------------------- -// LinkageTableEntry -//---------------------------------------------------------------------------- - -llvm::Expected<std::pair<EntityId, EntityLinkage>> -JSONFormat::linkageTableEntryFromJSON( - const Object &LinkageTableEntryObject) const { - const Value *EntityIdIntValue = LinkageTableEntryObject.get("id"); - if (!EntityIdIntValue) { - return ErrorBuilder::create(std::errc::invalid_argument, - ErrorMessages::FailedToReadObjectAtField, - "EntityId", "id", - "number (unsigned 64-bit integer)") - .build(); - } - - const std::optional<uint64_t> OptEntityIdInt = - EntityIdIntValue->getAsUINT64(); - if (!OptEntityIdInt) { - return ErrorBuilder::create(std::errc::invalid_argument, - ErrorMessages::FailedToReadObjectAtField, - "EntityId", "id", - "number (unsigned 64-bit integer)") - .build(); - } - - EntityId EI = entityIdFromJSON(*OptEntityIdInt); - - const Object *OptEntityLinkageObject = - LinkageTableEntryObject.getObject("linkage"); - if (!OptEntityLinkageObject) { - return ErrorBuilder::create(std::errc::invalid_argument, - ErrorMessages::FailedToReadObjectAtField, - "EntityLinkage", "linkage", "object") - .build(); - } - - auto ExpectedEntityLinkage = entityLinkageFromJSON(*OptEntityLinkageObject); - if (!ExpectedEntityLinkage) { - return ErrorBuilder::wrap(ExpectedEntityLinkage.takeError()) - .context(ErrorMessages::ReadingFromField, "EntityLinkage", "linkage") - .build(); - } - - return std::make_pair(std::move(EI), std::move(*ExpectedEntityLinkage)); -} - -Object JSONFormat::linkageTableEntryToJSON(EntityId EI, - const EntityLinkage &EL) const { - Object Entry; - Entry["id"] = entityIdToJSON(EI); - Entry["linkage"] = entityLinkageToJSON(EL); - return Entry; -} - -//---------------------------------------------------------------------------- -// LinkageTable -//---------------------------------------------------------------------------- - -llvm::Expected<std::map<EntityId, EntityLinkage>> -JSONFormat::linkageTableFromJSON(const Array &LinkageTableArray, - std::set<EntityId> EntityIds) const { - std::map<EntityId, EntityLinkage> LinkageTable; - - for (const auto &[Index, LinkageTableEntryValue] : - llvm::enumerate(LinkageTableArray)) { - - const Object *OptLinkageTableEntryObject = - LinkageTableEntryValue.getAsObject(); - if (!OptLinkageTableEntryObject) { - return ErrorBuilder::create(std::errc::invalid_argument, - ErrorMessages::FailedToReadObjectAtIndex, - "LinkageTable entry", Index, "object") - .build(); - } - - auto ExpectedLinkageTableEntry = - linkageTableEntryFromJSON(*OptLinkageTableEntryObject); - if (!ExpectedLinkageTableEntry) - return ErrorBuilder::wrap(ExpectedLinkageTableEntry.takeError()) - .context(ErrorMessages::ReadingFromIndex, "LinkageTable entry", Index) - .build(); - - const EntityId EI = ExpectedLinkageTableEntry->first; - - auto [It, Inserted] = - LinkageTable.insert(std::move(*ExpectedLinkageTableEntry)); - if (!Inserted) { - return ErrorBuilder::create(std::errc::invalid_argument, - ErrorMessages::FailedInsertionOnDuplication, - "LinkageTable entry", Index, "EntityId", - getIndex(It->first)) - .context(ErrorMessages::ReadingFromIndex, "LinkageTable entry", Index) - .build(); - } - - if (EntityIds.erase(EI) == 0) { - return ErrorBuilder::create( - std::errc::invalid_argument, - ErrorMessages::FailedToDeserializeLinkageTableExtraId, - getIndex(EI)) - .context(ErrorMessages::ReadingFromIndex, "LinkageTable entry", Index) - .build(); - } - } - - if (!EntityIds.empty()) { - return ErrorBuilder::create( - std::errc::invalid_argument, - ErrorMessages::FailedToDeserializeLinkageTableMissingId, - getIndex(*EntityIds.begin())) - .build(); - } - - return LinkageTable; -} - -Array JSONFormat::linkageTableToJSON( - const std::map<EntityId, EntityLinkage> &LinkageTable) const { - Array Result; - Result.reserve(LinkageTable.size()); - - for (const auto &[EI, EL] : LinkageTable) { - Result.push_back(linkageTableEntryToJSON(EI, EL)); - } - - return Result; -} - //---------------------------------------------------------------------------- // NestedBuildNamespace //---------------------------------------------------------------------------- @@ -573,6 +382,68 @@ Object JSONFormat::entityNameToJSON(const EntityName &EN) const { return Result; } +//---------------------------------------------------------------------------- +// EntityLinkageType +//---------------------------------------------------------------------------- + +namespace { + +std::optional<EntityLinkage::LinkageType> +parseEntityLinkageType(llvm::StringRef S) { + if (S == "none") + return EntityLinkage::LinkageType::None; + if (S == "internal") + return EntityLinkage::LinkageType::Internal; + if (S == "external") + return EntityLinkage::LinkageType::External; + return std::nullopt; +} + +llvm::StringRef entityLinkageTypeToJSON(EntityLinkage::LinkageType LT) { + switch (LT) { + case EntityLinkage::LinkageType::None: + return "none"; + case EntityLinkage::LinkageType::Internal: + return "internal"; + case EntityLinkage::LinkageType::External: + return "external"; + } + llvm_unreachable("Unhandled EntityLinkage::LinkageType variant"); +} + +} // namespace + +//---------------------------------------------------------------------------- +// EntityLinkage +//---------------------------------------------------------------------------- + +llvm::Expected<EntityLinkage> +JSONFormat::entityLinkageFromJSON(const Object &EntityLinkageObject) const { + auto OptLinkageStr = EntityLinkageObject.getString("type"); + if (!OptLinkageStr) { + return ErrorBuilder::create(std::errc::invalid_argument, + ErrorMessages::FailedToReadObjectAtField, + "EntityLinkageType", "type", "string") + .build(); + } + + auto OptLinkageType = parseEntityLinkageType(*OptLinkageStr); + if (!OptLinkageType) { + return ErrorBuilder::create(std::errc::invalid_argument, + ErrorMessages::InvalidEntityLinkageType, + *OptLinkageStr) + .build(); + } + + return EntityLinkage(*OptLinkageType); +} + +Object JSONFormat::entityLinkageToJSON(const EntityLinkage &EL) const { + Object Result; + Result["type"] = entityLinkageTypeToJSON(getLinkage(EL)); + return Result; +} + //---------------------------------------------------------------------------- // EntityIdTableEntry //---------------------------------------------------------------------------- @@ -685,6 +556,135 @@ Array JSONFormat::entityIdTableToJSON(const EntityIdTable &IdTable) const { return EntityIdTableArray; } +//---------------------------------------------------------------------------- +// LinkageTableEntry +//---------------------------------------------------------------------------- + +llvm::Expected<std::pair<EntityId, EntityLinkage>> +JSONFormat::linkageTableEntryFromJSON( + const Object &LinkageTableEntryObject) const { + const Value *EntityIdIntValue = LinkageTableEntryObject.get("id"); + if (!EntityIdIntValue) { + return ErrorBuilder::create(std::errc::invalid_argument, + ErrorMessages::FailedToReadObjectAtField, + "EntityId", "id", + "number (unsigned 64-bit integer)") + .build(); + } + + const std::optional<uint64_t> OptEntityIdInt = + EntityIdIntValue->getAsUINT64(); + if (!OptEntityIdInt) { + return ErrorBuilder::create(std::errc::invalid_argument, + ErrorMessages::FailedToReadObjectAtField, + "EntityId", "id", + "number (unsigned 64-bit integer)") + .build(); + } + + EntityId EI = entityIdFromJSON(*OptEntityIdInt); + + const Object *OptEntityLinkageObject = + LinkageTableEntryObject.getObject("linkage"); + if (!OptEntityLinkageObject) { + return ErrorBuilder::create(std::errc::invalid_argument, + ErrorMessages::FailedToReadObjectAtField, + "EntityLinkage", "linkage", "object") + .build(); + } + + auto ExpectedEntityLinkage = entityLinkageFromJSON(*OptEntityLinkageObject); + if (!ExpectedEntityLinkage) { + return ErrorBuilder::wrap(ExpectedEntityLinkage.takeError()) + .context(ErrorMessages::ReadingFromField, "EntityLinkage", "linkage") + .build(); + } + + return std::make_pair(std::move(EI), std::move(*ExpectedEntityLinkage)); +} + +Object JSONFormat::linkageTableEntryToJSON(EntityId EI, + const EntityLinkage &EL) const { + Object Entry; + Entry["id"] = entityIdToJSON(EI); + Entry["linkage"] = entityLinkageToJSON(EL); + return Entry; +} + +//---------------------------------------------------------------------------- +// LinkageTable +//---------------------------------------------------------------------------- + +llvm::Expected<std::map<EntityId, EntityLinkage>> +JSONFormat::linkageTableFromJSON(const Array &LinkageTableArray, + std::set<EntityId> EntityIds) const { + std::map<EntityId, EntityLinkage> LinkageTable; + + for (const auto &[Index, LinkageTableEntryValue] : + llvm::enumerate(LinkageTableArray)) { + + const Object *OptLinkageTableEntryObject = + LinkageTableEntryValue.getAsObject(); + if (!OptLinkageTableEntryObject) { + return ErrorBuilder::create(std::errc::invalid_argument, + ErrorMessages::FailedToReadObjectAtIndex, + "LinkageTable entry", Index, "object") + .build(); + } + + auto ExpectedLinkageTableEntry = + linkageTableEntryFromJSON(*OptLinkageTableEntryObject); + if (!ExpectedLinkageTableEntry) + return ErrorBuilder::wrap(ExpectedLinkageTableEntry.takeError()) + .context(ErrorMessages::ReadingFromIndex, "LinkageTable entry", Index) + .build(); + + const EntityId EI = ExpectedLinkageTableEntry->first; + + auto [It, Inserted] = + LinkageTable.insert(std::move(*ExpectedLinkageTableEntry)); + if (!Inserted) { + return ErrorBuilder::create(std::errc::invalid_argument, + ErrorMessages::FailedInsertionOnDuplication, + "LinkageTable entry", Index, "EntityId", + getIndex(It->first)) + .context(ErrorMessages::ReadingFromIndex, "LinkageTable entry", Index) + .build(); + } + + if (EntityIds.erase(EI) == 0) { + return ErrorBuilder::create( + std::errc::invalid_argument, + ErrorMessages::FailedToDeserializeLinkageTableExtraId, + getIndex(EI)) + .context(ErrorMessages::ReadingFromIndex, "LinkageTable entry", Index) + .build(); + } + } + + if (!EntityIds.empty()) { + return ErrorBuilder::create( + std::errc::invalid_argument, + ErrorMessages::FailedToDeserializeLinkageTableMissingId, + getIndex(*EntityIds.begin())) + .build(); + } + + return LinkageTable; +} + +Array JSONFormat::linkageTableToJSON( + const std::map<EntityId, EntityLinkage> &LinkageTable) const { + Array Result; + Result.reserve(LinkageTable.size()); + + for (const auto &[EI, EL] : LinkageTable) { + Result.push_back(linkageTableEntryToJSON(EI, EL)); + } + + return Result; +} + //---------------------------------------------------------------------------- // EntitySummary //---------------------------------------------------------------------------- >From 875abaa8513e23230fcd99ef0bda4a57f8d57117 Mon Sep 17 00:00:00 2001 From: Aviral Goel <[email protected]> Date: Mon, 23 Feb 2026 16:40:38 -0800 Subject: [PATCH 4/5] Minor fixes. --- .../Scalable/Serialization/JSONFormat.h | 28 +++++++++---------- .../Scalable/Serialization/JSONFormat.cpp | 5 ++-- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/clang/include/clang/Analysis/Scalable/Serialization/JSONFormat.h b/clang/include/clang/Analysis/Scalable/Serialization/JSONFormat.h index 25fdd07b7f469..706ace7e8feb8 100644 --- a/clang/include/clang/Analysis/Scalable/Serialization/JSONFormat.h +++ b/clang/include/clang/Analysis/Scalable/Serialization/JSONFormat.h @@ -72,20 +72,6 @@ class JSONFormat final : public SerializationFormat { llvm::Expected<BuildNamespaceKind> buildNamespaceKindFromJSON(llvm::StringRef BuildNamespaceKindStr) const; - llvm::Expected<EntityLinkage> - entityLinkageFromJSON(const Object &EntityLinkageObject) const; - Object entityLinkageToJSON(const EntityLinkage &EL) const; - - llvm::Expected<std::pair<EntityId, EntityLinkage>> - linkageTableEntryFromJSON(const Object &LinkageTableEntryObject) const; - Object linkageTableEntryToJSON(EntityId EI, const EntityLinkage &EL) const; - - llvm::Expected<std::map<EntityId, EntityLinkage>> - linkageTableFromJSON(const Array &LinkageTableArray, - std::set<EntityId> EntityIds) const; - Array linkageTableToJSON( - const std::map<EntityId, EntityLinkage> &LinkageTable) const; - llvm::Expected<BuildNamespace> buildNamespaceFromJSON(const Object &BuildNamespaceObject) const; Object buildNamespaceToJSON(const BuildNamespace &BN) const; @@ -98,6 +84,10 @@ class JSONFormat final : public SerializationFormat { entityNameFromJSON(const Object &EntityNameObject) const; Object entityNameToJSON(const EntityName &EN) const; + llvm::Expected<EntityLinkage> + entityLinkageFromJSON(const Object &EntityLinkageObject) const; + Object entityLinkageToJSON(const EntityLinkage &EL) const; + llvm::Expected<std::pair<EntityName, EntityId>> entityIdTableEntryFromJSON(const Object &EntityIdTableEntryObject) const; llvm::Expected<EntityIdTable> @@ -105,6 +95,16 @@ class JSONFormat final : public SerializationFormat { Object entityIdTableEntryToJSON(const EntityName &EN, EntityId EI) const; Array entityIdTableToJSON(const EntityIdTable &IdTable) const; + llvm::Expected<std::pair<EntityId, EntityLinkage>> + linkageTableEntryFromJSON(const Object &LinkageTableEntryObject) const; + Object linkageTableEntryToJSON(EntityId EI, const EntityLinkage &EL) const; + + llvm::Expected<std::map<EntityId, EntityLinkage>> + linkageTableFromJSON(const Array &LinkageTableArray, + std::set<EntityId> EntityIds) const; + Array linkageTableToJSON( + const std::map<EntityId, EntityLinkage> &LinkageTable) const; + llvm::Expected<std::unique_ptr<EntitySummary>> entitySummaryFromJSON(const SummaryName &SN, const Object &EntitySummaryObject, diff --git a/clang/lib/Analysis/Scalable/Serialization/JSONFormat.cpp b/clang/lib/Analysis/Scalable/Serialization/JSONFormat.cpp index 1c79f41df11a3..c5449012c18f4 100644 --- a/clang/lib/Analysis/Scalable/Serialization/JSONFormat.cpp +++ b/clang/lib/Analysis/Scalable/Serialization/JSONFormat.cpp @@ -622,7 +622,6 @@ JSONFormat::linkageTableFromJSON(const Array &LinkageTableArray, for (const auto &[Index, LinkageTableEntryValue] : llvm::enumerate(LinkageTableArray)) { - const Object *OptLinkageTableEntryObject = LinkageTableEntryValue.getAsObject(); if (!OptLinkageTableEntryObject) { @@ -634,10 +633,11 @@ JSONFormat::linkageTableFromJSON(const Array &LinkageTableArray, auto ExpectedLinkageTableEntry = linkageTableEntryFromJSON(*OptLinkageTableEntryObject); - if (!ExpectedLinkageTableEntry) + if (!ExpectedLinkageTableEntry) { return ErrorBuilder::wrap(ExpectedLinkageTableEntry.takeError()) .context(ErrorMessages::ReadingFromIndex, "LinkageTable entry", Index) .build(); + } const EntityId EI = ExpectedLinkageTableEntry->first; @@ -648,7 +648,6 @@ JSONFormat::linkageTableFromJSON(const Array &LinkageTableArray, ErrorMessages::FailedInsertionOnDuplication, "LinkageTable entry", Index, "EntityId", getIndex(It->first)) - .context(ErrorMessages::ReadingFromIndex, "LinkageTable entry", Index) .build(); } >From 108e7e3c8a2639c146f9609192b63fa603af2d75 Mon Sep 17 00:00:00 2001 From: Aviral Goel <[email protected]> Date: Tue, 24 Feb 2026 08:18:26 -0800 Subject: [PATCH 5/5] Rename parameter and argument and add an explanatory comment. --- .../Scalable/Serialization/JSONFormat.cpp | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/clang/lib/Analysis/Scalable/Serialization/JSONFormat.cpp b/clang/lib/Analysis/Scalable/Serialization/JSONFormat.cpp index c5449012c18f4..a72eb29a4039b 100644 --- a/clang/lib/Analysis/Scalable/Serialization/JSONFormat.cpp +++ b/clang/lib/Analysis/Scalable/Serialization/JSONFormat.cpp @@ -615,9 +615,13 @@ Object JSONFormat::linkageTableEntryToJSON(EntityId EI, // LinkageTable //---------------------------------------------------------------------------- +// ExpectedIds is the set of EntityIds from the IdTable that must appear in the +// linkage table—no more, no fewer. It is taken by value because it is consumed +// during parsing: each successfully matched id is erased from the set, and any +// ids remaining at the end are reported as missing. llvm::Expected<std::map<EntityId, EntityLinkage>> JSONFormat::linkageTableFromJSON(const Array &LinkageTableArray, - std::set<EntityId> EntityIds) const { + std::set<EntityId> ExpectedIds) const { std::map<EntityId, EntityLinkage> LinkageTable; for (const auto &[Index, LinkageTableEntryValue] : @@ -651,7 +655,7 @@ JSONFormat::linkageTableFromJSON(const Array &LinkageTableArray, .build(); } - if (EntityIds.erase(EI) == 0) { + if (ExpectedIds.erase(EI) == 0) { return ErrorBuilder::create( std::errc::invalid_argument, ErrorMessages::FailedToDeserializeLinkageTableExtraId, @@ -661,11 +665,11 @@ JSONFormat::linkageTableFromJSON(const Array &LinkageTableArray, } } - if (!EntityIds.empty()) { + if (!ExpectedIds.empty()) { return ErrorBuilder::create( std::errc::invalid_argument, ErrorMessages::FailedToDeserializeLinkageTableMissingId, - getIndex(*EntityIds.begin())) + getIndex(*ExpectedIds.begin())) .build(); } @@ -1054,12 +1058,15 @@ llvm::Expected<TUSummary> JSONFormat::readTUSummary(llvm::StringRef Path) { .build(); } - auto EntityIdRange = + auto ExpectedIdRange = llvm::make_second_range(getEntities(getIdTable(Summary))); - std::set<EntityId> EntityIds(EntityIdRange.begin(), EntityIdRange.end()); + std::set<EntityId> ExpectedIds(ExpectedIdRange.begin(), + ExpectedIdRange.end()); + // Move ExpectedIds in since linkageTableFromJSON consumes it to verify + // that the linkage table contains exactly the ids present in the IdTable. auto ExpectedLinkageTable = - linkageTableFromJSON(*LinkageTableArray, std::move(EntityIds)); + linkageTableFromJSON(*LinkageTableArray, std::move(ExpectedIds)); if (!ExpectedLinkageTable) { return ErrorBuilder::wrap(ExpectedLinkageTable.takeError()) .context(ErrorMessages::ReadingFromField, "LinkageTable", _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
