https://github.com/steakhal created 
https://github.com/llvm/llvm-project/pull/180779

This PR should be reviewed commit-by-commit.

My intention is to merge the first 3 commits as-is, and also get some feedback 
on the TUSummaryBuilder class and the unit test for it. That builder will 
change to enable better error handling but the APIs shouldn't change much to 
void the work already invested into the testing.

From 2232f02903690b00a821d378fe6093f93c9644a2 Mon Sep 17 00:00:00 2001
From: Balazs Benics <[email protected]>
Date: Tue, 10 Feb 2026 15:13:51 +0100
Subject: [PATCH 1/5] [clang][ssaf][NFC] Refactor SerializationFormat to use
 macro-based field accessors

This reduces code duplication and makes it easier to add new field accessors.

Assisted-By: claude
---
 .../Scalable/Model/PrivateFieldNames.def      | 30 +++++++++
 .../Serialization/SerializationFormat.h       | 32 +++-------
 .../Serialization/SerializationFormat.cpp     | 64 +------------------
 .../Registries/MockSerializationFormat.cpp    |  2 +-
 4 files changed, 44 insertions(+), 84 deletions(-)
 create mode 100644 
clang/include/clang/Analysis/Scalable/Model/PrivateFieldNames.def

diff --git a/clang/include/clang/Analysis/Scalable/Model/PrivateFieldNames.def 
b/clang/include/clang/Analysis/Scalable/Model/PrivateFieldNames.def
new file mode 100644
index 0000000000000..59064659996b4
--- /dev/null
+++ b/clang/include/clang/Analysis/Scalable/Model/PrivateFieldNames.def
@@ -0,0 +1,30 @@
+//===-- PrivateFieldNames.def -----------------------------------*- C++ 
-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file defines a list of non-static data members of the SSAF objects.
+//  These are used for granting access for:
+//   - SerializationFormat
+//   - TextFixture
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FIELD
+#define FIELD(CLASS, FIELD_NAME)
+#endif
+
+FIELD(BuildNamespace, Kind)
+FIELD(BuildNamespace, Name)
+FIELD(EntityName, Namespace)
+FIELD(EntityName, Suffix)
+FIELD(EntityName, USR)
+FIELD(NestedBuildNamespace, Namespaces)
+FIELD(TUSummary, Data)
+FIELD(TUSummary, IdTable)
+FIELD(TUSummary, TUNamespace)
+
+#undef FIELD
diff --git 
a/clang/include/clang/Analysis/Scalable/Serialization/SerializationFormat.h 
b/clang/include/clang/Analysis/Scalable/Serialization/SerializationFormat.h
index c7438e2859da0..2c0ca57f6db46 100644
--- a/clang/include/clang/Analysis/Scalable/Serialization/SerializationFormat.h
+++ b/clang/include/clang/Analysis/Scalable/Serialization/SerializationFormat.h
@@ -17,11 +17,9 @@
 #include "clang/Analysis/Scalable/Model/BuildNamespace.h"
 #include "clang/Analysis/Scalable/Model/SummaryName.h"
 #include "clang/Analysis/Scalable/TUSummary/TUSummary.h"
-#include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/ExtensibleRTTI.h"
 #include "llvm/Support/VirtualFileSystem.h"
