[clang] a04eb62 - [clang][ssaf] Add JSON serialization support for `TUSummary::LinkageTable`
via cfe-commits
cfe-commits at lists.llvm.org
Tue Feb 24 09:37:46 PST 2026
Author: Aviral Goel
Date: 2026-02-24T17:37:41Z
New Revision: a04eb62de6961f4300f780664b0207fac6f4497d
URL: https://github.com/llvm/llvm-project/commit/a04eb62de6961f4300f780664b0207fac6f4497d
DIFF: https://github.com/llvm/llvm-project/commit/a04eb62de6961f4300f780664b0207fac6f4497d.diff
LOG: [clang][ssaf] Add JSON serialization support for `TUSummary::LinkageTable`
This change adds full read/write support for the
`TUSummary::LinkageTable` field that maps each entity to its linkage
kind. The deserialization step validates that the set of entity ids in
`LinkageTable` exactly matches the set in `IdTable` in a single `O(N log
N)` pass. Existing tests have been updated and new tests have been added
to ensure 100% coverage of the new code.
Added:
Modified:
clang/include/clang/Analysis/Scalable/Serialization/JSONFormat.h
clang/lib/Analysis/Scalable/Serialization/JSONFormat.cpp
clang/unittests/Analysis/Scalable/Serialization/JSONFormatTest/TUSummaryTest.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Analysis/Scalable/Serialization/JSONFormat.h b/clang/include/clang/Analysis/Scalable/Serialization/JSONFormat.h
index 052aa2641dbce..706ace7e8feb8 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;
@@ -81,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>
@@ -88,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 d6c6e5223ea0f..a72eb29a4039b 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,17 @@ constexpr const char *FailedToSerializeEntitySummary =
constexpr const char *InvalidBuildNamespaceKind =
"invalid 'kind' BuildNamespaceKind value '{0}'";
+constexpr const char *InvalidEntityLinkageType =
+ "invalid 'type' EntityLinkageType value '{0}'";
+
+constexpr const char *FailedToDeserializeLinkageTableExtraId =
+ "failed to deserialize LinkageTable: extra EntityId '{0}' not present in "
+ "IdTable";
+
+constexpr const char *FailedToDeserializeLinkageTableMissingId =
+ "failed to deserialize LinkageTable: missing EntityId '{0}' present in "
+ "IdTable";
+
} // namespace ErrorMessages
} // namespace
@@ -234,7 +248,7 @@ llvm::StringRef buildNamespaceKindToJSON(BuildNamespaceKind BNK) {
} // namespace
//----------------------------------------------------------------------------
-// BuildNamespace
+// NestedBuildNamespace
//----------------------------------------------------------------------------
llvm::Expected<BuildNamespace>
@@ -368,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
//----------------------------------------------------------------------------
@@ -480,6 +556,138 @@ 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
+//----------------------------------------------------------------------------
+
+// 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> ExpectedIds) 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 (ExpectedIds.erase(EI) == 0) {
+ return ErrorBuilder::create(
+ std::errc::invalid_argument,
+ ErrorMessages::FailedToDeserializeLinkageTableExtraId,
+ getIndex(EI))
+ .context(ErrorMessages::ReadingFromIndex, "LinkageTable entry", Index)
+ .build();
+ }
+ }
+
+ if (!ExpectedIds.empty()) {
+ return ErrorBuilder::create(
+ std::errc::invalid_argument,
+ ErrorMessages::FailedToDeserializeLinkageTableMissingId,
+ getIndex(*ExpectedIds.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
//----------------------------------------------------------------------------
@@ -840,6 +1048,36 @@ 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 ExpectedIdRange =
+ llvm::make_second_range(getEntities(getIdTable(Summary)));
+ 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(ExpectedIds));
+ 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 +1112,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..ee20a48a321cb 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,285 @@ 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 'type' EntityLinkageType 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("failed to deserialize LinkageTable"),
+ HasSubstr("extra EntityId '0' not present in IdTable"))));
+}
+
+TEST_F(JSONFormatTUSummaryTest, LinkageTableMissingId) {
+ auto Result = readTUSummaryFromString(R"({
+ "tu_namespace": {
+ "kind": "compilation_unit",
+ "name": "test.cpp"
+ },
+ "id_table": [
+ {
+ "id": 0,
+ "name": {
+ "usr": "c:@F at foo",
+ "suffix": "",
+ "namespace": [
+ {
+ "kind": "compilation_unit",
+ "name": "test.cpp"
+ }
+ ]
+ }
+ }
+ ],
+ "linkage_table": [],
+ "data": []
+ })");
+
+ EXPECT_THAT_EXPECTED(
+ 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) {
+ auto Result = readTUSummaryFromString(R"({
+ "tu_namespace": {
+ "kind": "compilation_unit",
+ "name": "test.cpp"
+ },
+ "id_table": [
+ {
+ "id": 0,
+ "name": {
+ "usr": "c:@F at 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 +640,7 @@ TEST_F(JSONFormatTUSummaryTest, InvalidKind) {
"name": "test.cpp"
},
"id_table": [],
+ "linkage_table": [],
"data": []
})");
@@ -383,6 +664,7 @@ TEST_F(JSONFormatTUSummaryTest, MissingKind) {
"name": "test.cpp"
},
"id_table": [],
+ "linkage_table": [],
"data": []
})");
@@ -401,6 +683,7 @@ TEST_F(JSONFormatTUSummaryTest, MissingName) {
"kind": "compilation_unit"
},
"id_table": [],
+ "linkage_table": [],
"data": []
})");
@@ -433,6 +716,12 @@ TEST_F(JSONFormatTUSummaryTest, NamespaceElementNotObject) {
}
}
],
+ "linkage_table": [
+ {
+ "id": 0,
+ "linkage": { "type": "none" }
+ }
+ ],
"data": []
})");
@@ -468,6 +757,12 @@ TEST_F(JSONFormatTUSummaryTest, NamespaceElementMissingKind) {
}
}
],
+ "linkage_table": [
+ {
+ "id": 0,
+ "linkage": { "type": "internal" }
+ }
+ ],
"data": []
})");
@@ -505,6 +800,12 @@ TEST_F(JSONFormatTUSummaryTest, NamespaceElementInvalidKind) {
}
}
],
+ "linkage_table": [
+ {
+ "id": 0,
+ "linkage": { "type": "external" }
+ }
+ ],
"data": []
})");
@@ -542,6 +843,12 @@ TEST_F(JSONFormatTUSummaryTest, NamespaceElementMissingName) {
}
}
],
+ "linkage_table": [
+ {
+ "id": 0,
+ "linkage": { "type": "none" }
+ }
+ ],
"data": []
})");
@@ -577,6 +884,12 @@ TEST_F(JSONFormatTUSummaryTest, EntityNameMissingUSR) {
}
}
],
+ "linkage_table": [
+ {
+ "id": 0,
+ "linkage": { "type": "internal" }
+ }
+ ],
"data": []
})");
@@ -605,6 +918,12 @@ TEST_F(JSONFormatTUSummaryTest, EntityNameMissingSuffix) {
}
}
],
+ "linkage_table": [
+ {
+ "id": 0,
+ "linkage": { "type": "external" }
+ }
+ ],
"data": []
})");
@@ -633,6 +952,12 @@ TEST_F(JSONFormatTUSummaryTest, EntityNameMissingNamespace) {
}
}
],
+ "linkage_table": [
+ {
+ "id": 0,
+ "linkage": { "type": "none" }
+ }
+ ],
"data": []
})");
@@ -667,6 +992,7 @@ TEST_F(JSONFormatTUSummaryTest, IDTableEntryMissingID) {
}
}
],
+ "linkage_table": [],
"data": []
})");
@@ -691,6 +1017,12 @@ TEST_F(JSONFormatTUSummaryTest, IDTableEntryMissingName) {
"id": 0
}
],
+ "linkage_table": [
+ {
+ "id": 0,
+ "linkage": { "type": "none" }
+ }
+ ],
"data": []
})");
@@ -719,6 +1051,7 @@ TEST_F(JSONFormatTUSummaryTest, IDTableEntryIDNotUInt64) {
}
}
],
+ "linkage_table": [],
"data": []
})");
@@ -743,6 +1076,7 @@ TEST_F(JSONFormatTUSummaryTest, IDTableNotArray) {
"name": "test.cpp"
},
"id_table": {},
+ "linkage_table": [],
"data": []
})");
@@ -760,6 +1094,7 @@ TEST_F(JSONFormatTUSummaryTest, IDTableElementNotObject) {
"name": "test.cpp"
},
"id_table": [123],
+ "linkage_table": [],
"data": []
})");
@@ -806,6 +1141,16 @@ TEST_F(JSONFormatTUSummaryTest, DuplicateEntity) {
}
}
],
+ "linkage_table": [
+ {
+ "id": 0,
+ "linkage": { "type": "internal" }
+ },
+ {
+ "id": 1,
+ "linkage": { "type": "external" }
+ }
+ ],
"data": []
})");
@@ -829,6 +1174,7 @@ TEST_F(JSONFormatTUSummaryTest, EntitySummaryNoFormatInfo) {
"name": "test.cpp"
},
"id_table": [],
+ "linkage_table": [],
"data": [
{
"summary_name": "unknown_summary_type",
@@ -868,6 +1214,7 @@ TEST_F(JSONFormatTUSummaryTest,
"name": "test.cpp"
},
"id_table": [],
+ "linkage_table": [],
"data": [
{
"summary_name": "PairsEntitySummaryForJSONFormatTest",
@@ -901,6 +1248,7 @@ TEST_F(JSONFormatTUSummaryTest,
"name": "test.cpp"
},
"id_table": [],
+ "linkage_table": [],
"data": [
{
"summary_name": "PairsEntitySummaryForJSONFormatTest",
@@ -936,6 +1284,7 @@ TEST_F(JSONFormatTUSummaryTest,
"name": "test.cpp"
},
"id_table": [],
+ "linkage_table": [],
"data": [
{
"summary_name": "PairsEntitySummaryForJSONFormatTest",
@@ -971,6 +1320,7 @@ TEST_F(JSONFormatTUSummaryTest,
"name": "test.cpp"
},
"id_table": [],
+ "linkage_table": [],
"data": [
{
"summary_name": "PairsEntitySummaryForJSONFormatTest",
@@ -1010,6 +1360,7 @@ TEST_F(JSONFormatTUSummaryTest,
"name": "test.cpp"
},
"id_table": [],
+ "linkage_table": [],
"data": [
{
"summary_name": "PairsEntitySummaryForJSONFormatTest",
@@ -1050,6 +1401,7 @@ TEST_F(JSONFormatTUSummaryTest,
"name": "test.cpp"
},
"id_table": [],
+ "linkage_table": [],
"data": [
{
"summary_name": "PairsEntitySummaryForJSONFormatTest",
@@ -1089,6 +1441,7 @@ TEST_F(JSONFormatTUSummaryTest,
"name": "test.cpp"
},
"id_table": [],
+ "linkage_table": [],
"data": [
{
"summary_name": "PairsEntitySummaryForJSONFormatTest",
@@ -1132,6 +1485,7 @@ TEST_F(JSONFormatTUSummaryTest, EntityDataMissingEntityID) {
"name": "test.cpp"
},
"id_table": [],
+ "linkage_table": [],
"data": [
{
"summary_name": "PairsEntitySummaryForJSONFormatTest",
@@ -1163,6 +1517,7 @@ TEST_F(JSONFormatTUSummaryTest, EntityDataMissingEntitySummary) {
"name": "test.cpp"
},
"id_table": [],
+ "linkage_table": [],
"data": [
{
"summary_name": "PairsEntitySummaryForJSONFormatTest",
@@ -1194,6 +1549,7 @@ TEST_F(JSONFormatTUSummaryTest, EntityIDNotUInt64) {
"name": "test.cpp"
},
"id_table": [],
+ "linkage_table": [],
"data": [
{
"summary_name": "PairsEntitySummaryForJSONFormatTest",
@@ -1230,6 +1586,7 @@ TEST_F(JSONFormatTUSummaryTest, EntityDataElementNotObject) {
"name": "test.cpp"
},
"id_table": [],
+ "linkage_table": [],
"data": [
{
"summary_name": "PairsEntitySummaryForJSONFormatTest",
@@ -1265,6 +1622,14 @@ TEST_F(JSONFormatTUSummaryTest, DuplicateEntityIdInDataMap) {
}
}
],
+ "linkage_table": [
+ {
+ "id": 0,
+ "linkage": {
+ "type": "none"
+ }
+ }
+ ],
"data": [
{
"summary_name": "PairsEntitySummaryForJSONFormatTest",
@@ -1308,6 +1673,7 @@ TEST_F(JSONFormatTUSummaryTest, DataEntryMissingSummaryName) {
"name": "test.cpp"
},
"id_table": [],
+ "linkage_table": [],
"data": [
{
"summary_data": []
@@ -1332,6 +1698,7 @@ TEST_F(JSONFormatTUSummaryTest, DataEntryMissingData) {
"name": "test.cpp"
},
"id_table": [],
+ "linkage_table": [],
"data": [
{
"summary_name": "PairsEntitySummaryForJSONFormatTest"
@@ -1361,6 +1728,7 @@ TEST_F(JSONFormatTUSummaryTest, DataNotArray) {
"name": "test.cpp"
},
"id_table": [],
+ "linkage_table": [],
"data": {}
})");
@@ -1379,6 +1747,7 @@ TEST_F(JSONFormatTUSummaryTest, DataElementNotObject) {
"name": "test.cpp"
},
"id_table": [],
+ "linkage_table": [],
"data": ["invalid"]
})");
@@ -1397,6 +1766,7 @@ TEST_F(JSONFormatTUSummaryTest, DuplicateSummaryName) {
"name": "test.cpp"
},
"id_table": [],
+ "linkage_table": [],
"data": [
{
"summary_name": "PairsEntitySummaryForJSONFormatTest",
@@ -1425,6 +1795,7 @@ TEST_F(JSONFormatTUSummaryTest, DuplicateSummaryName) {
TEST_F(JSONFormatTUSummaryTest, MissingTUNamespace) {
auto Result = readTUSummaryFromString(R"({
"id_table": [],
+ "linkage_table": [],
"data": []
})");
@@ -1452,13 +1823,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 +1953,7 @@ TEST_F(JSONFormatTUSummaryTest, Empty) {
"name": "test.cpp"
},
"id_table": [],
+ "linkage_table": [],
"data": []
})");
}
@@ -1574,6 +1965,7 @@ TEST_F(JSONFormatTUSummaryTest, LinkUnit) {
"name": "libtest.so"
},
"id_table": [],
+ "linkage_table": [],
"data": []
})");
}
@@ -1616,6 +2008,16 @@ TEST_F(JSONFormatTUSummaryTest, WithIDTable) {
}
}
],
+ "linkage_table": [
+ {
+ "id": 0,
+ "linkage": { "type": "none" }
+ },
+ {
+ "id": 1,
+ "linkage": { "type": "internal" }
+ }
+ ],
"data": []
})");
}
@@ -1627,6 +2029,7 @@ TEST_F(JSONFormatTUSummaryTest, WithEmptyDataEntry) {
"name": "test.cpp"
},
"id_table": [],
+ "linkage_table": [],
"data": [
{
"summary_name": "PairsEntitySummaryForJSONFormatTest",
@@ -1657,6 +2060,12 @@ TEST_F(JSONFormatTUSummaryTest, RoundTripWithIDTable) {
}
}
],
+ "linkage_table": [
+ {
+ "id": 0,
+ "linkage": { "type": "none" }
+ }
+ ],
"data": []
})");
}
@@ -1708,6 +2117,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 +2156,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 at 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 at 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 at 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 at foo",
+ "suffix": "",
+ "namespace": [
+ {
+ "kind": "compilation_unit",
+ "name": "test.cpp"
+ }
+ ]
+ }
+ },
+ {
+ "id": 1,
+ "name": {
+ "usr": "c:@F at bar",
+ "suffix": "",
+ "namespace": [
+ {
+ "kind": "compilation_unit",
+ "name": "test.cpp"
+ }
+ ]
+ }
+ },
+ {
+ "id": 2,
+ "name": {
+ "usr": "c:@F at 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
More information about the cfe-commits
mailing list