[clang] [clang][ssaf] Implement TUSummaryBuilder with test infrastructure (PR #181220)

Balázs Benics via cfe-commits cfe-commits at lists.llvm.org
Fri Feb 13 11:52:57 PST 2026


https://github.com/steakhal updated https://github.com/llvm/llvm-project/pull/181220

>From d3e254571e30ff1e7a0087ee03b8be8b10b375e7 Mon Sep 17 00:00:00 2001
From: Balazs Benics <benicsbalazs at gmail.com>
Date: Thu, 12 Feb 2026 20:42:39 +0100
Subject: [PATCH 1/2] [clang][ssaf] Implement TUSummaryBuilder with test
 infrastructure

Also adds a ssaf::TestFixture to provide access to the
private fields of the SSAF object for introspection.

Assisted-By: claude

rdar://168773578
---
 .../Analysis/Scalable/Model/BuildNamespace.h  |   7 +-
 .../clang/Analysis/Scalable/Model/EntityId.h  |   1 +
 .../Analysis/Scalable/Model/EntityName.h      |   2 +-
 .../Analysis/Scalable/Model/SummaryName.h     |   1 +
 .../Analysis/Scalable/TUSummary/TUSummary.h   |   2 +
 .../Scalable/TUSummary/TUSummaryBuilder.h     |  22 +-
 clang/lib/Analysis/Scalable/CMakeLists.txt    |   1 +
 .../Scalable/TUSummary/TUSummaryBuilder.cpp   |  17 +
 .../Analysis/Scalable/CMakeLists.txt          |   2 +
 .../Registries/MockTUSummaryBuilder.h         |   1 +
 .../SummaryExtractorRegistryTest.cpp          |  15 +-
 .../Scalable/TUSummaryBuilderTest.cpp         | 297 ++++++++++++++++++
 .../Analysis/Scalable/TestFixture.cpp         |  29 ++
 .../unittests/Analysis/Scalable/TestFixture.h |  34 ++
 14 files changed, 422 insertions(+), 9 deletions(-)
 create mode 100644 clang/lib/Analysis/Scalable/TUSummary/TUSummaryBuilder.cpp
 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/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/EntityId.h b/clang/include/clang/Analysis/Scalable/Model/EntityId.h
index 6fa059445d853..755a60e40cc68 100644
--- a/clang/include/clang/Analysis/Scalable/Model/EntityId.h
+++ b/clang/include/clang/Analysis/Scalable/Model/EntityId.h
@@ -29,6 +29,7 @@ class EntityIdTable;
 /// \see EntityIdTable
 class EntityId {
   friend class EntityIdTable;
+  friend class TestFixture;
 
   size_t Index;
 
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/SummaryName.h b/clang/include/clang/Analysis/Scalable/Model/SummaryName.h
index 785fe0eb10372..36a8cf5da78b1 100644
--- a/clang/include/clang/Analysis/Scalable/Model/SummaryName.h
+++ b/clang/include/clang/Analysis/Scalable/Model/SummaryName.h
@@ -31,6 +31,7 @@ class SummaryName {
 
 private:
   std::string Name;
+  friend class TestFixture;
 };
 
 } // namespace clang::ssaf
diff --git a/clang/include/clang/Analysis/Scalable/TUSummary/TUSummary.h b/clang/include/clang/Analysis/Scalable/TUSummary/TUSummary.h
index 4af1c70e1a488..7d25234a7ca2c 100644
--- a/clang/include/clang/Analysis/Scalable/TUSummary/TUSummary.h
+++ b/clang/include/clang/Analysis/Scalable/TUSummary/TUSummary.h
@@ -32,6 +32,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..ad41bfb9450bc 100644
--- a/clang/include/clang/Analysis/Scalable/TUSummary/TUSummaryBuilder.h
+++ b/clang/include/clang/Analysis/Scalable/TUSummary/TUSummaryBuilder.h
@@ -9,10 +9,30 @@
 #ifndef LLVM_CLANG_ANALYSIS_SCALABLE_TUSUMMARY_TUSUMMARYBUILDER_H
 #define LLVM_CLANG_ANALYSIS_SCALABLE_TUSUMMARY_TUSUMMARYBUILDER_H
 
+#include <memory>
+
 namespace clang::ssaf {
 
+class EntityId;
+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);
+
+  /// 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 4145e7a521ba4..1d51a2fa1e67f 100644
--- a/clang/lib/Analysis/Scalable/CMakeLists.txt
+++ b/clang/lib/Analysis/Scalable/CMakeLists.txt
@@ -9,6 +9,7 @@ add_clang_library(clangAnalysisScalable
   Model/EntityName.cpp
   Serialization/SerializationFormatRegistry.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..f5ed26b144522
--- /dev/null
+++ b/clang/lib/Analysis/Scalable/TUSummary/TUSummaryBuilder.cpp
@@ -0,0 +1,17 @@
+#include "clang/Analysis/Scalable/TUSummary/TUSummaryBuilder.h"
+#include "clang/Analysis/Scalable/Model/EntityId.h"
+#include "clang/Analysis/Scalable/TUSummary/TUSummary.h"
+#include <memory>
+
+using namespace clang;
+using namespace ssaf;
+
+EntityId TUSummaryBuilder::addEntity(const EntityName &E) {
+  return Summary.IdTable.getId(E);
+}
+
+void TUSummaryBuilder::addFact(EntityId ContributingEntity,
+                               std::unique_ptr<EntitySummary> NewData) {
+  Summary.Data[NewData->getSummaryName()][ContributingEntity] =
+      std::move(NewData);
+}
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/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..a17a4f145b038 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,11 @@
 using namespace clang;
 using namespace ssaf;
 
+[[nodiscard]]
+static TUSummary makeFakeSummary() {
+  return BuildNamespace(BuildNamespaceKind::CompilationUnit, "Mock.cpp");
+}
+
 namespace {
 
 TEST(SummaryExtractorRegistryTest, isTUSummaryExtractorRegistered) {
@@ -39,7 +45,8 @@ TEST(SummaryExtractorRegistryTest, EnumeratingRegistryEntries) {
 }
 
 TEST(SummaryExtractorRegistryTest, InstantiatingExtractor1) {
-  MockTUSummaryBuilder FakeBuilder;
+  TUSummary Summary = makeFakeSummary();
+  MockTUSummaryBuilder FakeBuilder(Summary);
   {
     auto Consumer =
         makeTUSummaryExtractor("MockSummaryExtractor1", FakeBuilder);
@@ -52,7 +59,8 @@ TEST(SummaryExtractorRegistryTest, InstantiatingExtractor1) {
 }
 
 TEST(SummaryExtractorRegistryTest, InstantiatingExtractor2) {
-  MockTUSummaryBuilder FakeBuilder;
+  TUSummary Summary = makeFakeSummary();
+  MockTUSummaryBuilder FakeBuilder(Summary);
   {
     auto Consumer =
         makeTUSummaryExtractor("MockSummaryExtractor2", FakeBuilder);
@@ -65,7 +73,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);
diff --git a/clang/unittests/Analysis/Scalable/TUSummaryBuilderTest.cpp b/clang/unittests/Analysis/Scalable/TUSummaryBuilderTest.cpp
new file mode 100644
index 0000000000000..2e40b5caf9ce5
--- /dev/null
+++ b/clang/unittests/Analysis/Scalable/TUSummaryBuilderTest.cpp
@@ -0,0 +1,297 @@
+//===- 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() {
+  return BuildNamespace(BuildNamespaceKind::CompilationUnit, "Mock.cpp");
+}
+
+[[nodiscard]]
+static EntityId addTestEntity(TUSummaryBuilder &Builder, llvm::StringRef USR) {
+  return Builder.addEntity(EntityName(USR, /*Suffix=*/"", /*Namespace=*/{}));
+}
+
+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 EntitySummary {
+  explicit MockSummaryData1(int Value) : Value(Value) {}
+  SummaryName getSummaryName() const override {
+    return SummaryName("MockSummary1");
+  }
+  int Value;
+};
+
+struct MockSummaryData2 final : public EntitySummary {
+  explicit MockSummaryData2(std::string Text) : Text(std::move(Text)) {}
+  SummaryName getSummaryName() const override {
+    return SummaryName("MockSummary2");
+  }
+  std::string Text;
+};
+
+struct MockSummaryData3 final : public EntitySummary {
+  explicit MockSummaryData3(bool Flag) : Flag(Flag) {}
+  SummaryName getSummaryName() const override {
+    return SummaryName("MockSummary3");
+  }
+  bool Flag;
+};
+
+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 {
+  [[nodiscard]] static SmallVector<SummaryName>
+  summaryNames(const TUSummary &Summary) {
+    return llvm::to_vector(llvm::make_first_range(getData(Summary)));
+  }
+
+  [[nodiscard]] static 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>
+  [[nodiscard]] 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(Name == SummaryIt->second->getSummaryName());
+    return static_cast<const ConcreteSummaryData &>(*SummaryIt->second);
+  }
+};
+
+TEST_F(TUSummaryBuilderTest, AddEntity) {
+  TUSummary Summary = makeFakeSummary();
+  TUSummaryBuilder Builder(Summary);
+
+  EntityName EN1("c:@F at foo", "", /*Namespace=*/{});
+  EntityName EN2("c:@F at bar", "", /*Namespace=*/{});
+
+  EntityId ID = Builder.addEntity(EN1);
+  EntityId IDAlias = Builder.addEntity(EN1);
+  EXPECT_EQ(ID, IDAlias); // Idenpotency
+
+  EntityId ID2 = Builder.addEntity(EN2);
+  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));
+}
+
+TEST_F(TUSummaryBuilderTest, TUSummaryBuilderAddSingleFact) {
+  TUSummary Summary = makeFakeSummary();
+  TUSummaryBuilder Builder(Summary);
+
+  EntityId ID = addTestEntity(Builder, "c:@F at 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 at 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 at foo");
+  EntityId ID2 = addTestEntity(Builder, "c:@F at bar");
+  EntityId ID3 = addTestEntity(Builder, "c:@F at 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 at 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 at foo");
+  EntityId ID2 = addTestEntity(Builder, "c:@F at 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..c3c7027c1dc3f
--- /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 << "EntityId(" << E.Index << ")";
+}
+void TestFixture::PrintTo(const SummaryName &N, std::ostream *OS) {
+  *OS << "SummaryName(" << N.Name << ")";
+}
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

>From fa2934fbba84c9c1fd9540249bb74387c57d938d Mon Sep 17 00:00:00 2001
From: Balazs Benics <benicsbalazs at gmail.com>
Date: Fri, 13 Feb 2026 20:51:58 +0100
Subject: [PATCH 2/2] Avoid overwriting the existing EntitySummary, let users
 decide

---
 .../Scalable/TUSummary/TUSummaryBuilder.h     |  14 +-
 .../Scalable/TUSummary/TUSummaryBuilder.cpp   |  12 +-
 .../Scalable/TUSummaryBuilderTest.cpp         | 155 ++++++++----------
 3 files changed, 82 insertions(+), 99 deletions(-)

diff --git a/clang/include/clang/Analysis/Scalable/TUSummary/TUSummaryBuilder.h b/clang/include/clang/Analysis/Scalable/TUSummary/TUSummaryBuilder.h
index ad41bfb9450bc..e6781a6348a9a 100644
--- a/clang/include/clang/Analysis/Scalable/TUSummary/TUSummaryBuilder.h
+++ b/clang/include/clang/Analysis/Scalable/TUSummary/TUSummaryBuilder.h
@@ -10,6 +10,7 @@
 #define LLVM_CLANG_ANALYSIS_SCALABLE_TUSUMMARY_TUSUMMARYBUILDER_H
 
 #include <memory>
+#include <utility>
 
 namespace clang::ssaf {
 
@@ -26,10 +27,15 @@ class TUSummaryBuilder {
   /// If the entity already exists, returns the existing ID (idempotent).
   EntityId addEntity(const EntityName &E);
 
-  /// 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);
+  /// Associate a \p Data EntitySummary with the \p Entity.
+  /// This consumes the \p Data only if \p Entity wasn't associated yet with the
+  /// same kind of EntitySummary.
+  /// \returns a pointer to the EntitySummary and whether it inserted or not.
+  /// \note Be sure to pass exactly an expression of type
+  /// \sa std::unique_ptr<EntitySummary>, otherwise the conversion operator will
+  /// automatically consume the \p Data.
+  std::pair<EntitySummary *, bool>
+  addFact(EntityId Entity, std::unique_ptr<EntitySummary> &&Data);
 
 private:
   TUSummary &Summary;
diff --git a/clang/lib/Analysis/Scalable/TUSummary/TUSummaryBuilder.cpp b/clang/lib/Analysis/Scalable/TUSummary/TUSummaryBuilder.cpp
index f5ed26b144522..5df08c4ff069c 100644
--- a/clang/lib/Analysis/Scalable/TUSummary/TUSummaryBuilder.cpp
+++ b/clang/lib/Analysis/Scalable/TUSummary/TUSummaryBuilder.cpp
@@ -1,7 +1,9 @@
 #include "clang/Analysis/Scalable/TUSummary/TUSummaryBuilder.h"
 #include "clang/Analysis/Scalable/Model/EntityId.h"
+#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
 #include "clang/Analysis/Scalable/TUSummary/TUSummary.h"
 #include <memory>
+#include <utility>
 
 using namespace clang;
 using namespace ssaf;
@@ -10,8 +12,10 @@ EntityId TUSummaryBuilder::addEntity(const EntityName &E) {
   return Summary.IdTable.getId(E);
 }
 
-void TUSummaryBuilder::addFact(EntityId ContributingEntity,
-                               std::unique_ptr<EntitySummary> NewData) {
-  Summary.Data[NewData->getSummaryName()][ContributingEntity] =
-      std::move(NewData);
+std::pair<EntitySummary *, bool>
+TUSummaryBuilder::addFact(EntityId Entity,
+                          std::unique_ptr<EntitySummary> &&Data) {
+  auto &EntitySummaries = Summary.Data[Data->getSummaryName()];
+  auto [It, Inserted] = EntitySummaries.try_emplace(Entity, std::move(Data));
+  return {It->second.get(), Inserted};
 }
diff --git a/clang/unittests/Analysis/Scalable/TUSummaryBuilderTest.cpp b/clang/unittests/Analysis/Scalable/TUSummaryBuilderTest.cpp
index 2e40b5caf9ce5..9d6f3347e6d8e 100644
--- a/clang/unittests/Analysis/Scalable/TUSummaryBuilderTest.cpp
+++ b/clang/unittests/Analysis/Scalable/TUSummaryBuilderTest.cpp
@@ -21,6 +21,7 @@
 #include "llvm/ADT/StringRef.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include <memory>
 #include <type_traits>
 
 using namespace clang;
@@ -31,25 +32,25 @@ using testing::Field;
 using testing::Optional;
 using testing::UnorderedElementsAre;
 
-[[nodiscard]]
-static TUSummary makeFakeSummary() {
-  return BuildNamespace(BuildNamespaceKind::CompilationUnit, "Mock.cpp");
-}
-
 [[nodiscard]]
 static EntityId addTestEntity(TUSummaryBuilder &Builder, llvm::StringRef USR) {
   return Builder.addEntity(EntityName(USR, /*Suffix=*/"", /*Namespace=*/{}));
 }
 
+struct FactResult {
+  EntitySummary *Fact;
+  bool Inserted;
+};
+
 template <class ConcreteEntitySummary>
 [[nodiscard]]
-static SummaryName addFactTo(TUSummaryBuilder &Builder, EntityId ID,
-                             ConcreteEntitySummary Fact) {
+static auto 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;
+  auto [Place, Inserted] = Builder.addFact(ID, std::move(NewFact));
+  return std::pair{Name, FactResult{Place, Inserted}};
 }
 
 namespace {
@@ -90,6 +91,10 @@ void PrintTo(const MockSummaryData3 &S, std::ostream *OS) {
 }
 
 struct TUSummaryBuilderTest : ssaf::TestFixture {
+  TUSummary Summary =
+      BuildNamespace(BuildNamespaceKind::CompilationUnit, "Mock.cpp");
+  TUSummaryBuilder Builder = TUSummaryBuilder(this->Summary);
+
   [[nodiscard]] static SmallVector<SummaryName>
   summaryNames(const TUSummary &Summary) {
     return llvm::to_vector(llvm::make_first_range(getData(Summary)));
@@ -120,9 +125,6 @@ struct TUSummaryBuilderTest : ssaf::TestFixture {
 };
 
 TEST_F(TUSummaryBuilderTest, AddEntity) {
-  TUSummary Summary = makeFakeSummary();
-  TUSummaryBuilder Builder(Summary);
-
   EntityName EN1("c:@F at foo", "", /*Namespace=*/{});
   EntityName EN2("c:@F at bar", "", /*Namespace=*/{});
 
@@ -141,11 +143,10 @@ TEST_F(TUSummaryBuilderTest, AddEntity) {
 }
 
 TEST_F(TUSummaryBuilderTest, TUSummaryBuilderAddSingleFact) {
-  TUSummary Summary = makeFakeSummary();
-  TUSummaryBuilder Builder(Summary);
-
   EntityId ID = addTestEntity(Builder, "c:@F at foo");
-  SummaryName Name = addFactTo(Builder, ID, MockSummaryData1(10));
+  auto [Name, Res] = addFactTo(Builder, ID, MockSummaryData1(10));
+  ASSERT_TRUE(Res.Inserted);
+  ASSERT_TRUE(Res.Fact);
 
   // Should have a summary type with an entity.
   EXPECT_THAT(summaryNames(Summary), UnorderedElementsAre(Name));
@@ -156,14 +157,18 @@ TEST_F(TUSummaryBuilderTest, TUSummaryBuilderAddSingleFact) {
 }
 
 TEST_F(TUSummaryBuilderTest, AddMultipleFactsToSameEntity) {
-  TUSummary Summary = makeFakeSummary();
-  TUSummaryBuilder Builder(Summary);
   EntityId ID = addTestEntity(Builder, "c:@F at 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));
+  auto [Name1, Res1] = addFactTo(Builder, ID, MockSummaryData1(42));
+  auto [Name2, Res2] = addFactTo(Builder, ID, MockSummaryData2("test data"));
+  auto [Name3, Res3] = addFactTo(Builder, ID, MockSummaryData3(true));
+  ASSERT_TRUE(Res1.Inserted);
+  ASSERT_TRUE(Res2.Inserted);
+  ASSERT_TRUE(Res3.Inserted);
+  ASSERT_TRUE(Res1.Fact);
+  ASSERT_TRUE(Res2.Fact);
+  ASSERT_TRUE(Res3.Fact);
 
   // All Names must be unique
   EXPECT_EQ((std::set<SummaryName>{Name1, Name2, Name3}.size()), 3U);
@@ -183,17 +188,20 @@ TEST_F(TUSummaryBuilderTest, AddMultipleFactsToSameEntity) {
 }
 
 TEST_F(TUSummaryBuilderTest, AddSameFactTypeToMultipleEntities) {
-  TUSummary Summary = makeFakeSummary();
-  TUSummaryBuilder Builder(Summary);
-
   EntityId ID1 = addTestEntity(Builder, "c:@F at foo");
   EntityId ID2 = addTestEntity(Builder, "c:@F at bar");
   EntityId ID3 = addTestEntity(Builder, "c:@F at 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));
+  auto [Name1, Res1] = addFactTo(Builder, ID1, MockSummaryData1(1));
+  auto [Name2, Res2] = addFactTo(Builder, ID2, MockSummaryData1(2));
+  auto [Name3, Res3] = addFactTo(Builder, ID3, MockSummaryData1(3));
+  ASSERT_TRUE(Res1.Inserted);
+  ASSERT_TRUE(Res2.Inserted);
+  ASSERT_TRUE(Res3.Inserted);
+  ASSERT_TRUE(Res1.Fact);
+  ASSERT_TRUE(Res2.Fact);
+  ASSERT_TRUE(Res3.Fact);
 
   // All 3 should be the same summary type.
   EXPECT_THAT((llvm::ArrayRef{Name1, Name2, Name3}), testing::Each(Name1));
@@ -211,12 +219,12 @@ TEST_F(TUSummaryBuilderTest, AddSameFactTypeToMultipleEntities) {
               Optional(Field(&MockSummaryData1::Value, 3)));
 }
 
-TEST_F(TUSummaryBuilderTest, AddFactReplacesExistingFact) {
-  TUSummary Summary = makeFakeSummary();
-  TUSummaryBuilder Builder(Summary);
+TEST_F(TUSummaryBuilderTest, AddConflictingFactToSameEntity) {
   EntityId ID = addTestEntity(Builder, "c:@F at foo");
 
-  SummaryName Name = addFactTo(Builder, ID, MockSummaryData1(10));
+  auto [Name, Res] = addFactTo(Builder, ID, MockSummaryData1(10));
+  ASSERT_TRUE(Res.Inserted);
+  ASSERT_TRUE(Res.Fact);
 
   // Check the initial value.
   EXPECT_THAT(summaryNames(Summary), UnorderedElementsAre(Name));
@@ -224,74 +232,39 @@ TEST_F(TUSummaryBuilderTest, AddFactReplacesExistingFact) {
   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 at foo");
-  EntityId ID2 = addTestEntity(Builder, "c:@F at bar");
-
-  SummaryName Name1 = addFactTo(Builder, ID1, MockSummaryData1(10));
-  SummaryName Name2 = addFactTo(Builder, ID1, MockSummaryData2("twenty"));
+  // This is a different fact of the same kind.
+  std::unique_ptr<EntitySummary> NewFact =
+      std::make_unique<MockSummaryData1>(20);
+  ASSERT_EQ(NewFact->getSummaryName(), Name);
 
-  SummaryName Name3 = addFactTo(Builder, ID2, MockSummaryData1(30));
-  SummaryName Name4 = addFactTo(Builder, ID2, MockSummaryData3(true));
+  // Let's add this different fact.
+  // This should keep the map intact and give us the existing entity summary.
+  auto [Slot, Inserted] = Builder.addFact(ID, std::move(NewFact));
+  ASSERT_FALSE(Inserted);
+  ASSERT_TRUE(Slot);
 
-  // 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 the fact object is not consumed and remained the same.
+  ASSERT_TRUE(NewFact);
+  ASSERT_EQ(static_cast<MockSummaryData1 *>(NewFact.get())->Value, 20);
 
-  // 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));
+  // Check that the Slot refers to the existing entity summary.
+  ASSERT_EQ(Slot->getSummaryName(), Name);
+  ASSERT_EQ(static_cast<MockSummaryData1 *>(Slot)->Value, 10);
 
-  EXPECT_THAT(getAsEntitySummary<MockSummaryData1>(Summary, Name1, ID1),
+  // Check that the values remained the same.
+  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)));
-  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);
+  // We can update the existing summary.
+  static_cast<MockSummaryData1 *>(Res.Fact)->Value = 30;
 
-  // 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),
+  // Check that the values remained the same except what we updated.
+  EXPECT_THAT(summaryNames(Summary), UnorderedElementsAre(Name));
+  EXPECT_THAT(entitiesOfSummary(Summary, Name), UnorderedElementsAre(ID));
+  EXPECT_THAT(getAsEntitySummary<MockSummaryData1>(Summary, Name, ID),
               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



More information about the cfe-commits mailing list