-#include <vector>
 
 namespace clang::ssaf {
 
@@ -33,26 +31,6 @@ class EntitySummary;
 /// Abstract base class for serialization formats.
 class SerializationFormat
     : public llvm::RTTIExtends<SerializationFormat, llvm::RTTIRoot> {
-protected:
-  // Helpers providing access to implementation details of basic data 
structures
-  // for efficient serialization/deserialization.
-  static EntityIdTable &getIdTableForDeserialization(TUSummary &S);
-  static BuildNamespace &getTUNamespaceForDeserialization(TUSummary &S);
-  static const EntityIdTable &getIdTable(const TUSummary &S);
-  static const BuildNamespace &getTUNamespace(const TUSummary &S);
-
-  static BuildNamespaceKind getBuildNamespaceKind(const BuildNamespace &BN);
-  static llvm::StringRef getBuildNamespaceName(const BuildNamespace &BN);
-  static const std::vector<BuildNamespace> &
-  getNestedBuildNamespaces(const NestedBuildNamespace &NBN);
-
-  static llvm::StringRef getEntityNameUSR(const EntityName &EN);
-  static const llvm::SmallString<16> &getEntityNameSuffix(const EntityName 
&EN);
-  static const NestedBuildNamespace &
-  getEntityNameNamespace(const EntityName &EN);
-  static decltype(TUSummary::Data) &getData(TUSummary &S);
-  static const decltype(TUSummary::Data) &getData(const TUSummary &S);
-
 public:
   explicit SerializationFormat(
       llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS);
@@ -66,7 +44,17 @@ class SerializationFormat
   static char ID; // For RTTIExtends.
 
 protected:
+  // Helpers providing access to implementation details of basic data 
structures
+  // for efficient serialization/deserialization.
+#define FIELD(CLASS, FIELD_NAME)                                               
\
+  static const auto &get##FIELD_NAME(const CLASS &X) { return X.FIELD_NAME; }  
\
+  static auto &get##FIELD_NAME(CLASS &X) { return X.FIELD_NAME; }
+#include "clang/Analysis/Scalable/Model/PrivateFieldNames.def"
+
   llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS;
+
+private:
+  void anchor() override;
 };
 
 template <class SerializerFn, class DeserializerFn> struct FormatInfoEntry {
diff --git a/clang/lib/Analysis/Scalable/Serialization/SerializationFormat.cpp 
b/clang/lib/Analysis/Scalable/Serialization/SerializationFormat.cpp
index 1d62ea837616f..be2740d7a3e4c 100644
--- a/clang/lib/Analysis/Scalable/Serialization/SerializationFormat.cpp
+++ b/clang/lib/Analysis/Scalable/Serialization/SerializationFormat.cpp
@@ -7,70 +7,12 @@
 
//===----------------------------------------------------------------------===//
 
 #include "clang/Analysis/Scalable/Serialization/SerializationFormat.h"
-#include "clang/Analysis/Scalable/Model/BuildNamespace.h"
-#include "clang/Analysis/Scalable/Model/EntityId.h"
-#include "clang/Analysis/Scalable/Model/EntityName.h"
-#include "clang/Analysis/Scalable/TUSummary/TUSummary.h"
 
 using namespace clang::ssaf;
 
+char SerializationFormat::ID = 0;
+void SerializationFormat::anchor() {}
+
 SerializationFormat::SerializationFormat(
     llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
     : FS(FS) {}
-
-EntityIdTable &SerializationFormat::getIdTableForDeserialization(TUSummary &S) 
{
-  return S.IdTable;
-}
-
-BuildNamespace &
-SerializationFormat::getTUNamespaceForDeserialization(TUSummary &S) {
-  return S.TUNamespace;
-}
-
-const EntityIdTable &SerializationFormat::getIdTable(const TUSummary &S) {
-  return S.IdTable;
-}
-
-const BuildNamespace &SerializationFormat::getTUNamespace(const TUSummary &S) {
-  return S.TUNamespace;
-}
-
-BuildNamespaceKind
-SerializationFormat::getBuildNamespaceKind(const BuildNamespace &BN) {
-  return BN.Kind;
-}
-
-llvm::StringRef
-SerializationFormat::getBuildNamespaceName(const BuildNamespace &BN) {
-  return BN.Name;
-}
-
-const std::vector<BuildNamespace> &
-SerializationFormat::getNestedBuildNamespaces(const NestedBuildNamespace &NBN) 
{
-  return NBN.Namespaces;
-}
-
-llvm::StringRef SerializationFormat::getEntityNameUSR(const EntityName &EN) {
-  return EN.USR;
-}
-
-const llvm::SmallString<16> &
-SerializationFormat::getEntityNameSuffix(const EntityName &EN) {
-  return EN.Suffix;
-}
-
-const NestedBuildNamespace &
-SerializationFormat::getEntityNameNamespace(const EntityName &EN) {
-  return EN.Namespace;
-}
-
-const decltype(TUSummary::Data) &
-SerializationFormat::getData(const TUSummary &S) {
-  return S.Data;
-}
-
-decltype(TUSummary::Data) &SerializationFormat::getData(TUSummary &S) {
-  return S.Data;
-}
-
-char SerializationFormat::ID = 0;
diff --git 
a/clang/unittests/Analysis/Scalable/Registries/MockSerializationFormat.cpp 
b/clang/unittests/Analysis/Scalable/Registries/MockSerializationFormat.cpp
index 03328e8bd9742..127e61b26c0cb 100644
--- a/clang/unittests/Analysis/Scalable/Registries/MockSerializationFormat.cpp
+++ b/clang/unittests/Analysis/Scalable/Registries/MockSerializationFormat.cpp
@@ -67,7 +67,7 @@ TUSummary 
MockSerializationFormat::readTUSummary(llvm::StringRef Path) {
     assert(InfoEntry.ForSummary == Name);
 
     SpecialFileRepresentation Repr{(*InputFile)->getBuffer().str()};
-    auto &Table = getIdTableForDeserialization(Summary);
+    auto &Table = getIdTable(Summary);
 
     std::unique_ptr<EntitySummary> Result = InfoEntry.Deserialize(Repr, Table);
     if (!Result) // TODO: Handle error.

From 0ac78a0f4fd596e5cef66a24f8b01e9a19b23ec7 Mon Sep 17 00:00:00 2001
From: Balazs Benics <[email protected]>
Date: Tue, 10 Feb 2026 17:04:05 +0100
Subject: [PATCH 2/5] [clang][ssaf] Add EntityLinkage stub

---
 .../Analysis/Scalable/Model/EntityLinkage.h   | 27 +++++++++++++++++++
 1 file changed, 27 insertions(+)
 create mode 100644 clang/include/clang/Analysis/Scalable/Model/EntityLinkage.h

diff --git a/clang/include/clang/Analysis/Scalable/Model/EntityLinkage.h 
b/clang/include/clang/Analysis/Scalable/Model/EntityLinkage.h
new file mode 100644
index 0000000000000..a89ab200fbd79
--- /dev/null
+++ b/clang/include/clang/Analysis/Scalable/Model/EntityLinkage.h
@@ -0,0 +1,27 @@
+//===- EntityLinkage.h ------------------------------------------*- C++ 
-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_MODEL_ENTITY_LINKAGE_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_MODEL_ENTITY_LINKAGE_H
+
+namespace clang::ssaf {
+
+class EntityLinkage {
+public:
+  // Empty for now.
+  bool operator==(const EntityLinkage &Other) const {
+    return true; // FIXME: Adjust this once no longer empty.
+  }
+  bool operator!=(const EntityLinkage &Other) const {
+    return !(*this == Other);
+  }
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_MODEL_ENTITY_LINKAGE_H

From cdc2dbc1942e2228679874e506e618a53c724ef1 Mon Sep 17 00:00:00 2001
From: Balazs Benics <[email protected]>
Date: Tue, 10 Feb 2026 17:04:59 +0100
Subject: [PATCH 3/5] [clang][ssaf] Add operator<< for debugging to SummaryName
 and EntityId

---
 .../clang/Analysis/Scalable/Model/EntityId.h   |  6 ++++++
 .../Analysis/Scalable/Model/SummaryName.h      |  7 +++++++
 clang/lib/Analysis/Scalable/CMakeLists.txt     |  2 ++
 clang/lib/Analysis/Scalable/Model/EntityId.cpp | 17 +++++++++++++++++
 .../Analysis/Scalable/Model/SummaryName.cpp    | 18 ++++++++++++++++++
 5 files changed, 50 insertions(+)
 create mode 100644 clang/lib/Analysis/Scalable/Model/EntityId.cpp
 create mode 100644 clang/lib/Analysis/Scalable/Model/SummaryName.cpp

diff --git a/clang/include/clang/Analysis/Scalable/Model/EntityId.h 
b/clang/include/clang/Analysis/Scalable/Model/EntityId.h
index 6fa059445d853..6e1b7ce276bda 100644
--- a/clang/include/clang/Analysis/Scalable/Model/EntityId.h
+++ b/clang/include/clang/Analysis/Scalable/Model/EntityId.h
@@ -17,6 +17,10 @@
 
 #include <cstddef>
 
+namespace llvm {
+class raw_ostream;
+} // namespace llvm
+
 namespace clang::ssaf {
 
 class EntityIdTable;
@@ -40,6 +44,8 @@ class EntityId {
   bool operator==(const EntityId &Other) const { return Index == Other.Index; }
   bool operator<(const EntityId &Other) const { return Index < Other.Index; }
   bool operator!=(const EntityId &Other) const { return !(*this == Other); }
+
+  friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const EntityId &);
 };
 
 } // namespace clang::ssaf
diff --git a/clang/include/clang/Analysis/Scalable/Model/SummaryName.h 
b/clang/include/clang/Analysis/Scalable/Model/SummaryName.h
index 785fe0eb10372..14929592b266b 100644
--- a/clang/include/clang/Analysis/Scalable/Model/SummaryName.h
+++ b/clang/include/clang/Analysis/Scalable/Model/SummaryName.h
@@ -12,6 +12,10 @@
 #include "llvm/ADT/StringRef.h"
 #include <string>
 
+namespace llvm {
+class raw_ostream;
+} // namespace llvm
+
 namespace clang::ssaf {
 
 /// Uniquely identifies an analysis summary.
@@ -29,6 +33,9 @@ class SummaryName {
   /// Explicit conversion to the underlying string representation.
   llvm::StringRef str() const { return Name; }
 
+  friend llvm::raw_ostream &operator<<(llvm::raw_ostream &,
+                                       const SummaryName &);
+
 private:
   std::string Name;
 };
diff --git a/clang/lib/Analysis/Scalable/CMakeLists.txt 
b/clang/lib/Analysis/Scalable/CMakeLists.txt
index 47fe87074d728..227aeded475c1 100644
--- a/clang/lib/Analysis/Scalable/CMakeLists.txt
+++ b/clang/lib/Analysis/Scalable/CMakeLists.txt
@@ -5,8 +5,10 @@ set(LLVM_LINK_COMPONENTS
 add_clang_library(clangAnalysisScalable
   ASTEntityMapping.cpp
   Model/BuildNamespace.cpp
+  Model/EntityId.cpp
   Model/EntityIdTable.cpp
   Model/EntityName.cpp
+  Model/SummaryName.cpp
   Serialization/SerializationFormat.cpp
   Serialization/SerializationFormatRegistry.cpp
   TUSummary/EntitySummary.cpp
diff --git a/clang/lib/Analysis/Scalable/Model/EntityId.cpp 
b/clang/lib/Analysis/Scalable/Model/EntityId.cpp
new file mode 100644
index 0000000000000..2aa0ee312ca6b
--- /dev/null
+++ b/clang/lib/Analysis/Scalable/Model/EntityId.cpp
@@ -0,0 +1,17 @@
+//===- EntityId.cpp 
-------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/Scalable/Model/EntityId.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace ssaf;
+
+llvm::raw_ostream &ssaf::operator<<(llvm::raw_ostream &OS, const EntityId &ID) 
{
+  return OS << "EntityId(" << ID.Index << ")";
+}
diff --git a/clang/lib/Analysis/Scalable/Model/SummaryName.cpp 
b/clang/lib/Analysis/Scalable/Model/SummaryName.cpp
new file mode 100644
index 0000000000000..663f8900dbec6
--- /dev/null
+++ b/clang/lib/Analysis/Scalable/Model/SummaryName.cpp
@@ -0,0 +1,18 @@
+//===- SummaryName.cpp 
----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/Scalable/Model/SummaryName.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace ssaf;
+
+llvm::raw_ostream &ssaf::operator<<(llvm::raw_ostream &OS,
+                                    const SummaryName &Name) {
+  return OS << "SummaryName(\"" << Name.str() << "\")";
+}

From fc76c4a4a7934d1ff4427c6b991d94a200fa15f1 Mon Sep 17 00:00:00 2001
From: Balazs Benics <[email protected]>
Date: Tue, 10 Feb 2026 15:16:47 +0100
Subject: [PATCH 4/5] [clang][ssaf] Implement TUSummaryBuilder with addEntity
 and addFact methods

Add core functionality to TUSummaryBuilder for building TUSummary
objects. The addEntity method registers entities with their linkage
information, and addFact attaches entity summaries to entities.
This enables constructing TUSummary objects incrementally during
analysis.

Assisted-By: claude
---
 .../Analysis/Scalable/Model/BuildNamespace.h  |  7 +++--
 .../Analysis/Scalable/Model/EntityName.h      |  2 +-
 .../Scalable/Model/PrivateFieldNames.def      |  1 +
 .../Analysis/Scalable/TUSummary/TUSummary.h   |  4 +++
 .../Scalable/TUSummary/TUSummaryBuilder.h     | 23 +++++++++++++++-
 clang/lib/Analysis/Scalable/CMakeLists.txt    |  1 +
 .../Scalable/TUSummary/TUSummaryBuilder.cpp   | 26 +++++++++++++++++++
 .../Registries/MockTUSummaryBuilder.h         |  1 +
 .../SummaryExtractorRegistryTest.cpp          | 16 +++++++++---
 9 files changed, 72 insertions(+), 9 deletions(-)
 create mode 100644 clang/lib/Analysis/Scalable/TUSummary/TUSummaryBuilder.cpp

diff --git a/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h 
b/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h
index 5ca26df1e9252..2cd8990708b8d 100644
--- a/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h
+++ b/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h
@@ -63,6 +63,7 @@ class BuildNamespace {
   bool operator<(const BuildNamespace &Other) const;
 
   friend class SerializationFormat;
+  friend class TestFixture;
 };
 
 /// Represents a hierarchical sequence of build namespaces.
@@ -75,8 +76,6 @@ class BuildNamespace {
 /// For example, an entity might be qualified by a compilation unit namespace
 /// followed by a shared library namespace.
 class NestedBuildNamespace {
-  friend class SerializationFormat;
-
   std::vector<BuildNamespace> Namespaces;
 
 public:
@@ -114,8 +113,8 @@ class NestedBuildNamespace {
   bool operator!=(const NestedBuildNamespace &Other) const;
   bool operator<(const NestedBuildNamespace &Other) const;
 
-  friend class JSONWriter;
-  friend class LinkUnitResolution;
+  friend class SerializationFormat;
+  friend class TestFixture;
 };
 
 } // namespace clang::ssaf
diff --git a/clang/include/clang/Analysis/Scalable/Model/EntityName.h 
b/clang/include/clang/Analysis/Scalable/Model/EntityName.h
index 23890ab7bea43..72dd9ac803052 100644
--- a/clang/include/clang/Analysis/Scalable/Model/EntityName.h
+++ b/clang/include/clang/Analysis/Scalable/Model/EntityName.h
@@ -47,8 +47,8 @@ class EntityName {
   /// \param Namespace The namespace steps to append to this entity's 
namespace.
   EntityName makeQualified(NestedBuildNamespace Namespace) const;
 
-  friend class LinkUnitResolution;
   friend class SerializationFormat;
+  friend class TestFixture;
 };
 
 } // namespace clang::ssaf
diff --git a/clang/include/clang/Analysis/Scalable/Model/PrivateFieldNames.def 
b/clang/include/clang/Analysis/Scalable/Model/PrivateFieldNames.def
index 59064659996b4..a91ff5952b807 100644
--- a/clang/include/clang/Analysis/Scalable/Model/PrivateFieldNames.def
+++ b/clang/include/clang/Analysis/Scalable/Model/PrivateFieldNames.def
@@ -24,6 +24,7 @@ FIELD(EntityName, Suffix)
 FIELD(EntityName, USR)
 FIELD(NestedBuildNamespace, Namespaces)
 FIELD(TUSummary, Data)
+FIELD(TUSummary, Entities)
 FIELD(TUSummary, IdTable)
 FIELD(TUSummary, TUNamespace)
 
diff --git a/clang/include/clang/Analysis/Scalable/TUSummary/TUSummary.h 
b/clang/include/clang/Analysis/Scalable/TUSummary/TUSummary.h
index 4af1c70e1a488..9ed35a2c3828f 100644
--- a/clang/include/clang/Analysis/Scalable/TUSummary/TUSummary.h
+++ b/clang/include/clang/Analysis/Scalable/TUSummary/TUSummary.h
@@ -12,6 +12,7 @@
 #include "clang/Analysis/Scalable/Model/BuildNamespace.h"
 #include "clang/Analysis/Scalable/Model/EntityId.h"
 #include "clang/Analysis/Scalable/Model/EntityIdTable.h"
+#include "clang/Analysis/Scalable/Model/EntityLinkage.h"
 #include "clang/Analysis/Scalable/Model/SummaryName.h"
 #include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
 #include <map>
@@ -24,6 +25,7 @@ class TUSummary {
   /// Identifies the translation unit.
   BuildNamespace TUNamespace;
   EntityIdTable IdTable;
+  std::map<EntityId, EntityLinkage> Entities;
 
   std::map<SummaryName, std::map<EntityId, std::unique_ptr<EntitySummary>>>
       Data;
@@ -32,6 +34,8 @@ class TUSummary {
   TUSummary(BuildNamespace TUNamespace) : TUNamespace(std::move(TUNamespace)) 
{}
 
   friend class SerializationFormat;
+  friend class TestFixture;
+  friend class TUSummaryBuilder;
 };
 
 } // namespace clang::ssaf
diff --git a/clang/include/clang/Analysis/Scalable/TUSummary/TUSummaryBuilder.h 
b/clang/include/clang/Analysis/Scalable/TUSummary/TUSummaryBuilder.h
index fa679c145faa5..55dddb2d25656 100644
--- a/clang/include/clang/Analysis/Scalable/TUSummary/TUSummaryBuilder.h
+++ b/clang/include/clang/Analysis/Scalable/TUSummary/TUSummaryBuilder.h
@@ -9,10 +9,31 @@
 #ifndef LLVM_CLANG_ANALYSIS_SCALABLE_TUSUMMARY_TUSUMMARYBUILDER_H
 #define LLVM_CLANG_ANALYSIS_SCALABLE_TUSUMMARY_TUSUMMARYBUILDER_H
 
+#include <memory>
+
 namespace clang::ssaf {
 
+class EntityId;
+class EntityLinkage;
+class EntityName;
+class EntitySummary;
+class TUSummary;
+
 class TUSummaryBuilder {
-  // Empty for now.
+public:
+  explicit TUSummaryBuilder(TUSummary &Summary) : Summary(Summary) {}
+
+  /// Add an entity to the summary and return its EntityId.
+  /// If the entity already exists, returns the existing ID (idempotent).
+  EntityId addEntity(const EntityName &E, const EntityLinkage &Linkage);
+
+  /// Add analysis-specific fact data for an entity.
+  /// Precondition: The ContributingEntity must have been added via 
addEntity().
+  void addFact(EntityId ContributingEntity,
+               std::unique_ptr<EntitySummary> NewData);
+
+private:
+  TUSummary &Summary;
 };
 
 } // namespace clang::ssaf
diff --git a/clang/lib/Analysis/Scalable/CMakeLists.txt 
b/clang/lib/Analysis/Scalable/CMakeLists.txt
index 227aeded475c1..03bed630a78eb 100644
--- a/clang/lib/Analysis/Scalable/CMakeLists.txt
+++ b/clang/lib/Analysis/Scalable/CMakeLists.txt
@@ -13,6 +13,7 @@ add_clang_library(clangAnalysisScalable
   Serialization/SerializationFormatRegistry.cpp
   TUSummary/EntitySummary.cpp
   TUSummary/ExtractorRegistry.cpp
+  TUSummary/TUSummaryBuilder.cpp
 
   LINK_LIBS
   clangAST
diff --git a/clang/lib/Analysis/Scalable/TUSummary/TUSummaryBuilder.cpp 
b/clang/lib/Analysis/Scalable/TUSummary/TUSummaryBuilder.cpp
new file mode 100644
index 0000000000000..7dfae5e1bd0e2
--- /dev/null
+++ b/clang/lib/Analysis/Scalable/TUSummary/TUSummaryBuilder.cpp
@@ -0,0 +1,26 @@
+#include "clang/Analysis/Scalable/TUSummary/TUSummaryBuilder.h"
+#include "clang/Analysis/Scalable/Model/EntityId.h"
+#include "clang/Analysis/Scalable/Model/EntityLinkage.h"
+#include "clang/Analysis/Scalable/TUSummary/TUSummary.h"
+#include <memory>
+
+using namespace clang;
+using namespace ssaf;
+
+EntityId TUSummaryBuilder::addEntity(const EntityName &E,
+                                     const EntityLinkage &Linkage) {
+  EntityId Id = Summary.IdTable.getId(E);
+  const EntityLinkage &ExistingLinkage =
+      Summary.Entities.try_emplace(Id, Linkage).first->second;
+  if (ExistingLinkage != Linkage) {
+    // print ExistingLinkage, Linkage, and ID;
+    llvm::report_fatal_error("Entity already exists: ");
+  }
+  return Id;
+}
+
+void TUSummaryBuilder::addFact(EntityId ContributingEntity,
+                               std::unique_ptr<EntitySummary> NewData) {
+  Summary.Data[NewData->getSummaryName()][ContributingEntity] =
+      std::move(NewData);
+}
\ No newline at end of file
diff --git 
a/clang/unittests/Analysis/Scalable/Registries/MockTUSummaryBuilder.h 
b/clang/unittests/Analysis/Scalable/Registries/MockTUSummaryBuilder.h
index ccb79ae042625..755a47471a9f8 100644
--- a/clang/unittests/Analysis/Scalable/Registries/MockTUSummaryBuilder.h
+++ b/clang/unittests/Analysis/Scalable/Registries/MockTUSummaryBuilder.h
@@ -14,6 +14,7 @@ namespace clang::ssaf {
 
 class MockTUSummaryBuilder : public TUSummaryBuilder {
 public:
+  using TUSummaryBuilder::TUSummaryBuilder;
   void sendMessage(llvm::Twine Message) { Stream << Message << '\n'; }
   std::string consumeMessages() { return std::move(OutputBuffer); }
 
diff --git 
a/clang/unittests/Analysis/Scalable/Registries/SummaryExtractorRegistryTest.cpp 
b/clang/unittests/Analysis/Scalable/Registries/SummaryExtractorRegistryTest.cpp
index 2076fae0b5ab0..70c84363f389e 100644
--- 
a/clang/unittests/Analysis/Scalable/Registries/SummaryExtractorRegistryTest.cpp
+++ 
b/clang/unittests/Analysis/Scalable/Registries/SummaryExtractorRegistryTest.cpp
@@ -8,6 +8,7 @@
 
 #include "MockTUSummaryBuilder.h"
 #include "clang/Analysis/Scalable/TUSummary/ExtractorRegistry.h"
+#include "clang/Analysis/Scalable/TUSummary/TUSummary.h"
 #include "clang/Frontend/MultiplexConsumer.h"
 #include "clang/Tooling/Tooling.h"
 #include "llvm/ADT/StringRef.h"
@@ -17,6 +18,12 @@
 using namespace clang;
 using namespace ssaf;
 
+static TUSummary makeFakeSummary() {
+  BuildNamespace NS(BuildNamespaceKind::CompilationUnit, "Mock.cpp");
+  TUSummary Summary(NS);
+  return Summary;
+}
+
 namespace {
 
 TEST(SummaryExtractorRegistryTest, isTUSummaryExtractorRegistered) {
@@ -39,7 +46,8 @@ TEST(SummaryExtractorRegistryTest, 
EnumeratingRegistryEntries) {
 }
 
 TEST(SummaryExtractorRegistryTest, InstantiatingExtractor1) {
-  MockTUSummaryBuilder FakeBuilder;
+  TUSummary Summary = makeFakeSummary();
+  MockTUSummaryBuilder FakeBuilder(Summary);
   {
     auto Consumer =
         makeTUSummaryExtractor("MockSummaryExtractor1", FakeBuilder);
@@ -52,7 +60,8 @@ TEST(SummaryExtractorRegistryTest, InstantiatingExtractor1) {
 }
 
 TEST(SummaryExtractorRegistryTest, InstantiatingExtractor2) {
-  MockTUSummaryBuilder FakeBuilder;
+  TUSummary Summary = makeFakeSummary();
+  MockTUSummaryBuilder FakeBuilder(Summary);
   {
     auto Consumer =
         makeTUSummaryExtractor("MockSummaryExtractor2", FakeBuilder);
@@ -65,7 +74,8 @@ TEST(SummaryExtractorRegistryTest, InstantiatingExtractor2) {
 }
 
 TEST(SummaryExtractorRegistryTest, InvokingExtractors) {
-  MockTUSummaryBuilder FakeBuilder;
+  TUSummary Summary = makeFakeSummary();
+  MockTUSummaryBuilder FakeBuilder(Summary);
   std::vector<std::unique_ptr<ASTConsumer>> Consumers;
   for (std::string Name : {"MockSummaryExtractor1", "MockSummaryExtractor2"}) {
     auto Consumer = makeTUSummaryExtractor(Name, FakeBuilder);

From 78b33671ee16bd7bc6731a38f3552dfae3d7a497 Mon Sep 17 00:00:00 2001
From: Balazs Benics <[email protected]>
Date: Tue, 10 Feb 2026 15:17:26 +0100
Subject: [PATCH 5/5] [clang][ssaf] Add tests for TUSummaryBuilder

Add comprehensive unit tests for TUSummaryBuilder functionality,
covering entity registration, fact addition, and various edge cases.
Introduce TestFixture helper for common test setup and update related
test files to use the new TUSummaryBuilder interface.

Assisted-By: claude
---
 .../Analysis/Scalable/CMakeLists.txt          |   2 +
 .../Scalable/TUSummaryBuilderTest.cpp         | 319 ++++++++++++++++++
 .../Analysis/Scalable/TestFixture.cpp         |  29 ++
 .../unittests/Analysis/Scalable/TestFixture.h |  34 ++
 4 files changed, 384 insertions(+)
 create mode 100644 clang/unittests/Analysis/Scalable/TUSummaryBuilderTest.cpp
 create mode 100644 clang/unittests/Analysis/Scalable/TestFixture.cpp
 create mode 100644 clang/unittests/Analysis/Scalable/TestFixture.h

diff --git a/clang/unittests/Analysis/Scalable/CMakeLists.txt 
b/clang/unittests/Analysis/Scalable/CMakeLists.txt
index 601845b4ab77a..ca3f67a005707 100644
--- a/clang/unittests/Analysis/Scalable/CMakeLists.txt
+++ b/clang/unittests/Analysis/Scalable/CMakeLists.txt
@@ -11,6 +11,8 @@ add_distinct_clang_unittest(ClangScalableAnalysisTests
   Registries/SerializationFormatRegistryTest.cpp
   Registries/SummaryExtractorRegistryTest.cpp
   SummaryNameTest.cpp
+  TestFixture.cpp
+  TUSummaryBuilderTest.cpp
 
   CLANG_LIBS
   clangAnalysisScalable
diff --git a/clang/unittests/Analysis/Scalable/TUSummaryBuilderTest.cpp 
b/clang/unittests/Analysis/Scalable/TUSummaryBuilderTest.cpp
new file mode 100644
index 0000000000000..2b610b17cdbe0
--- /dev/null
+++ b/clang/unittests/Analysis/Scalable/TUSummaryBuilderTest.cpp
@@ -0,0 +1,319 @@
+//===- unittests/Analysis/Scalable/TUSummaryBuilderTest.cpp 
---------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/Scalable/TUSummary/TUSummaryBuilder.h"
+#include "TestFixture.h"
+#include "clang/Analysis/Scalable/Model/BuildNamespace.h"
+#include "clang/Analysis/Scalable/Model/EntityId.h"
+#include "clang/Analysis/Scalable/Model/EntityName.h"
+#include "clang/Analysis/Scalable/Model/SummaryName.h"
+#include "clang/Analysis/Scalable/Serialization/SerializationFormat.h"
+#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
+#include "clang/Analysis/Scalable/TUSummary/TUSummary.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <type_traits>
+
+using namespace clang;
+using namespace ssaf;
+
+using llvm::SmallVector;
+using testing::Field;
+using testing::Optional;
+using testing::UnorderedElementsAre;
+
+[[nodiscard]]
+static TUSummary makeFakeSummary() {
+  BuildNamespace NS(BuildNamespaceKind::CompilationUnit, "Mock.cpp");
+  TUSummary Summary(NS);
+  return Summary;
+}
+
+[[nodiscard]]
+static EntityId addTestEntity(TUSummaryBuilder &Builder, llvm::StringRef USR) {
+  EntityName EN(USR, "", /*Namespace=*/{});
+  EntityLinkage MockLinkage;
+  return Builder.addEntity(EN, MockLinkage);
+}
+
+template <class ConcreteEntitySummary>
+[[nodiscard]]
+static SummaryName addFactTo(TUSummaryBuilder &Builder, EntityId ID,
+                             ConcreteEntitySummary Fact) {
+  static_assert(std::is_base_of_v<EntitySummary, ConcreteEntitySummary>);
+  auto NewFact = std::make_unique<ConcreteEntitySummary>(std::move(Fact));
+  SummaryName Name = NewFact->getSummaryName();
+  Builder.addFact(ID, std::move(NewFact));
+  return Name;
+}
+
+namespace {
+
+// Mock EntitySummary classes for testing
+struct MockSummaryData1 final
+    : public llvm::RTTIExtends<MockSummaryData1, EntitySummary> {
+  explicit MockSummaryData1(int Value) : Value(Value) {}
+  SummaryName getSummaryName() const override {
+    return SummaryName("MockSummary1");
+  }
+  int Value = 0;
+  static char ID;
+};
+
+struct MockSummaryData2 final
+    : public llvm::RTTIExtends<MockSummaryData2, EntitySummary> {
+  explicit MockSummaryData2(std::string Text) : Text(std::move(Text)) {}
+  SummaryName getSummaryName() const override {
+    return SummaryName("MockSummary2");
+  }
+  std::string Text;
+  static char ID;
+};
+
+struct MockSummaryData3 final
+    : public llvm::RTTIExtends<MockSummaryData3, EntitySummary> {
+  explicit MockSummaryData3(bool Flag) : Flag(Flag) {}
+  SummaryName getSummaryName() const override {
+    return SummaryName("MockSummary3");
+  }
+  bool Flag = false;
+  static char ID;
+};
+
+char MockSummaryData1::ID = 0;
+char MockSummaryData2::ID = 0;
+char MockSummaryData3::ID = 0;
+
+void PrintTo(const MockSummaryData1 &S, std::ostream *OS) {
+  *OS << "MockSummaryData1(" << S.getSummaryName().str().str() << ")";
+}
+void PrintTo(const MockSummaryData2 &S, std::ostream *OS) {
+  *OS << "MockSummaryData2(" << S.getSummaryName().str().str() << ")";
+}
+void PrintTo(const MockSummaryData3 &S, std::ostream *OS) {
+  *OS << "MockSummaryData3(" << S.getSummaryName().str().str() << ")";
+}
+
+struct TUSummaryBuilderTest : ssaf::TestFixture {
+  static llvm::SmallVector<SummaryName> summaryNames(const TUSummary &Summary) 
{
+    return llvm::to_vector(llvm::make_first_range(getData(Summary)));
+  }
+
+  static llvm::SmallVector<EntityId>
+  entitiesOfSummary(const TUSummary &Summary, const SummaryName &Name) {
+    const auto &MappingIt = getData(Summary).find(Name);
+    if (MappingIt == getData(Summary).end())
+      return {};
+    return llvm::to_vector(llvm::make_first_range(MappingIt->second));
+  }
+
+  template <class ConcreteSummaryData>
+  static std::optional<ConcreteSummaryData>
+  getAsEntitySummary(const TUSummary &Summary, const SummaryName &Name,
+                     EntityId E) {
+    static_assert(std::is_base_of_v<EntitySummary, ConcreteSummaryData>);
+    const auto &MappingIt = getData(Summary).find(Name);
+    if (MappingIt == getData(Summary).end())
+      return std::nullopt;
+    auto SummaryIt = MappingIt->second.find(E);
+    if (SummaryIt == MappingIt->second.end())
+      return std::nullopt;
+    assert(llvm::isa<ConcreteSummaryData>(*SummaryIt->second));
+    return llvm::cast<ConcreteSummaryData>(*SummaryIt->second);
+  }
+};
+
+TEST_F(TUSummaryBuilderTest, AddEntity) {
+  TUSummary Summary = makeFakeSummary();
+  TUSummaryBuilder Builder(Summary);
+
+  EntityName EN1("c:@F@foo", "", /*Namespace=*/{});
+  EntityName EN2("c:@F@bar", "", /*Namespace=*/{});
+
+  EntityLinkage MockLinkage;
+  EntityId ID = Builder.addEntity(EN1, MockLinkage);
+  EntityId IDAlias = Builder.addEntity(EN1, MockLinkage);
+  EXPECT_EQ(ID, IDAlias); // Idenpotency
+
+  EntityId ID2 = Builder.addEntity(EN2, MockLinkage);
+  EXPECT_NE(ID, ID2);
+  EXPECT_NE(IDAlias, ID2);
+
+  const EntityIdTable &IdTable = getIdTable(Summary);
+  EXPECT_EQ(IdTable.count(), 2U);
+  EXPECT_TRUE(IdTable.contains(EN1));
+  EXPECT_TRUE(IdTable.contains(EN2));
+
+  const auto &Entities = getEntities(Summary);
+  EXPECT_EQ(Entities.size(), 2U);
+  ASSERT_EQ(Entities.count(ID), 1U);
+  EXPECT_EQ(Entities.find(ID)->second, MockLinkage);
+
+  ASSERT_EQ(Entities.count(ID2), 1U);
+  EXPECT_EQ(Entities.find(ID2)->second, MockLinkage);
+}
+
+TEST_F(TUSummaryBuilderTest, TUSummaryBuilderAddSingleFact) {
+  TUSummary Summary = makeFakeSummary();
+  TUSummaryBuilder Builder(Summary);
+
+  EntityId ID = addTestEntity(Builder, "c:@F@foo");
+  SummaryName Name = addFactTo(Builder, ID, MockSummaryData1(10));
+
+  // Should have a summary type with an entity.
+  EXPECT_THAT(summaryNames(Summary), UnorderedElementsAre(Name));
+  EXPECT_THAT(entitiesOfSummary(Summary, Name), UnorderedElementsAre(ID));
+
+  EXPECT_THAT(getAsEntitySummary<MockSummaryData1>(Summary, Name, ID),
+              Optional(Field(&MockSummaryData1::Value, 10)));
+}
+
+TEST_F(TUSummaryBuilderTest, AddMultipleFactsToSameEntity) {
+  TUSummary Summary = makeFakeSummary();
+  TUSummaryBuilder Builder(Summary);
+  EntityId ID = addTestEntity(Builder, "c:@F@foo");
+
+  // Add different summary types to the same entity.
+  SummaryName Name1 = addFactTo(Builder, ID, MockSummaryData1(42));
+  SummaryName Name2 = addFactTo(Builder, ID, MockSummaryData2("test data"));
+  SummaryName Name3 = addFactTo(Builder, ID, MockSummaryData3(true));
+
+  // All Names must be unique
+  EXPECT_EQ((std::set<SummaryName>{Name1, Name2, Name3}.size()), 3U);
+
+  // Should have 3 summary type with the same entity.
+  EXPECT_THAT(summaryNames(Summary), UnorderedElementsAre(Name1, Name2, 
Name3));
+  EXPECT_THAT(entitiesOfSummary(Summary, Name1), UnorderedElementsAre(ID));
+  EXPECT_THAT(entitiesOfSummary(Summary, Name2), UnorderedElementsAre(ID));
+  EXPECT_THAT(entitiesOfSummary(Summary, Name3), UnorderedElementsAre(ID));
+
+  EXPECT_THAT(getAsEntitySummary<MockSummaryData1>(Summary, Name1, ID),
+              Optional(Field(&MockSummaryData1::Value, 42)));
+  EXPECT_THAT(getAsEntitySummary<MockSummaryData2>(Summary, Name2, ID),
+              Optional(Field(&MockSummaryData2::Text, "test data")));
+  EXPECT_THAT(getAsEntitySummary<MockSummaryData3>(Summary, Name3, ID),
+              Optional(Field(&MockSummaryData3::Flag, true)));
+}
+
+TEST_F(TUSummaryBuilderTest, AddSameFactTypeToMultipleEntities) {
+  TUSummary Summary = makeFakeSummary();
+  TUSummaryBuilder Builder(Summary);
+
+  EntityId ID1 = addTestEntity(Builder, "c:@F@foo");
+  EntityId ID2 = addTestEntity(Builder, "c:@F@bar");
+  EntityId ID3 = addTestEntity(Builder, "c:@F@baz");
+
+  // Add the same summary type to different entities.
+  SummaryName Name1 = addFactTo(Builder, ID1, MockSummaryData1(1));
+  SummaryName Name2 = addFactTo(Builder, ID2, MockSummaryData1(2));
+  SummaryName Name3 = addFactTo(Builder, ID3, MockSummaryData1(3));
+
+  // All 3 should be the same summary type.
+  EXPECT_THAT((llvm::ArrayRef{Name1, Name2, Name3}), testing::Each(Name1));
+
+  // Should have only 1 summary type with 3 entities.
+  EXPECT_THAT(summaryNames(Summary), UnorderedElementsAre(Name1));
+  EXPECT_THAT(entitiesOfSummary(Summary, Name1),
+              UnorderedElementsAre(ID1, ID2, ID3));
+
+  EXPECT_THAT(getAsEntitySummary<MockSummaryData1>(Summary, Name1, ID1),
+              Optional(Field(&MockSummaryData1::Value, 1)));
+  EXPECT_THAT(getAsEntitySummary<MockSummaryData1>(Summary, Name2, ID2),
+              Optional(Field(&MockSummaryData1::Value, 2)));
+  EXPECT_THAT(getAsEntitySummary<MockSummaryData1>(Summary, Name3, ID3),
+              Optional(Field(&MockSummaryData1::Value, 3)));
+}
+
+TEST_F(TUSummaryBuilderTest, AddFactReplacesExistingFact) {
+  TUSummary Summary = makeFakeSummary();
+  TUSummaryBuilder Builder(Summary);
+  EntityId ID = addTestEntity(Builder, "c:@F@foo");
+
+  SummaryName Name = addFactTo(Builder, ID, MockSummaryData1(10));
+
+  // Check the initial value.
+  EXPECT_THAT(summaryNames(Summary), UnorderedElementsAre(Name));
+  EXPECT_THAT(entitiesOfSummary(Summary, Name), UnorderedElementsAre(ID));
+  EXPECT_THAT(getAsEntitySummary<MockSummaryData1>(Summary, Name, ID),
+              Optional(Field(&MockSummaryData1::Value, 10)));
+
+  // Add another fact with the same SummaryName.
+  // This should replace the previous fact.
+  SummaryName ReplacementName = addFactTo(Builder, ID, MockSummaryData1(20));
+  ASSERT_EQ(ReplacementName, Name);
+
+  // Check that the value was replaced.
+  EXPECT_THAT(summaryNames(Summary), UnorderedElementsAre(Name));
+  EXPECT_THAT(entitiesOfSummary(Summary, Name), UnorderedElementsAre(ID));
+  EXPECT_THAT(getAsEntitySummary<MockSummaryData1>(Summary, Name, ID),
+              Optional(Field(&MockSummaryData1::Value, 20)));
+}
+
+TEST_F(TUSummaryBuilderTest, AddFactsComplexScenario) {
+  TUSummary Summary = makeFakeSummary();
+  TUSummaryBuilder Builder(Summary);
+
+  EntityId ID1 = addTestEntity(Builder, "c:@F@foo");
+  EntityId ID2 = addTestEntity(Builder, "c:@F@bar");
+
+  SummaryName Name1 = addFactTo(Builder, ID1, MockSummaryData1(10));
+  SummaryName Name2 = addFactTo(Builder, ID1, MockSummaryData2("twenty"));
+
+  SummaryName Name3 = addFactTo(Builder, ID2, MockSummaryData1(30));
+  SummaryName Name4 = addFactTo(Builder, ID2, MockSummaryData3(true));
+
+  // Check that we have only 3 distinct summary names.
+  EXPECT_EQ(Name1, Name3);
+  EXPECT_THAT((std::set{Name1, Name2, Name3, Name4}),
+              UnorderedElementsAre(Name1, Name2, Name4));
+
+  // Check that we have two facts for the two summaries each.
+  EXPECT_THAT(summaryNames(Summary), UnorderedElementsAre(Name1, Name2, 
Name4));
+  EXPECT_THAT(entitiesOfSummary(Summary, Name1),
+              UnorderedElementsAre(ID1, ID2));
+  EXPECT_THAT(entitiesOfSummary(Summary, Name2), UnorderedElementsAre(ID1));
+  EXPECT_THAT(entitiesOfSummary(Summary, Name4), UnorderedElementsAre(ID2));
+
+  EXPECT_THAT(getAsEntitySummary<MockSummaryData1>(Summary, Name1, ID1),
+              Optional(Field(&MockSummaryData1::Value, 10)));
+  EXPECT_THAT(getAsEntitySummary<MockSummaryData1>(Summary, Name1, ID2),
+              Optional(Field(&MockSummaryData1::Value, 30)));
+  EXPECT_THAT(getAsEntitySummary<MockSummaryData2>(Summary, Name2, ID1),
+              Optional(Field(&MockSummaryData2::Text, "twenty")));
+  EXPECT_THAT(getAsEntitySummary<MockSummaryData3>(Summary, Name4, ID2),
+              Optional(Field(&MockSummaryData3::Flag, true)));
+
+  // Replace a fact of Name1 on entity 1 with the new value 50.
+  SummaryName Name5 = addFactTo(Builder, ID1, MockSummaryData1(50));
+  ASSERT_EQ(Name5, Name1);
+
+  // Check that the summary names and entity IDs didn't change.
+  EXPECT_THAT(summaryNames(Summary), UnorderedElementsAre(Name1, Name2, 
Name4));
+  EXPECT_THAT(entitiesOfSummary(Summary, Name1),
+              UnorderedElementsAre(ID1, ID2));
+  EXPECT_THAT(entitiesOfSummary(Summary, Name2), UnorderedElementsAre(ID1));
+  EXPECT_THAT(entitiesOfSummary(Summary, Name4), UnorderedElementsAre(ID2));
+
+  // Check the Name1 ID1 entity summary value was changed to 50.
+  EXPECT_THAT(getAsEntitySummary<MockSummaryData1>(Summary, Name1, ID1),
+              Optional(Field(&MockSummaryData1::Value, 50)));
+
+  // Check that the rest remained the same.
+  EXPECT_THAT(getAsEntitySummary<MockSummaryData1>(Summary, Name1, ID2),
+              Optional(Field(&MockSummaryData1::Value, 30)));
+  EXPECT_THAT(getAsEntitySummary<MockSummaryData2>(Summary, Name2, ID1),
+              Optional(Field(&MockSummaryData2::Text, "twenty")));
+  EXPECT_THAT(getAsEntitySummary<MockSummaryData3>(Summary, Name4, ID2),
+              Optional(Field(&MockSummaryData3::Flag, true)));
+}
+
+} // namespace
diff --git a/clang/unittests/Analysis/Scalable/TestFixture.cpp 
b/clang/unittests/Analysis/Scalable/TestFixture.cpp
new file mode 100644
index 0000000000000..dfe6ae1dfc7b9
--- /dev/null
+++ b/clang/unittests/Analysis/Scalable/TestFixture.cpp
@@ -0,0 +1,29 @@
+//===- unittests/Analysis/Scalable/TestFixture.cpp 
------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestFixture.h"
+#include "clang/Analysis/Scalable/Model/EntityId.h"
+#include "llvm/Support/raw_ostream.h"
+#include <ostream>
+#include <string>
+
+using namespace clang;
+using namespace ssaf;
+
+template <class T> static std::string asString(const T &Obj) {
+  std::string Repr;
+  llvm::raw_string_ostream(Repr) << Obj;
+  return Repr;
+}
+
+void TestFixture::PrintTo(const EntityId &E, std::ostream *OS) {
+  *OS << asString(E);
+}
+void TestFixture::PrintTo(const SummaryName &N, std::ostream *OS) {
+  *OS << asString(N);
+}
diff --git a/clang/unittests/Analysis/Scalable/TestFixture.h 
b/clang/unittests/Analysis/Scalable/TestFixture.h
new file mode 100644
index 0000000000000..44d07dcafc21f
--- /dev/null
+++ b/clang/unittests/Analysis/Scalable/TestFixture.h
@@ -0,0 +1,34 @@
+//===- TestFixture.h --------------------------------------------*- C++ 
-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_UNITTESTS_ANALYSIS_SCALABLE_TESTFIXTURE_H
+#define LLVM_CLANG_UNITTESTS_ANALYSIS_SCALABLE_TESTFIXTURE_H
+
+#include "clang/Analysis/Scalable/Model/BuildNamespace.h"
+#include "clang/Analysis/Scalable/Model/EntityId.h"
+#include "clang/Analysis/Scalable/Model/SummaryName.h"
+#include "clang/Analysis/Scalable/TUSummary/TUSummary.h"
+#include "gtest/gtest.h"
+#include <iosfwd>
+
+namespace clang::ssaf {
+
+class TestFixture : public ::testing::Test {
+protected:
+#define FIELD(CLASS, FIELD_NAME)                                               
\
+  static const auto &get##FIELD_NAME(const CLASS &X) { return X.FIELD_NAME; }  
\
+  static auto &get##FIELD_NAME(CLASS &X) { return X.FIELD_NAME; }
+#include "clang/Analysis/Scalable/Model/PrivateFieldNames.def"
+
+  static void PrintTo(const EntityId &, std::ostream *);
+  static void PrintTo(const SummaryName &, std::ostream *);
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_UNITTESTS_ANALYSIS_SCALABLE_TESTFIXTURE_H

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

Reply via email to