https://github.com/aviralg created 
https://github.com/llvm/llvm-project/pull/182961

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.

>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] 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

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

Reply via email to