[clang] [clang][ssaf] Add LUSummary consumer APIs (PR #185803)

Aviral Goel via cfe-commits cfe-commits at lists.llvm.org
Wed Mar 11 21:57:28 PDT 2026


https://github.com/aviralg updated https://github.com/llvm/llvm-project/pull/185803

>From 23e4142de20f0ae2ea6b8d800a115d40456fba5f Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Tue, 10 Mar 2026 22:17:34 -0700
Subject: [PATCH 1/8] Initial implementation

---
 .../Scalable/EntityLinker/LUSummary.h         |   1 +
 .../Scalable/SummaryView/LUSummaryConsumer.h  |  81 +++++++
 .../Scalable/SummaryView/SummaryView.h        |  32 +++
 .../Scalable/SummaryView/SummaryViewBuilder.h | 104 +++++++++
 .../SummaryView/SummaryViewBuilderRegistry.h  |  44 ++++
 clang/lib/Analysis/Scalable/CMakeLists.txt    |   2 +
 .../SummaryView/LUSummaryConsumer.cpp         |  51 +++++
 .../SummaryViewBuilderRegistry.cpp            |  23 ++
 .../Analysis/Scalable/CMakeLists.txt          |   4 +
 .../SummaryView/LUSummaryConsumerTest.cpp     | 215 ++++++++++++++++++
 .../SummaryView/MockSummaryViewBuilder1.cpp   |  45 ++++
 .../SummaryView/MockSummaryViewBuilder2.cpp   |  41 ++++
 .../SummaryView/MockSummaryViewBuilders.h     |  68 ++++++
 .../SummaryViewBuilderRegistryTest.cpp        |  82 +++++++
 14 files changed, 793 insertions(+)
 create mode 100644 clang/include/clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h
 create mode 100644 clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h
 create mode 100644 clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h
 create mode 100644 clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h
 create mode 100644 clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp
 create mode 100644 clang/lib/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.cpp
 create mode 100644 clang/unittests/Analysis/Scalable/SummaryView/LUSummaryConsumerTest.cpp
 create mode 100644 clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder1.cpp
 create mode 100644 clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder2.cpp
 create mode 100644 clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilders.h
 create mode 100644 clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp

diff --git a/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummary.h b/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummary.h
index e24f7f581185c..7d0ec92f5c9e7 100644
--- a/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummary.h
+++ b/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummary.h
@@ -31,6 +31,7 @@ namespace clang::ssaf {
 /// together. It contains deduplicated entities with their linkage information
 /// and the merged entity summaries.
 class LUSummary {
+  friend class LUSummaryConsumer;
   friend class SerializationFormat;
   friend class TestFixture;
 
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h b/clang/include/clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h
new file mode 100644
index 0000000000000..eb71b2add386d
--- /dev/null
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h
@@ -0,0 +1,81 @@
+//===- LUSummaryConsumer.h ------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// LUSummaryConsumer constructs SummaryView objects by routing LUSummary entity
+// data to the corresponding SummaryViewBuilder objects.
+//
+// Typical usage:
+//
+//   auto LU = Format->readLUSummary(Path);
+//
+//   LUSummaryConsumer Consumer(std::move(LU));
+//   Consumer.run();
+//
+//   auto View = Consumer.getView<MyView>();
+//   // View is std::unique_ptr<MyView>; Consumer no longer holds it.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_LUSUMMARYCONSUMER_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_LUSUMMARYCONSUMER_H
+
+#include "clang/Analysis/Scalable/EntityLinker/LUSummary.h"
+#include "clang/Analysis/Scalable/Model/EntityId.h"
+#include "clang/Analysis/Scalable/Model/SummaryName.h"
+#include "clang/Analysis/Scalable/SummaryView/SummaryView.h"
+#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
+#include <map>
+#include <memory>
+
+namespace clang::ssaf {
+
+/// Consumes a LUSummary by dispatching its entity data to all registered
+/// SummaryViewBuilders and collecting the resulting views.
+class LUSummaryConsumer final {
+  std::unique_ptr<LUSummary> LU;
+  std::map<SummaryName, std::unique_ptr<SummaryView>> Views;
+  bool WasRun = false;
+
+public:
+  explicit LUSummaryConsumer(std::unique_ptr<LUSummary> LU)
+      : LU(std::move(LU)) {}
+
+  /// Instantiates a builder for each SummaryName present in the LUSummary,
+  /// delivers its entities, finalizes it, and stores the resulting view.
+  /// Each builder is fully processed (addSummary → finalize → getView) before
+  /// the next SummaryName is visited. Builders are discarded on return.
+  ///
+  /// \pre Must be called exactly once.
+  void run();
+
+  /// Transfers ownership of the view for \p ViewT to the caller.
+  ///
+  /// Returns nullptr if no builder for \p ViewT was registered or run() has
+  /// not been called. A second call for the same ViewT also returns nullptr.
+  template <typename ViewT> [[nodiscard]] std::unique_ptr<ViewT> getView() {
+    auto It = Views.find(ViewT::summaryName());
+    if (It == Views.end()) {
+      return nullptr;
+    }
+    auto *RawPtr = static_cast<ViewT *>(It->second.release());
+    Views.erase(It);
+    return std::unique_ptr<ViewT>(RawPtr);
+  }
+
+private:
+  using EntityDataMap = std::map<EntityId, std::unique_ptr<EntitySummary>>;
+
+  /// Processes all entities for a single SummaryName: instantiates the
+  /// registered builder, delivers entities, finalizes, and stores the view.
+  /// Does nothing if no builder is registered for \p SN.
+  void run(const SummaryName &SN, EntityDataMap &Data);
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_LUSUMMARYCONSUMER_H
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h
new file mode 100644
index 0000000000000..34accf5be3c20
--- /dev/null
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h
@@ -0,0 +1,32 @@
+//===- SummaryView.h
+//-------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Abstract base class for all whole-program analysis views built from
+// LUSummary data. Carries no query API — all analysis-specific methods live
+// on concrete subclasses.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEW_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEW_H
+
+namespace clang::ssaf {
+
+/// Abstract base class for whole-program analysis views.
+///
+/// Concrete view classes inherit from this and add a static
+/// \c summaryName() method along with their analysis-specific query API.
+class SummaryView {
+public:
+  virtual ~SummaryView() = default;
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEW_H
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h
new file mode 100644
index 0000000000000..d3d05587b30d9
--- /dev/null
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h
@@ -0,0 +1,104 @@
+//===- SummaryViewBuilder.h
+//------------------------------------------------===//
+//
+// 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 SummaryViewBuilderBase (abstract base known to the
+// registry and LUSummaryConsumer) and the typed intermediate template
+// SummaryViewBuilder<ViewT, SummaryT> that concrete builders inherit from.
+//
+// To implement a view builder, inherit from SummaryViewBuilder:
+//
+//   class MyViewBuilder
+//       : public SummaryViewBuilder<MyView, MyEntitySummary> {
+//   public:
+//     void addSummary(EntityId Id,
+//                     std::unique_ptr<MyEntitySummary> Summary) override {
+//       // accumulate into getView()
+//     }
+//     // summaryName() and getView() && provided by the intermediate.
+//     // override finalize() if post-processing is needed.
+//   };
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDER_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDER_H
+
+#include "clang/Analysis/Scalable/Model/EntityId.h"
+#include "clang/Analysis/Scalable/Model/SummaryName.h"
+#include "clang/Analysis/Scalable/SummaryView/SummaryView.h"
+#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
+#include <memory>
+
+namespace clang::ssaf {
+
+/// Abstract base class for all summary view builders.
+///
+/// Known to the registry and LUSummaryConsumer. Receives entities one at a
+/// time via \c addSummary(), is finalized via \c finalize(), and transfers
+/// ownership of the built view via \c getView() &&.
+class SummaryViewBuilderBase {
+public:
+  virtual ~SummaryViewBuilderBase() = default;
+
+  /// Returns the SummaryName this builder handles.
+  virtual SummaryName summaryName() const = 0;
+
+  /// Called once per entity belonging to this builder's analysis.
+  /// Takes ownership of the summary data.
+  virtual void addSummary(EntityId Id,
+                          std::unique_ptr<EntitySummary> Summary) = 0;
+
+  /// Called after all entities have been added.
+  virtual void finalize() {}
+
+  /// Transfers ownership of the built view (type-erased).
+  /// Called by LUSummaryConsumer after finalize(). The rvalue ref-qualifier
+  /// enforces single use — the builder cannot be accessed after this call.
+  virtual std::unique_ptr<SummaryView> getView() && = 0;
+};
+
+/// Typed intermediate template that concrete builders inherit from.
+///
+/// Provides:
+/// - \c summaryName() derived from \c ViewT::summaryName().
+/// - \c getView() && which moves out the built view.
+/// - A protected \c getView() accessor for use during construction.
+/// - NVI dispatch: seals the base \c addSummary(EntityId, EntitySummary) as
+///   \c final, downcasts, and dispatches to the typed overload.
+///
+/// Concrete builders only need to implement the typed
+/// \c addSummary(EntityId, unique_ptr<SummaryT>) overload.
+template <typename ViewT, typename SummaryT>
+class SummaryViewBuilder : public SummaryViewBuilderBase {
+  std::unique_ptr<ViewT> View;
+
+protected:
+  ViewT &getView() & { return *View; }
+
+public:
+  SummaryViewBuilder() : View(std::make_unique<ViewT>()) {}
+
+  SummaryName summaryName() const override { return ViewT::summaryName(); }
+
+  std::unique_ptr<SummaryView> getView() && override { return std::move(View); }
+
+  /// Typed customization point — concrete builders override this.
+  virtual void addSummary(EntityId Id, std::unique_ptr<SummaryT> Summary) = 0;
+
+private:
+  /// Seals the base overload, downcasts, and dispatches to the typed overload.
+  void addSummary(EntityId Id, std::unique_ptr<EntitySummary> Summary) final {
+    addSummary(Id, std::unique_ptr<SummaryT>(
+                       static_cast<SummaryT *>(Summary.release())));
+  }
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDER_H
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h
new file mode 100644
index 0000000000000..c02843ad940d3
--- /dev/null
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h
@@ -0,0 +1,44 @@
+//===- SummaryViewBuilderRegistry.h
+//----------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Registry for SummaryViewBuilders and helper functions.
+//
+// To register a builder, add this to the builder's translation unit:
+//
+//   static SummaryViewBuilderRegistry::Add<MyViewBuilder>
+//       Register("MyAnalysis", "View builder for MyAnalysis");
+//
+// The registry entry name must match MyView::summaryName().
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDERREGISTRY_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDERREGISTRY_H
+
+#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h"
+#include "clang/Support/Compiler.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Registry.h"
+
+namespace clang::ssaf {
+
+/// Check if a SummaryViewBuilder was registered with a given name.
+bool isSummaryViewBuilderRegistered(llvm::StringRef Name);
+
+/// Registry for adding new SummaryViewBuilder implementations.
+using SummaryViewBuilderRegistry = llvm::Registry<SummaryViewBuilderBase>;
+
+} // namespace clang::ssaf
+
+namespace llvm {
+extern template class CLANG_TEMPLATE_ABI
+    Registry<clang::ssaf::SummaryViewBuilderBase>;
+} // namespace llvm
+
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDERREGISTRY_H
diff --git a/clang/lib/Analysis/Scalable/CMakeLists.txt b/clang/lib/Analysis/Scalable/CMakeLists.txt
index 4593fbcd515b5..3da854558f760 100644
--- a/clang/lib/Analysis/Scalable/CMakeLists.txt
+++ b/clang/lib/Analysis/Scalable/CMakeLists.txt
@@ -18,6 +18,8 @@ add_clang_library(clangAnalysisScalable
   Serialization/JSONFormat/TUSummary.cpp
   Serialization/JSONFormat/TUSummaryEncoding.cpp
   Serialization/SerializationFormatRegistry.cpp
+  SummaryView/LUSummaryConsumer.cpp
+  SummaryView/SummaryViewBuilderRegistry.cpp
   Support/ErrorBuilder.cpp
   TUSummary/ExtractorRegistry.cpp
   TUSummary/TUSummaryBuilder.cpp
diff --git a/clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp b/clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp
new file mode 100644
index 0000000000000..a3db29be840d6
--- /dev/null
+++ b/clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp
@@ -0,0 +1,51 @@
+//===- LUSummaryConsumer.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/SummaryView/LUSummaryConsumer.h"
+#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
+#include <cassert>
+#include <memory>
+
+using namespace clang;
+using namespace ssaf;
+
+static std::unique_ptr<SummaryViewBuilderBase>
+instantiateBuilder(const SummaryName &SN) {
+  for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
+    if (Entry.getName() == SN.str()) {
+      return Entry.instantiate();
+    }
+  }
+  return nullptr;
+}
+
+void LUSummaryConsumer::run(const SummaryName &SN, EntityDataMap &Data) {
+  auto Builder = instantiateBuilder(SN);
+  if (!Builder) {
+    return;
+  }
+
+  assert(Builder->summaryName() == SN &&
+         "registry entry name must match SummaryViewBuilder::summaryName()");
+
+  for (auto &[Id, Summary] : Data) {
+    Builder->addSummary(Id, std::move(Summary));
+  }
+
+  Builder->finalize();
+
+  Views.emplace(SN, std::move(*Builder).getView());
+}
+
+void LUSummaryConsumer::run() {
+  assert(!WasRun && "run() must be called exactly once");
+  WasRun = true;
+  for (auto &[SN, Data] : LU->Data) {
+    run(SN, Data);
+  }
+}
diff --git a/clang/lib/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.cpp b/clang/lib/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.cpp
new file mode 100644
index 0000000000000..eb64394ba4401
--- /dev/null
+++ b/clang/lib/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.cpp
@@ -0,0 +1,23 @@
+//===- SummaryViewBuilderRegistry.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/SummaryView/SummaryViewBuilderRegistry.h"
+
+using namespace clang;
+using namespace ssaf;
+
+LLVM_INSTANTIATE_REGISTRY(SummaryViewBuilderRegistry)
+
+bool ssaf::isSummaryViewBuilderRegistered(llvm::StringRef Name) {
+  for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
+    if (Entry.getName() == Name) {
+      return true;
+    }
+  }
+  return false;
+}
diff --git a/clang/unittests/Analysis/Scalable/CMakeLists.txt b/clang/unittests/Analysis/Scalable/CMakeLists.txt
index 54f9de71884dd..fbeefea15b421 100644
--- a/clang/unittests/Analysis/Scalable/CMakeLists.txt
+++ b/clang/unittests/Analysis/Scalable/CMakeLists.txt
@@ -19,6 +19,10 @@ add_distinct_clang_unittest(ClangScalableAnalysisTests
   Serialization/JSONFormatTest/LUSummaryTest.cpp
   Serialization/JSONFormatTest/TUSummaryTest.cpp
   SummaryNameTest.cpp
+  SummaryView/LUSummaryConsumerTest.cpp
+  SummaryView/MockSummaryViewBuilder1.cpp
+  SummaryView/MockSummaryViewBuilder2.cpp
+  SummaryView/SummaryViewBuilderRegistryTest.cpp
   TestFixture.cpp
   TUSummaryBuilderTest.cpp
 
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/LUSummaryConsumerTest.cpp b/clang/unittests/Analysis/Scalable/SummaryView/LUSummaryConsumerTest.cpp
new file mode 100644
index 0000000000000..bab8730ec3343
--- /dev/null
+++ b/clang/unittests/Analysis/Scalable/SummaryView/LUSummaryConsumerTest.cpp
@@ -0,0 +1,215 @@
+//===- LUSummaryConsumerTest.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/SummaryView/LUSummaryConsumer.h"
+#include "../TestFixture.h"
+#include "MockSummaryViewBuilders.h"
+#include "clang/Analysis/Scalable/EntityLinker/LUSummary.h"
+#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/EntityName.h"
+#include "clang/Analysis/Scalable/Model/SummaryName.h"
+#include "gtest/gtest.h"
+#include <memory>
+
+using namespace clang;
+using namespace ssaf;
+
+namespace {
+
+class LUSummaryConsumerTest : public TestFixture {
+protected:
+  void SetUp() override { clearMockBuilderLog(); }
+
+  std::unique_ptr<LUSummary> makeLUSummary() {
+    NestedBuildNamespace NS(
+        {BuildNamespace(BuildNamespaceKind::LinkUnit, "TestLU")});
+    return std::make_unique<LUSummary>(std::move(NS));
+  }
+
+  /// Add an entity to the LUSummary's id table and return its EntityId.
+  EntityId addEntity(LUSummary &LU, llvm::StringRef USR) {
+    NestedBuildNamespace NS(
+        {BuildNamespace(BuildNamespaceKind::LinkUnit, "TestLU")});
+    EntityName Name(USR.str(), "", NS);
+    return getIdTable(LU).getId(Name);
+  }
+
+  /// Insert a pre-built EntitySummary into LUSummary::Data.
+  void insertSummary(LUSummary &LU, llvm::StringRef SummaryNameStr, EntityId Id,
+                     std::unique_ptr<EntitySummary> Summary) {
+    getData(LU)[SummaryName(SummaryNameStr.str())][Id] = std::move(Summary);
+  }
+};
+
+// ---------------------------------------------------------------------------
+// No matching data in LUSummary
+// ---------------------------------------------------------------------------
+
+TEST_F(LUSummaryConsumerTest, NoMatchingData_AddSummaryNeverCalled) {
+  auto LU = makeLUSummary();
+  // Insert data for a summary name that has no registered builder.
+  auto Id = addEntity(*LU, "A");
+  insertSummary(*LU, "UnregisteredAnalysis", Id,
+                std::make_unique<MockEntitySummary1>());
+
+  LUSummaryConsumer Consumer(std::move(LU));
+  Consumer.run();
+
+  EXPECT_EQ(MockBuilderLog.find("addSummary"), std::string::npos);
+}
+
+TEST_F(LUSummaryConsumerTest, NoMatchingData_FinalizeNotCalled) {
+  auto LU = makeLUSummary(); // empty — no Mock1 data
+
+  LUSummaryConsumer Consumer(std::move(LU));
+  Consumer.run();
+
+  EXPECT_EQ(MockBuilderLog.find("finalize"), std::string::npos);
+}
+
+TEST_F(LUSummaryConsumerTest, NoMatchingData_GetViewReturnsNull) {
+  auto LU = makeLUSummary();
+
+  LUSummaryConsumer Consumer(std::move(LU));
+  Consumer.run();
+
+  EXPECT_EQ(Consumer.getView<MockView1>(), nullptr);
+}
+
+// ---------------------------------------------------------------------------
+// Matching data delivered correctly
+// ---------------------------------------------------------------------------
+
+TEST_F(LUSummaryConsumerTest, MatchingData_AddSummaryCalledPerEntity) {
+  auto LU = makeLUSummary();
+  auto Id1 = addEntity(*LU, "A");
+  auto Id2 = addEntity(*LU, "B");
+  insertSummary(*LU, "Mock1", Id1, std::make_unique<MockEntitySummary1>());
+  insertSummary(*LU, "Mock1", Id2, std::make_unique<MockEntitySummary1>());
+
+  LUSummaryConsumer Consumer(std::move(LU));
+  Consumer.run();
+
+  auto View = Consumer.getView<MockView1>();
+  ASSERT_NE(View, nullptr);
+  EXPECT_EQ(View->Ids.size(), 2u);
+  EXPECT_NE(std::find(View->Ids.begin(), View->Ids.end(), Id1),
+            View->Ids.end());
+  EXPECT_NE(std::find(View->Ids.begin(), View->Ids.end(), Id2),
+            View->Ids.end());
+}
+
+TEST_F(LUSummaryConsumerTest, MatchingData_FinalizeCalledAfterAllAddSummary) {
+  auto LU = makeLUSummary();
+  auto Id = addEntity(*LU, "A");
+  insertSummary(*LU, "Mock1", Id, std::make_unique<MockEntitySummary1>());
+
+  LUSummaryConsumer Consumer(std::move(LU));
+  Consumer.run();
+
+  // Verify ordering in the log: all addSummary entries must precede all
+  // finalize entries.
+  auto AddSummaryPos = MockBuilderLog.find("addSummary");
+  auto FinalizePos = MockBuilderLog.find("finalize");
+  ASSERT_NE(AddSummaryPos, std::string::npos);
+  ASSERT_NE(FinalizePos, std::string::npos);
+  EXPECT_LT(AddSummaryPos, FinalizePos);
+}
+
+// ---------------------------------------------------------------------------
+// Multiple builders receive only their own entities
+// ---------------------------------------------------------------------------
+
+TEST_F(LUSummaryConsumerTest, MultipleBuilders_IndependentEntities) {
+  auto LU = makeLUSummary();
+  auto Id1 = addEntity(*LU, "A");
+  auto Id2 = addEntity(*LU, "B");
+  insertSummary(*LU, "Mock1", Id1, std::make_unique<MockEntitySummary1>());
+  insertSummary(*LU, "Mock2", Id2, std::make_unique<MockEntitySummary2>());
+
+  LUSummaryConsumer Consumer(std::move(LU));
+  Consumer.run();
+
+  auto View1 = Consumer.getView<MockView1>();
+  ASSERT_NE(View1, nullptr);
+  ASSERT_EQ(View1->Ids.size(), 1u);
+  EXPECT_EQ(View1->Ids[0], Id1);
+
+  auto View2 = Consumer.getView<MockView2>();
+  ASSERT_NE(View2, nullptr);
+  ASSERT_EQ(View2->Ids.size(), 1u);
+  EXPECT_EQ(View2->Ids[0], Id2);
+}
+
+// ---------------------------------------------------------------------------
+// getView ownership transfer
+// ---------------------------------------------------------------------------
+
+TEST_F(LUSummaryConsumerTest, GetView_FirstCallReturnsNonNull) {
+  auto LU = makeLUSummary();
+  auto Id = addEntity(*LU, "A");
+  insertSummary(*LU, "Mock1", Id, std::make_unique<MockEntitySummary1>());
+
+  LUSummaryConsumer Consumer(std::move(LU));
+  Consumer.run();
+
+  EXPECT_NE(Consumer.getView<MockView1>(), nullptr);
+}
+
+TEST_F(LUSummaryConsumerTest, GetView_SecondCallReturnsNull) {
+  auto LU = makeLUSummary();
+  auto Id = addEntity(*LU, "A");
+  insertSummary(*LU, "Mock1", Id, std::make_unique<MockEntitySummary1>());
+
+  LUSummaryConsumer Consumer(std::move(LU));
+  Consumer.run();
+
+  auto First = Consumer.getView<MockView1>();
+  EXPECT_NE(First, nullptr);
+  EXPECT_EQ(Consumer.getView<MockView1>(), nullptr);
+}
+
+TEST_F(LUSummaryConsumerTest, GetView_UnregisteredViewReturnsNull) {
+  // MockView1 is registered; a hypothetical OtherView is not.
+  struct OtherView : public SummaryView {
+    static SummaryName summaryName() { return SummaryName("Other"); }
+  };
+
+  auto LU = makeLUSummary();
+  LUSummaryConsumer Consumer(std::move(LU));
+  Consumer.run();
+
+  EXPECT_EQ(Consumer.getView<OtherView>(), nullptr);
+}
+
+// ---------------------------------------------------------------------------
+// Builder lifetime
+// ---------------------------------------------------------------------------
+
+TEST_F(LUSummaryConsumerTest, BuildersDestroyedAfterRun) {
+  auto LU = makeLUSummary();
+  auto Id1 = addEntity(*LU, "A");
+  auto Id2 = addEntity(*LU, "B");
+  insertSummary(*LU, "Mock1", Id1, std::make_unique<MockEntitySummary1>());
+  insertSummary(*LU, "Mock2", Id2, std::make_unique<MockEntitySummary2>());
+
+  LUSummaryConsumer Consumer(std::move(LU));
+  Consumer.run();
+
+  // Both destructors must have been called by the time run() returns.
+  EXPECT_NE(
+      MockBuilderLog.find("MockSummaryViewBuilder1 destructor was invoked"),
+      std::string::npos);
+  EXPECT_NE(
+      MockBuilderLog.find("MockSummaryViewBuilder2 destructor was invoked"),
+      std::string::npos);
+}
+
+} // namespace
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder1.cpp b/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder1.cpp
new file mode 100644
index 0000000000000..2bfc526969393
--- /dev/null
+++ b/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder1.cpp
@@ -0,0 +1,45 @@
+//===- MockSummaryViewBuilder1.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 "MockSummaryViewBuilders.h"
+#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
+
+using namespace clang;
+using namespace ssaf;
+
+std::string clang::ssaf::MockBuilderLog;
+
+void clang::ssaf::clearMockBuilderLog() { MockBuilderLog.clear(); }
+
+namespace {
+
+class MockSummaryViewBuilder1
+    : public SummaryViewBuilder<MockView1, MockEntitySummary1> {
+public:
+  MockSummaryViewBuilder1() {
+    MockBuilderLog += "MockSummaryViewBuilder1 constructor was invoked\n";
+  }
+
+  ~MockSummaryViewBuilder1() {
+    MockBuilderLog += "MockSummaryViewBuilder1 destructor was invoked\n";
+  }
+
+  void addSummary(EntityId Id, std::unique_ptr<MockEntitySummary1>) override {
+    MockBuilderLog += "MockSummaryViewBuilder1 addSummary was invoked\n";
+    getView().Ids.push_back(Id);
+  }
+
+  void finalize() override {
+    MockBuilderLog += "MockSummaryViewBuilder1 finalize was invoked\n";
+  }
+};
+
+static SummaryViewBuilderRegistry::Add<MockSummaryViewBuilder1>
+    Register("Mock1", "Mock view builder 1");
+
+} // namespace
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder2.cpp b/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder2.cpp
new file mode 100644
index 0000000000000..b43e7f69bbb8e
--- /dev/null
+++ b/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder2.cpp
@@ -0,0 +1,41 @@
+//===- MockSummaryViewBuilder2.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 "MockSummaryViewBuilders.h"
+#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
+
+using namespace clang;
+using namespace ssaf;
+
+namespace {
+
+class MockSummaryViewBuilder2
+    : public SummaryViewBuilder<MockView2, MockEntitySummary2> {
+public:
+  MockSummaryViewBuilder2() {
+    MockBuilderLog += "MockSummaryViewBuilder2 constructor was invoked\n";
+  }
+
+  ~MockSummaryViewBuilder2() {
+    MockBuilderLog += "MockSummaryViewBuilder2 destructor was invoked\n";
+  }
+
+  void addSummary(EntityId Id, std::unique_ptr<MockEntitySummary2>) override {
+    MockBuilderLog += "MockSummaryViewBuilder2 addSummary was invoked\n";
+    getView().Ids.push_back(Id);
+  }
+
+  void finalize() override {
+    MockBuilderLog += "MockSummaryViewBuilder2 finalize was invoked\n";
+  }
+};
+
+static SummaryViewBuilderRegistry::Add<MockSummaryViewBuilder2>
+    Register("Mock2", "Mock view builder 2");
+
+} // namespace
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilders.h b/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilders.h
new file mode 100644
index 0000000000000..f6a46a65d2256
--- /dev/null
+++ b/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilders.h
@@ -0,0 +1,68 @@
+//===- MockSummaryViewBuilders.h
+//-------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Shared mock types for SummaryView tests. Two mock analyses ("Mock1" and
+// "Mock2") are defined here, with their builders registered in
+// MockSummaryViewBuilder1.cpp and MockSummaryViewBuilder2.cpp.
+//
+// Tests observe builder behaviour via MockBuilderLog, which records lifecycle
+// events (constructor, addSummary, finalize, destructor) as newline-terminated
+// strings. Call clearMockBuilderLog() in SetUp() to isolate tests.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef CLANG_UNITTESTS_ANALYSIS_SCALABLE_SUMMARYVIEW_MOCKSUMMARYVIEWBUILDERS_H
+#define CLANG_UNITTESTS_ANALYSIS_SCALABLE_SUMMARYVIEW_MOCKSUMMARYVIEWBUILDERS_H
+
+#include "clang/Analysis/Scalable/Model/EntityId.h"
+#include "clang/Analysis/Scalable/Model/SummaryName.h"
+#include "clang/Analysis/Scalable/SummaryView/SummaryView.h"
+#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h"
+#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
+#include <string>
+#include <vector>
+
+namespace clang::ssaf {
+
+// ---- Mock entity summaries -----------------------------------------------
+
+class MockEntitySummary1 : public EntitySummary {
+public:
+  SummaryName getSummaryName() const override { return SummaryName("Mock1"); }
+};
+
+class MockEntitySummary2 : public EntitySummary {
+public:
+  SummaryName getSummaryName() const override { return SummaryName("Mock2"); }
+};
+
+// ---- Mock views ------------------------------------------------------------
+
+class MockView1 : public SummaryView {
+public:
+  static SummaryName summaryName() { return SummaryName("Mock1"); }
+  std::vector<EntityId> Ids;
+};
+
+class MockView2 : public SummaryView {
+public:
+  static SummaryName summaryName() { return SummaryName("Mock2"); }
+  std::vector<EntityId> Ids;
+};
+
+// ---- Shared log ------------------------------------------------------------
+// Defined in MockSummaryViewBuilder1.cpp; written by both mock builders.
+
+extern std::string MockBuilderLog;
+
+void clearMockBuilderLog();
+
+} // namespace clang::ssaf
+
+#endif // CLANG_UNITTESTS_ANALYSIS_SCALABLE_SUMMARYVIEW_MOCKSUMMARYVIEWBUILDERS_H
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp b/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp
new file mode 100644
index 0000000000000..99803ef6ffff7
--- /dev/null
+++ b/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp
@@ -0,0 +1,82 @@
+//===- SummaryViewBuilderRegistryTest.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/SummaryView/SummaryViewBuilderRegistry.h"
+#include "MockSummaryViewBuilders.h"
+#include "llvm/ADT/StringRef.h"
+#include "gtest/gtest.h"
+#include <set>
+
+using namespace clang;
+using namespace ssaf;
+
+namespace {
+
+class SummaryViewBuilderRegistryTest : public ::testing::Test {
+protected:
+  void SetUp() override { clearMockBuilderLog(); }
+};
+
+TEST_F(SummaryViewBuilderRegistryTest, isSummaryViewBuilderRegistered) {
+  EXPECT_FALSE(isSummaryViewBuilderRegistered("Non-existent-builder"));
+  EXPECT_TRUE(isSummaryViewBuilderRegistered("Mock1"));
+  EXPECT_TRUE(isSummaryViewBuilderRegistered("Mock2"));
+}
+
+TEST_F(SummaryViewBuilderRegistryTest, EnumeratingRegistryEntries) {
+  std::set<llvm::StringRef> ActualNames;
+  for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
+    bool Inserted = ActualNames.insert(Entry.getName()).second;
+    EXPECT_TRUE(Inserted);
+  }
+
+  EXPECT_EQ(ActualNames, (std::set<llvm::StringRef>{"Mock1", "Mock2"}));
+}
+
+TEST_F(SummaryViewBuilderRegistryTest, InstantiatingBuilder1) {
+  {
+    // Find Mock1 entry explicitly.
+    std::unique_ptr<SummaryViewBuilderBase> B1;
+    for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
+      if (Entry.getName() == "Mock1") {
+        B1 = Entry.instantiate();
+      }
+    }
+
+    ASSERT_TRUE(B1);
+    EXPECT_EQ(B1->summaryName(), SummaryName("Mock1"));
+    EXPECT_TRUE(MockBuilderLog.find(
+                    "MockSummaryViewBuilder1 constructor was invoked") !=
+                std::string::npos);
+  }
+  EXPECT_TRUE(
+      MockBuilderLog.find("MockSummaryViewBuilder1 destructor was invoked") !=
+      std::string::npos);
+}
+
+TEST_F(SummaryViewBuilderRegistryTest, InstantiatingBuilder2) {
+  {
+    std::unique_ptr<SummaryViewBuilderBase> B2;
+    for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
+      if (Entry.getName() == "Mock2") {
+        B2 = Entry.instantiate();
+      }
+    }
+
+    ASSERT_TRUE(B2);
+    EXPECT_EQ(B2->summaryName(), SummaryName("Mock2"));
+    EXPECT_TRUE(MockBuilderLog.find(
+                    "MockSummaryViewBuilder2 constructor was invoked") !=
+                std::string::npos);
+  }
+  EXPECT_TRUE(
+      MockBuilderLog.find("MockSummaryViewBuilder2 destructor was invoked") !=
+      std::string::npos);
+}
+
+} // namespace

>From 3e97ae7a0ad8ab63d0ea17c55f3cd2b2438532c7 Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Tue, 10 Mar 2026 22:33:38 -0700
Subject: [PATCH 2/8] Fix

---
 .../Scalable/SummaryView/SummaryView.h        |  3 +--
 .../Scalable/SummaryView/SummaryViewBuilder.h |  3 +--
 .../SummaryView/SummaryViewBuilderRegistry.h  |  3 +--
 .../SummaryView/MockSummaryViewBuilders.h     |  3 +--
 .../SummaryViewBuilderRegistryTest.cpp        | 20 +++++++++----------
 5 files changed, 14 insertions(+), 18 deletions(-)

diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h
index 34accf5be3c20..46778e94976f3 100644
--- a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h
@@ -1,5 +1,4 @@
-//===- SummaryView.h
-//-------------------------------------------------------===//
+//===- SummaryView.h ------------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h
index d3d05587b30d9..c71652662f4d9 100644
--- a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h
@@ -1,5 +1,4 @@
-//===- SummaryViewBuilder.h
-//------------------------------------------------===//
+//===- SummaryViewBuilder.h -----------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h
index c02843ad940d3..d3fc08a71a446 100644
--- a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h
@@ -1,5 +1,4 @@
-//===- SummaryViewBuilderRegistry.h
-//----------------------------------------===//
+//===- SummaryViewBuilderRegistry.h ---------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilders.h b/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilders.h
index f6a46a65d2256..8bdcb27ca93f6 100644
--- a/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilders.h
+++ b/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilders.h
@@ -1,5 +1,4 @@
-//===- MockSummaryViewBuilders.h
-//-------------------------------------------===//
+//===- MockSummaryViewBuilders.h ------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp b/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp
index 99803ef6ffff7..b48682500cd39 100644
--- a/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp
+++ b/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp
@@ -50,12 +50,12 @@ TEST_F(SummaryViewBuilderRegistryTest, InstantiatingBuilder1) {
 
     ASSERT_TRUE(B1);
     EXPECT_EQ(B1->summaryName(), SummaryName("Mock1"));
-    EXPECT_TRUE(MockBuilderLog.find(
-                    "MockSummaryViewBuilder1 constructor was invoked") !=
-                std::string::npos);
+    EXPECT_NE(
+        MockBuilderLog.find("MockSummaryViewBuilder1 constructor was invoked"),
+        std::string::npos);
   }
-  EXPECT_TRUE(
-      MockBuilderLog.find("MockSummaryViewBuilder1 destructor was invoked") !=
+  EXPECT_NE(
+      MockBuilderLog.find("MockSummaryViewBuilder1 destructor was invoked"),
       std::string::npos);
 }
 
@@ -70,12 +70,12 @@ TEST_F(SummaryViewBuilderRegistryTest, InstantiatingBuilder2) {
 
     ASSERT_TRUE(B2);
     EXPECT_EQ(B2->summaryName(), SummaryName("Mock2"));
-    EXPECT_TRUE(MockBuilderLog.find(
-                    "MockSummaryViewBuilder2 constructor was invoked") !=
-                std::string::npos);
+    EXPECT_NE(
+        MockBuilderLog.find("MockSummaryViewBuilder2 constructor was invoked"),
+        std::string::npos);
   }
-  EXPECT_TRUE(
-      MockBuilderLog.find("MockSummaryViewBuilder2 destructor was invoked") !=
+  EXPECT_NE(
+      MockBuilderLog.find("MockSummaryViewBuilder2 destructor was invoked"),
       std::string::npos);
 }
 

>From edf933a1d8355485a56f605f6606be57d622d4e8 Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Wed, 11 Mar 2026 13:40:46 -0700
Subject: [PATCH 3/8] Testing

---
 .../Analysis/Scalable/CMakeLists.txt          |   2 -
 .../SummaryView/LUSummaryConsumerTest.cpp     | 336 +++++++++++-------
 .../SummaryView/MockSummaryViewBuilder1.cpp   |  45 ---
 .../SummaryView/MockSummaryViewBuilder2.cpp   |  41 ---
 .../SummaryView/MockSummaryViewBuilders.h     |  67 ----
 .../SummaryViewBuilderRegistryTest.cpp        |  66 ++--
 6 files changed, 223 insertions(+), 334 deletions(-)
 delete mode 100644 clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder1.cpp
 delete mode 100644 clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder2.cpp
 delete mode 100644 clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilders.h

diff --git a/clang/unittests/Analysis/Scalable/CMakeLists.txt b/clang/unittests/Analysis/Scalable/CMakeLists.txt
index fbeefea15b421..41e99dcca5fd2 100644
--- a/clang/unittests/Analysis/Scalable/CMakeLists.txt
+++ b/clang/unittests/Analysis/Scalable/CMakeLists.txt
@@ -20,8 +20,6 @@ add_distinct_clang_unittest(ClangScalableAnalysisTests
   Serialization/JSONFormatTest/TUSummaryTest.cpp
   SummaryNameTest.cpp
   SummaryView/LUSummaryConsumerTest.cpp
-  SummaryView/MockSummaryViewBuilder1.cpp
-  SummaryView/MockSummaryViewBuilder2.cpp
   SummaryView/SummaryViewBuilderRegistryTest.cpp
   TestFixture.cpp
   TUSummaryBuilderTest.cpp
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/LUSummaryConsumerTest.cpp b/clang/unittests/Analysis/Scalable/SummaryView/LUSummaryConsumerTest.cpp
index bab8730ec3343..3499d5bda1847 100644
--- a/clang/unittests/Analysis/Scalable/SummaryView/LUSummaryConsumerTest.cpp
+++ b/clang/unittests/Analysis/Scalable/SummaryView/LUSummaryConsumerTest.cpp
@@ -8,208 +8,272 @@
 
 #include "clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h"
 #include "../TestFixture.h"
-#include "MockSummaryViewBuilders.h"
 #include "clang/Analysis/Scalable/EntityLinker/LUSummary.h"
 #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/EntityName.h"
 #include "clang/Analysis/Scalable/Model/SummaryName.h"
+#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
+#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
 #include "gtest/gtest.h"
+#include <algorithm>
 #include <memory>
+#include <utility>
+#include <vector>
 
 using namespace clang;
 using namespace ssaf;
 
 namespace {
 
-class LUSummaryConsumerTest : public TestFixture {
-protected:
-  void SetUp() override { clearMockBuilderLog(); }
+// ---------------------------------------------------------------------------
+// Instance counter
+// ---------------------------------------------------------------------------
 
-  std::unique_ptr<LUSummary> makeLUSummary() {
-    NestedBuildNamespace NS(
-        {BuildNamespace(BuildNamespaceKind::LinkUnit, "TestLU")});
-    return std::make_unique<LUSummary>(std::move(NS));
+static int NextSummaryInstanceId = 0;
+
+// ---------------------------------------------------------------------------
+// Entity summaries
+// ---------------------------------------------------------------------------
+
+class Analysis1EntitySummary : public EntitySummary {
+public:
+  int InstanceId = NextSummaryInstanceId++;
+  SummaryName getSummaryName() const override {
+    return SummaryName("Analysis1");
   }
+};
 
-  /// Add an entity to the LUSummary's id table and return its EntityId.
-  EntityId addEntity(LUSummary &LU, llvm::StringRef USR) {
-    NestedBuildNamespace NS(
-        {BuildNamespace(BuildNamespaceKind::LinkUnit, "TestLU")});
-    EntityName Name(USR.str(), "", NS);
-    return getIdTable(LU).getId(Name);
+class Analysis2EntitySummary : public EntitySummary {
+public:
+  int InstanceId = NextSummaryInstanceId++;
+  SummaryName getSummaryName() const override {
+    return SummaryName("Analysis2");
   }
+};
 
-  /// Insert a pre-built EntitySummary into LUSummary::Data.
-  void insertSummary(LUSummary &LU, llvm::StringRef SummaryNameStr, EntityId Id,
-                     std::unique_ptr<EntitySummary> Summary) {
-    getData(LU)[SummaryName(SummaryNameStr.str())][Id] = std::move(Summary);
+class Analysis4EntitySummary : public EntitySummary {
+public:
+  int InstanceId = NextSummaryInstanceId++;
+  SummaryName getSummaryName() const override {
+    return SummaryName("Analysis4");
   }
 };
 
 // ---------------------------------------------------------------------------
-// No matching data in LUSummary
+// Views
 // ---------------------------------------------------------------------------
 
-TEST_F(LUSummaryConsumerTest, NoMatchingData_AddSummaryNeverCalled) {
-  auto LU = makeLUSummary();
-  // Insert data for a summary name that has no registered builder.
-  auto Id = addEntity(*LU, "A");
-  insertSummary(*LU, "UnregisteredAnalysis", Id,
-                std::make_unique<MockEntitySummary1>());
+class Analysis1View : public SummaryView {
+public:
+  static SummaryName summaryName() { return SummaryName("Analysis1"); }
+  std::vector<std::pair<EntityId, int>> Entries;
+  bool WasFinalized = false;
+};
 
-  LUSummaryConsumer Consumer(std::move(LU));
-  Consumer.run();
+class Analysis2View : public SummaryView {
+public:
+  static SummaryName summaryName() { return SummaryName("Analysis2"); }
+  std::vector<std::pair<EntityId, int>> Entries;
+  bool WasFinalized = false;
+};
 
-  EXPECT_EQ(MockBuilderLog.find("addSummary"), std::string::npos);
-}
+// No builder or registration for Analysis3. Data for Analysis3 is inserted
+// into the LUSummary to verify the consumer silently skips it.
+class Analysis3View : public SummaryView {
+public:
+  static SummaryName summaryName() { return SummaryName("Analysis3"); }
+};
 
-TEST_F(LUSummaryConsumerTest, NoMatchingData_FinalizeNotCalled) {
-  auto LU = makeLUSummary(); // empty — no Mock1 data
+// Analysis4 has a registered builder but no data is inserted into the
+// LUSummary, so the builder is never invoked and getView returns nullptr.
+class Analysis4View : public SummaryView {
+public:
+  static SummaryName summaryName() { return SummaryName("Analysis4"); }
+};
 
-  LUSummaryConsumer Consumer(std::move(LU));
-  Consumer.run();
+// ---------------------------------------------------------------------------
+// Builder destruction flags (reset in SetUp)
+// ---------------------------------------------------------------------------
 
-  EXPECT_EQ(MockBuilderLog.find("finalize"), std::string::npos);
-}
+static bool Analysis1BuilderWasDestroyed = false;
+static bool Analysis2BuilderWasDestroyed = false;
+static bool Analysis4BuilderWasDestroyed = false;
 
-TEST_F(LUSummaryConsumerTest, NoMatchingData_GetViewReturnsNull) {
-  auto LU = makeLUSummary();
+// ---------------------------------------------------------------------------
+// Builders
+// ---------------------------------------------------------------------------
 
-  LUSummaryConsumer Consumer(std::move(LU));
-  Consumer.run();
+class Analysis1Builder
+    : public SummaryViewBuilder<Analysis1View, Analysis1EntitySummary> {
+public:
+  ~Analysis1Builder() { Analysis1BuilderWasDestroyed = true; }
 
-  EXPECT_EQ(Consumer.getView<MockView1>(), nullptr);
-}
+  void addSummary(EntityId Id,
+                  std::unique_ptr<Analysis1EntitySummary> S) override {
+    getView().Entries.push_back({Id, S->InstanceId});
+  }
 
-// ---------------------------------------------------------------------------
-// Matching data delivered correctly
-// ---------------------------------------------------------------------------
+  void finalize() override { getView().WasFinalized = true; }
+};
 
-TEST_F(LUSummaryConsumerTest, MatchingData_AddSummaryCalledPerEntity) {
-  auto LU = makeLUSummary();
-  auto Id1 = addEntity(*LU, "A");
-  auto Id2 = addEntity(*LU, "B");
-  insertSummary(*LU, "Mock1", Id1, std::make_unique<MockEntitySummary1>());
-  insertSummary(*LU, "Mock1", Id2, std::make_unique<MockEntitySummary1>());
+static SummaryViewBuilderRegistry::Add<Analysis1Builder>
+    RegAnalysis1("Analysis1", "Builder for Analysis1");
 
-  LUSummaryConsumer Consumer(std::move(LU));
-  Consumer.run();
+class Analysis2Builder
+    : public SummaryViewBuilder<Analysis2View, Analysis2EntitySummary> {
+public:
+  ~Analysis2Builder() { Analysis2BuilderWasDestroyed = true; }
 
-  auto View = Consumer.getView<MockView1>();
-  ASSERT_NE(View, nullptr);
-  EXPECT_EQ(View->Ids.size(), 2u);
-  EXPECT_NE(std::find(View->Ids.begin(), View->Ids.end(), Id1),
-            View->Ids.end());
-  EXPECT_NE(std::find(View->Ids.begin(), View->Ids.end(), Id2),
-            View->Ids.end());
-}
+  void addSummary(EntityId Id,
+                  std::unique_ptr<Analysis2EntitySummary> S) override {
+    getView().Entries.push_back({Id, S->InstanceId});
+  }
 
-TEST_F(LUSummaryConsumerTest, MatchingData_FinalizeCalledAfterAllAddSummary) {
-  auto LU = makeLUSummary();
-  auto Id = addEntity(*LU, "A");
-  insertSummary(*LU, "Mock1", Id, std::make_unique<MockEntitySummary1>());
+  void finalize() override { getView().WasFinalized = true; }
+};
 
-  LUSummaryConsumer Consumer(std::move(LU));
-  Consumer.run();
+static SummaryViewBuilderRegistry::Add<Analysis2Builder>
+    RegAnalysis2("Analysis2", "Builder for Analysis2");
 
-  // Verify ordering in the log: all addSummary entries must precede all
-  // finalize entries.
-  auto AddSummaryPos = MockBuilderLog.find("addSummary");
-  auto FinalizePos = MockBuilderLog.find("finalize");
-  ASSERT_NE(AddSummaryPos, std::string::npos);
-  ASSERT_NE(FinalizePos, std::string::npos);
-  EXPECT_LT(AddSummaryPos, FinalizePos);
-}
+class Analysis4Builder
+    : public SummaryViewBuilder<Analysis4View, Analysis4EntitySummary> {
+public:
+  ~Analysis4Builder() { Analysis4BuilderWasDestroyed = true; }
+
+  void addSummary(EntityId Id,
+                  std::unique_ptr<Analysis4EntitySummary> S) override {
+    getView().Entries.push_back({Id, S->InstanceId});
+  }
+
+  void finalize() override { getView().WasFinalized = true; }
+};
+
+static SummaryViewBuilderRegistry::Add<Analysis4Builder>
+    RegAnalysis4("Analysis4", "Builder for Analysis4");
 
 // ---------------------------------------------------------------------------
-// Multiple builders receive only their own entities
+// Fixture
 // ---------------------------------------------------------------------------
 
-TEST_F(LUSummaryConsumerTest, MultipleBuilders_IndependentEntities) {
-  auto LU = makeLUSummary();
-  auto Id1 = addEntity(*LU, "A");
-  auto Id2 = addEntity(*LU, "B");
-  insertSummary(*LU, "Mock1", Id1, std::make_unique<MockEntitySummary1>());
-  insertSummary(*LU, "Mock2", Id2, std::make_unique<MockEntitySummary2>());
+class LUSummaryConsumerTest : public TestFixture {
+protected:
+  static constexpr EntityLinkage ExternalLinkage =
+      EntityLinkage(EntityLinkageType::External);
+
+  void SetUp() override {
+    NextSummaryInstanceId = 0;
+    Analysis1BuilderWasDestroyed = false;
+    Analysis2BuilderWasDestroyed = false;
+    Analysis4BuilderWasDestroyed = false;
+  }
 
-  LUSummaryConsumer Consumer(std::move(LU));
-  Consumer.run();
+  std::unique_ptr<LUSummary> makeLUSummary() {
+    NestedBuildNamespace NS(
+        {BuildNamespace(BuildNamespaceKind::LinkUnit, "TestLU")});
+    return std::make_unique<LUSummary>(std::move(NS));
+  }
 
-  auto View1 = Consumer.getView<MockView1>();
-  ASSERT_NE(View1, nullptr);
-  ASSERT_EQ(View1->Ids.size(), 1u);
-  EXPECT_EQ(View1->Ids[0], Id1);
+  EntityId addEntity(LUSummary &LU, llvm::StringRef USR) {
+    NestedBuildNamespace NS(
+        {BuildNamespace(BuildNamespaceKind::LinkUnit, "TestLU")});
+    EntityName Name(USR.str(), "", NS);
+    EntityId Id = getIdTable(LU).getId(Name);
+    getLinkageTable(LU).insert({Id, ExternalLinkage});
+    return Id;
+  }
 
-  auto View2 = Consumer.getView<MockView2>();
-  ASSERT_NE(View2, nullptr);
-  ASSERT_EQ(View2->Ids.size(), 1u);
-  EXPECT_EQ(View2->Ids[0], Id2);
-}
+  static bool hasEntry(const std::vector<std::pair<EntityId, int>> &Entries,
+                       EntityId Id, int InstanceId) {
+    return std::find(Entries.begin(), Entries.end(),
+                     std::make_pair(Id, InstanceId)) != Entries.end();
+  }
+
+  /// Inserts a freshly constructed SummaryT for the given entity and returns
+  /// the summary's InstanceId so the test can verify delivery later.
+  template <typename SummaryT>
+  int insertSummary(LUSummary &LU, llvm::StringRef SN, EntityId Id) {
+    auto S = std::make_unique<SummaryT>();
+    int InstanceId = S->InstanceId;
+    getData(LU)[SummaryName(SN.str())][Id] = std::move(S);
+    return InstanceId;
+  }
+};
 
 // ---------------------------------------------------------------------------
-// getView ownership transfer
+// Tests
 // ---------------------------------------------------------------------------
 
-TEST_F(LUSummaryConsumerTest, GetView_FirstCallReturnsNonNull) {
+TEST_F(LUSummaryConsumerTest, Run) {
   auto LU = makeLUSummary();
-  auto Id = addEntity(*LU, "A");
-  insertSummary(*LU, "Mock1", Id, std::make_unique<MockEntitySummary1>());
+  const auto E1 = addEntity(*LU, "Entity1");
+  const auto E2 = addEntity(*LU, "Entity2");
+  const auto E3 = addEntity(*LU, "Entity3");
+
+  int s1a = insertSummary<Analysis1EntitySummary>(*LU, "Analysis1", E1);
+  int s1b = insertSummary<Analysis1EntitySummary>(*LU, "Analysis1", E2);
+  int s2a = insertSummary<Analysis2EntitySummary>(*LU, "Analysis2", E2);
+  int s2b = insertSummary<Analysis2EntitySummary>(*LU, "Analysis2", E3);
+
+  // no registered builder
+  [[maybe_unused]] int s3a =
+      insertSummary<Analysis1EntitySummary>(*LU, "Analysis3", E1);
 
   LUSummaryConsumer Consumer(std::move(LU));
   Consumer.run();
 
-  EXPECT_NE(Consumer.getView<MockView1>(), nullptr);
-}
+  // Analysis1
+  {
+    auto View1 = Consumer.getView<Analysis1View>();
+    ASSERT_NE(View1, nullptr);
 
-TEST_F(LUSummaryConsumerTest, GetView_SecondCallReturnsNull) {
-  auto LU = makeLUSummary();
-  auto Id = addEntity(*LU, "A");
-  insertSummary(*LU, "Mock1", Id, std::make_unique<MockEntitySummary1>());
+    // getView ownership transfer — second call returns nullptr
+    EXPECT_EQ(Consumer.getView<Analysis1View>(), nullptr);
 
-  LUSummaryConsumer Consumer(std::move(LU));
-  Consumer.run();
+    EXPECT_EQ(View1->Entries.size(), 2u);
+    EXPECT_TRUE(hasEntry(View1->Entries, E1, s1a));
+    EXPECT_TRUE(hasEntry(View1->Entries, E2, s1b));
 
-  auto First = Consumer.getView<MockView1>();
-  EXPECT_NE(First, nullptr);
-  EXPECT_EQ(Consumer.getView<MockView1>(), nullptr);
-}
+    EXPECT_TRUE(View1->WasFinalized);
 
-TEST_F(LUSummaryConsumerTest, GetView_UnregisteredViewReturnsNull) {
-  // MockView1 is registered; a hypothetical OtherView is not.
-  struct OtherView : public SummaryView {
-    static SummaryName summaryName() { return SummaryName("Other"); }
-  };
+    // Builder lifetime
+    EXPECT_TRUE(Analysis1BuilderWasDestroyed);
+  }
 
-  auto LU = makeLUSummary();
-  LUSummaryConsumer Consumer(std::move(LU));
-  Consumer.run();
+  // Analysis2
+  {
+    auto View2 = Consumer.getView<Analysis2View>();
+    ASSERT_NE(View2, nullptr);
 
-  EXPECT_EQ(Consumer.getView<OtherView>(), nullptr);
-}
+    // getView ownership transfer — second call returns nullptr
+    EXPECT_EQ(Consumer.getView<Analysis2View>(), nullptr);
 
-// ---------------------------------------------------------------------------
-// Builder lifetime
-// ---------------------------------------------------------------------------
+    EXPECT_EQ(View2->Entries.size(), 2u);
+    EXPECT_TRUE(hasEntry(View2->Entries, E2, s2a));
+    EXPECT_TRUE(hasEntry(View2->Entries, E3, s2b));
 
-TEST_F(LUSummaryConsumerTest, BuildersDestroyedAfterRun) {
-  auto LU = makeLUSummary();
-  auto Id1 = addEntity(*LU, "A");
-  auto Id2 = addEntity(*LU, "B");
-  insertSummary(*LU, "Mock1", Id1, std::make_unique<MockEntitySummary1>());
-  insertSummary(*LU, "Mock2", Id2, std::make_unique<MockEntitySummary2>());
+    EXPECT_TRUE(View2->WasFinalized);
 
-  LUSummaryConsumer Consumer(std::move(LU));
-  Consumer.run();
+    // Builder lifetime
+    EXPECT_TRUE(Analysis2BuilderWasDestroyed);
+  }
+
+  // Analysis 3
+  {
+    // Unregistered builder — Analysis3 data silently skipped
+    EXPECT_EQ(Consumer.getView<Analysis3View>(), nullptr);
+  }
+
+  // Analysis4
+  {
+    // Registered builder but no data in LUSummary — builder never invoked
+    EXPECT_EQ(Consumer.getView<Analysis4View>(), nullptr);
 
-  // Both destructors must have been called by the time run() returns.
-  EXPECT_NE(
-      MockBuilderLog.find("MockSummaryViewBuilder1 destructor was invoked"),
-      std::string::npos);
-  EXPECT_NE(
-      MockBuilderLog.find("MockSummaryViewBuilder2 destructor was invoked"),
-      std::string::npos);
+    // Builder lifetime
+    EXPECT_FALSE(Analysis4BuilderWasDestroyed);
+  }
 }
 
 } // namespace
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder1.cpp b/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder1.cpp
deleted file mode 100644
index 2bfc526969393..0000000000000
--- a/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder1.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-//===- MockSummaryViewBuilder1.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 "MockSummaryViewBuilders.h"
-#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
-
-using namespace clang;
-using namespace ssaf;
-
-std::string clang::ssaf::MockBuilderLog;
-
-void clang::ssaf::clearMockBuilderLog() { MockBuilderLog.clear(); }
-
-namespace {
-
-class MockSummaryViewBuilder1
-    : public SummaryViewBuilder<MockView1, MockEntitySummary1> {
-public:
-  MockSummaryViewBuilder1() {
-    MockBuilderLog += "MockSummaryViewBuilder1 constructor was invoked\n";
-  }
-
-  ~MockSummaryViewBuilder1() {
-    MockBuilderLog += "MockSummaryViewBuilder1 destructor was invoked\n";
-  }
-
-  void addSummary(EntityId Id, std::unique_ptr<MockEntitySummary1>) override {
-    MockBuilderLog += "MockSummaryViewBuilder1 addSummary was invoked\n";
-    getView().Ids.push_back(Id);
-  }
-
-  void finalize() override {
-    MockBuilderLog += "MockSummaryViewBuilder1 finalize was invoked\n";
-  }
-};
-
-static SummaryViewBuilderRegistry::Add<MockSummaryViewBuilder1>
-    Register("Mock1", "Mock view builder 1");
-
-} // namespace
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder2.cpp b/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder2.cpp
deleted file mode 100644
index b43e7f69bbb8e..0000000000000
--- a/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder2.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-//===- MockSummaryViewBuilder2.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 "MockSummaryViewBuilders.h"
-#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
-
-using namespace clang;
-using namespace ssaf;
-
-namespace {
-
-class MockSummaryViewBuilder2
-    : public SummaryViewBuilder<MockView2, MockEntitySummary2> {
-public:
-  MockSummaryViewBuilder2() {
-    MockBuilderLog += "MockSummaryViewBuilder2 constructor was invoked\n";
-  }
-
-  ~MockSummaryViewBuilder2() {
-    MockBuilderLog += "MockSummaryViewBuilder2 destructor was invoked\n";
-  }
-
-  void addSummary(EntityId Id, std::unique_ptr<MockEntitySummary2>) override {
-    MockBuilderLog += "MockSummaryViewBuilder2 addSummary was invoked\n";
-    getView().Ids.push_back(Id);
-  }
-
-  void finalize() override {
-    MockBuilderLog += "MockSummaryViewBuilder2 finalize was invoked\n";
-  }
-};
-
-static SummaryViewBuilderRegistry::Add<MockSummaryViewBuilder2>
-    Register("Mock2", "Mock view builder 2");
-
-} // namespace
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilders.h b/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilders.h
deleted file mode 100644
index 8bdcb27ca93f6..0000000000000
--- a/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilders.h
+++ /dev/null
@@ -1,67 +0,0 @@
-//===- MockSummaryViewBuilders.h ------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-//
-// Shared mock types for SummaryView tests. Two mock analyses ("Mock1" and
-// "Mock2") are defined here, with their builders registered in
-// MockSummaryViewBuilder1.cpp and MockSummaryViewBuilder2.cpp.
-//
-// Tests observe builder behaviour via MockBuilderLog, which records lifecycle
-// events (constructor, addSummary, finalize, destructor) as newline-terminated
-// strings. Call clearMockBuilderLog() in SetUp() to isolate tests.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef CLANG_UNITTESTS_ANALYSIS_SCALABLE_SUMMARYVIEW_MOCKSUMMARYVIEWBUILDERS_H
-#define CLANG_UNITTESTS_ANALYSIS_SCALABLE_SUMMARYVIEW_MOCKSUMMARYVIEWBUILDERS_H
-
-#include "clang/Analysis/Scalable/Model/EntityId.h"
-#include "clang/Analysis/Scalable/Model/SummaryName.h"
-#include "clang/Analysis/Scalable/SummaryView/SummaryView.h"
-#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h"
-#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
-#include <string>
-#include <vector>
-
-namespace clang::ssaf {
-
-// ---- Mock entity summaries -----------------------------------------------
-
-class MockEntitySummary1 : public EntitySummary {
-public:
-  SummaryName getSummaryName() const override { return SummaryName("Mock1"); }
-};
-
-class MockEntitySummary2 : public EntitySummary {
-public:
-  SummaryName getSummaryName() const override { return SummaryName("Mock2"); }
-};
-
-// ---- Mock views ------------------------------------------------------------
-
-class MockView1 : public SummaryView {
-public:
-  static SummaryName summaryName() { return SummaryName("Mock1"); }
-  std::vector<EntityId> Ids;
-};
-
-class MockView2 : public SummaryView {
-public:
-  static SummaryName summaryName() { return SummaryName("Mock2"); }
-  std::vector<EntityId> Ids;
-};
-
-// ---- Shared log ------------------------------------------------------------
-// Defined in MockSummaryViewBuilder1.cpp; written by both mock builders.
-
-extern std::string MockBuilderLog;
-
-void clearMockBuilderLog();
-
-} // namespace clang::ssaf
-
-#endif // CLANG_UNITTESTS_ANALYSIS_SCALABLE_SUMMARYVIEW_MOCKSUMMARYVIEWBUILDERS_H
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp b/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp
index b48682500cd39..30d3606247b2e 100644
--- a/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp
+++ b/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp
@@ -7,9 +7,9 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
-#include "MockSummaryViewBuilders.h"
 #include "llvm/ADT/StringRef.h"
 #include "gtest/gtest.h"
+#include <memory>
 #include <set>
 
 using namespace clang;
@@ -17,15 +17,13 @@ using namespace ssaf;
 
 namespace {
 
-class SummaryViewBuilderRegistryTest : public ::testing::Test {
-protected:
-  void SetUp() override { clearMockBuilderLog(); }
-};
+class SummaryViewBuilderRegistryTest : public ::testing::Test {};
 
 TEST_F(SummaryViewBuilderRegistryTest, isSummaryViewBuilderRegistered) {
   EXPECT_FALSE(isSummaryViewBuilderRegistered("Non-existent-builder"));
-  EXPECT_TRUE(isSummaryViewBuilderRegistered("Mock1"));
-  EXPECT_TRUE(isSummaryViewBuilderRegistered("Mock2"));
+  EXPECT_TRUE(isSummaryViewBuilderRegistered("Analysis1"));
+  EXPECT_TRUE(isSummaryViewBuilderRegistered("Analysis2"));
+  EXPECT_TRUE(isSummaryViewBuilderRegistered("Analysis4"));
 }
 
 TEST_F(SummaryViewBuilderRegistryTest, EnumeratingRegistryEntries) {
@@ -35,48 +33,30 @@ TEST_F(SummaryViewBuilderRegistryTest, EnumeratingRegistryEntries) {
     EXPECT_TRUE(Inserted);
   }
 
-  EXPECT_EQ(ActualNames, (std::set<llvm::StringRef>{"Mock1", "Mock2"}));
+  EXPECT_EQ(ActualNames,
+            (std::set<llvm::StringRef>{"Analysis1", "Analysis2", "Analysis4"}));
 }
 
-TEST_F(SummaryViewBuilderRegistryTest, InstantiatingBuilder1) {
-  {
-    // Find Mock1 entry explicitly.
-    std::unique_ptr<SummaryViewBuilderBase> B1;
-    for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
-      if (Entry.getName() == "Mock1") {
-        B1 = Entry.instantiate();
-      }
-    }
-
-    ASSERT_TRUE(B1);
-    EXPECT_EQ(B1->summaryName(), SummaryName("Mock1"));
-    EXPECT_NE(
-        MockBuilderLog.find("MockSummaryViewBuilder1 constructor was invoked"),
-        std::string::npos);
+TEST_F(SummaryViewBuilderRegistryTest, InstantiatingBuilder_Analysis1) {
+  std::unique_ptr<SummaryViewBuilderBase> B;
+  for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
+    if (Entry.getName() == "Analysis1")
+      B = Entry.instantiate();
   }
-  EXPECT_NE(
-      MockBuilderLog.find("MockSummaryViewBuilder1 destructor was invoked"),
-      std::string::npos);
-}
 
-TEST_F(SummaryViewBuilderRegistryTest, InstantiatingBuilder2) {
-  {
-    std::unique_ptr<SummaryViewBuilderBase> B2;
-    for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
-      if (Entry.getName() == "Mock2") {
-        B2 = Entry.instantiate();
-      }
-    }
+  ASSERT_NE(B, nullptr);
+  EXPECT_EQ(B->summaryName(), SummaryName("Analysis1"));
+}
 
-    ASSERT_TRUE(B2);
-    EXPECT_EQ(B2->summaryName(), SummaryName("Mock2"));
-    EXPECT_NE(
-        MockBuilderLog.find("MockSummaryViewBuilder2 constructor was invoked"),
-        std::string::npos);
+TEST_F(SummaryViewBuilderRegistryTest, InstantiatingBuilder_Analysis2) {
+  std::unique_ptr<SummaryViewBuilderBase> B;
+  for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
+    if (Entry.getName() == "Analysis2")
+      B = Entry.instantiate();
   }
-  EXPECT_NE(
-      MockBuilderLog.find("MockSummaryViewBuilder2 destructor was invoked"),
-      std::string::npos);
+
+  ASSERT_NE(B, nullptr);
+  EXPECT_EQ(B->summaryName(), SummaryName("Analysis2"));
 }
 
 } // namespace

>From c6f2af8315d60bdb5adbcdabe0f9f088fadba648 Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Wed, 11 Mar 2026 15:57:27 -0700
Subject: [PATCH 4/8] Fix

---
 .../Scalable/SummaryView/LUSummaryConsumer.h  |  33 +--
 .../Scalable/SummaryView/SummaryView.h        |   3 -
 .../Scalable/SummaryView/SummaryViewBuilder.h |  58 ++--
 .../SummaryView/SummaryViewBuilderRegistry.h  |  50 ++--
 .../Scalable/SummaryView/SummaryViewTraits.h  |  33 +++
 .../SummaryView/LUSummaryConsumer.cpp         |  15 +-
 .../SummaryViewBuilderRegistry.cpp            |  17 +-
 .../Analysis/Scalable/CMakeLists.txt          |   3 +-
 .../SummaryView/LUSummaryConsumerTest.cpp     | 279 ------------------
 .../SummaryViewBuilderRegistryTest.cpp        |  62 ----
 10 files changed, 115 insertions(+), 438 deletions(-)
 create mode 100644 clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewTraits.h
 delete mode 100644 clang/unittests/Analysis/Scalable/SummaryView/LUSummaryConsumerTest.cpp
 delete mode 100644 clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp

diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h b/clang/include/clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h
index eb71b2add386d..a440f05999e0c 100644
--- a/clang/include/clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h
@@ -9,16 +9,6 @@
 // LUSummaryConsumer constructs SummaryView objects by routing LUSummary entity
 // data to the corresponding SummaryViewBuilder objects.
 //
-// Typical usage:
-//
-//   auto LU = Format->readLUSummary(Path);
-//
-//   LUSummaryConsumer Consumer(std::move(LU));
-//   Consumer.run();
-//
-//   auto View = Consumer.getView<MyView>();
-//   // View is std::unique_ptr<MyView>; Consumer no longer holds it.
-//
 //===----------------------------------------------------------------------===//
 
 #ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_LUSUMMARYCONSUMER_H
@@ -28,6 +18,7 @@
 #include "clang/Analysis/Scalable/Model/EntityId.h"
 #include "clang/Analysis/Scalable/Model/SummaryName.h"
 #include "clang/Analysis/Scalable/SummaryView/SummaryView.h"
+#include "clang/Analysis/Scalable/SummaryView/SummaryViewTraits.h"
 #include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
 #include <map>
 #include <memory>
@@ -37,18 +28,14 @@ namespace clang::ssaf {
 /// Consumes a LUSummary by dispatching its entity data to all registered
 /// SummaryViewBuilders and collecting the resulting views.
 class LUSummaryConsumer final {
-  std::unique_ptr<LUSummary> LU;
-  std::map<SummaryName, std::unique_ptr<SummaryView>> Views;
-  bool WasRun = false;
-
 public:
   explicit LUSummaryConsumer(std::unique_ptr<LUSummary> LU)
       : LU(std::move(LU)) {}
 
   /// Instantiates a builder for each SummaryName present in the LUSummary,
-  /// delivers its entities, finalizes it, and stores the resulting view.
-  /// Each builder is fully processed (addSummary → finalize → getView) before
-  /// the next SummaryName is visited. Builders are discarded on return.
+  /// delivers its entities, finalizes it, and stores the resulting view. Each
+  /// builder is fully processed before the next SummaryName is visited.
+  /// Builders are discarded on return.
   ///
   /// \pre Must be called exactly once.
   void run();
@@ -58,6 +45,11 @@ class LUSummaryConsumer final {
   /// Returns nullptr if no builder for \p ViewT was registered or run() has
   /// not been called. A second call for the same ViewT also returns nullptr.
   template <typename ViewT> [[nodiscard]] std::unique_ptr<ViewT> getView() {
+    static_assert(std::is_base_of_v<SummaryView, ViewT>,
+                  "ViewT must derive from SummaryView");
+    static_assert(HasSummaryName<ViewT>::value,
+                  "ViewT must have a static summaryName() method");
+
     auto It = Views.find(ViewT::summaryName());
     if (It == Views.end()) {
       return nullptr;
@@ -70,9 +62,10 @@ class LUSummaryConsumer final {
 private:
   using EntityDataMap = std::map<EntityId, std::unique_ptr<EntitySummary>>;
 
-  /// Processes all entities for a single SummaryName: instantiates the
-  /// registered builder, delivers entities, finalizes, and stores the view.
-  /// Does nothing if no builder is registered for \p SN.
+  std::unique_ptr<LUSummary> LU;
+  std::map<SummaryName, std::unique_ptr<SummaryView>> Views;
+  bool WasRun = false;
+
   void run(const SummaryName &SN, EntityDataMap &Data);
 };
 
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h
index 46778e94976f3..dfbdab4814ac7 100644
--- a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h
@@ -18,9 +18,6 @@
 namespace clang::ssaf {
 
 /// Abstract base class for whole-program analysis views.
-///
-/// Concrete view classes inherit from this and add a static
-/// \c summaryName() method along with their analysis-specific query API.
 class SummaryView {
 public:
   virtual ~SummaryView() = default;
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h
index c71652662f4d9..15c40236c0b99 100644
--- a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h
@@ -10,44 +10,33 @@
 // registry and LUSummaryConsumer) and the typed intermediate template
 // SummaryViewBuilder<ViewT, SummaryT> that concrete builders inherit from.
 //
-// To implement a view builder, inherit from SummaryViewBuilder:
-//
-//   class MyViewBuilder
-//       : public SummaryViewBuilder<MyView, MyEntitySummary> {
-//   public:
-//     void addSummary(EntityId Id,
-//                     std::unique_ptr<MyEntitySummary> Summary) override {
-//       // accumulate into getView()
-//     }
-//     // summaryName() and getView() && provided by the intermediate.
-//     // override finalize() if post-processing is needed.
-//   };
-//
 //===----------------------------------------------------------------------===//
 
 #ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDER_H
 #define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDER_H
 
 #include "clang/Analysis/Scalable/Model/EntityId.h"
-#include "clang/Analysis/Scalable/Model/SummaryName.h"
 #include "clang/Analysis/Scalable/SummaryView/SummaryView.h"
+#include "clang/Analysis/Scalable/SummaryView/SummaryViewTraits.h"
 #include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
 #include <memory>
 
 namespace clang::ssaf {
 
+class LUSummaryConsumer;
+
 /// Abstract base class for all summary view builders.
 ///
 /// Known to the registry and LUSummaryConsumer. Receives entities one at a
 /// time via \c addSummary(), is finalized via \c finalize(), and transfers
-/// ownership of the built view via \c getView() &&.
+/// ownership of the built view via \c getView().
 class SummaryViewBuilderBase {
+  friend class LUSummaryConsumer;
+
 public:
   virtual ~SummaryViewBuilderBase() = default;
 
-  /// Returns the SummaryName this builder handles.
-  virtual SummaryName summaryName() const = 0;
-
+private:
   /// Called once per entity belonging to this builder's analysis.
   /// Takes ownership of the summary data.
   virtual void addSummary(EntityId Id,
@@ -56,41 +45,40 @@ class SummaryViewBuilderBase {
   /// Called after all entities have been added.
   virtual void finalize() {}
 
-  /// Transfers ownership of the built view (type-erased).
-  /// Called by LUSummaryConsumer after finalize(). The rvalue ref-qualifier
-  /// enforces single use — the builder cannot be accessed after this call.
+  /// Transfers ownership of the built view. Called by LUSummaryConsumer after
+  /// finalize(). The rvalue ref-qualifier enforces single use — the builder
+  /// cannot be accessed after this call.
   virtual std::unique_ptr<SummaryView> getView() && = 0;
 };
 
 /// Typed intermediate template that concrete builders inherit from.
-///
-/// Provides:
-/// - \c summaryName() derived from \c ViewT::summaryName().
-/// - \c getView() && which moves out the built view.
-/// - A protected \c getView() accessor for use during construction.
-/// - NVI dispatch: seals the base \c addSummary(EntityId, EntitySummary) as
-///   \c final, downcasts, and dispatches to the typed overload.
-///
 /// Concrete builders only need to implement the typed
 /// \c addSummary(EntityId, unique_ptr<SummaryT>) overload.
 template <typename ViewT, typename SummaryT>
 class SummaryViewBuilder : public SummaryViewBuilderBase {
-  std::unique_ptr<ViewT> View;
+  static_assert(std::is_base_of_v<SummaryView, ViewT>,
+                "ViewT must derive from SummaryView");
+  static_assert(HasSummaryName<ViewT>::value,
+                "ViewT must have a static summaryName() method");
 
-protected:
-  ViewT &getView() & { return *View; }
+  std::unique_ptr<ViewT> View;
 
 public:
   SummaryViewBuilder() : View(std::make_unique<ViewT>()) {}
 
-  SummaryName summaryName() const override { return ViewT::summaryName(); }
-
-  std::unique_ptr<SummaryView> getView() && override { return std::move(View); }
+  /// Returns the SummaryName of the view this builder produces.
+  /// Used by SummaryViewBuilderRegistry::Add to derive the registry entry name.
+  static SummaryName summaryName() { return ViewT::summaryName(); }
 
   /// Typed customization point — concrete builders override this.
   virtual void addSummary(EntityId Id, std::unique_ptr<SummaryT> Summary) = 0;
 
+protected:
+  ViewT &getView() & { return *View; }
+
 private:
+  std::unique_ptr<SummaryView> getView() && override { return std::move(View); }
+
   /// Seals the base overload, downcasts, and dispatches to the typed overload.
   void addSummary(EntityId Id, std::unique_ptr<EntitySummary> Summary) final {
     addSummary(Id, std::unique_ptr<SummaryT>(
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h
index d3fc08a71a446..1cfe6fa730eec 100644
--- a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h
@@ -6,14 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 //
-// Registry for SummaryViewBuilders and helper functions.
-//
-// To register a builder, add this to the builder's translation unit:
-//
-//   static SummaryViewBuilderRegistry::Add<MyViewBuilder>
-//       Register("MyAnalysis", "View builder for MyAnalysis");
-//
-// The registry entry name must match MyView::summaryName().
+// Registry for SummaryViewBuilders.
 //
 //===----------------------------------------------------------------------===//
 
@@ -21,23 +14,40 @@
 #define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDERREGISTRY_H
 
 #include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h"
-#include "clang/Support/Compiler.h"
-#include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Registry.h"
+#include <memory>
 
 namespace clang::ssaf {
 
-/// Check if a SummaryViewBuilder was registered with a given name.
-bool isSummaryViewBuilderRegistered(llvm::StringRef Name);
-
-/// Registry for adding new SummaryViewBuilder implementations.
-using SummaryViewBuilderRegistry = llvm::Registry<SummaryViewBuilderBase>;
+/// Registry for SummaryViewBuilder implementations.
+///
+/// Provides an Add helper that derives the registry entry name from
+/// BuilderT::summaryName(), eliminating the possibility of registering a
+/// builder under the wrong name.
+class SummaryViewBuilderRegistry {
+  using RegistryT = llvm::Registry<SummaryViewBuilderBase>;
+
+public:
+  /// Registers \p BuilderT under the name returned by
+  /// \c BuilderT::summaryName(). Only a description is required.
+  template <typename BuilderT> struct Add {
+    explicit Add(llvm::StringRef Desc)
+        : Name(BuilderT::summaryName().str().str()), Node(Name, Desc) {}
+
+  private:
+    std::string Name;
+    RegistryT::Add<BuilderT> Node;
+  };
+
+  /// Returns true if a builder is registered under \p Name.
+  static bool isRegistered(llvm::StringRef Name);
+
+  /// Instantiates the builder registered under \p Name, or returns nullptr
+  /// if no such builder is registered.
+  static std::unique_ptr<SummaryViewBuilderBase>
+  instantiate(llvm::StringRef Name);
+};
 
 } // namespace clang::ssaf
 
-namespace llvm {
-extern template class CLANG_TEMPLATE_ABI
-    Registry<clang::ssaf::SummaryViewBuilderBase>;
-} // namespace llvm
-
 #endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDERREGISTRY_H
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewTraits.h b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewTraits.h
new file mode 100644
index 0000000000000..542714b8b21a7
--- /dev/null
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewTraits.h
@@ -0,0 +1,33 @@
+//===- SummaryViewTraits.h ------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Type traits for SummaryView subclasses.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWTRAITS_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWTRAITS_H
+
+#include "clang/Analysis/Scalable/Model/SummaryName.h"
+#include <type_traits>
+
+namespace clang::ssaf {
+
+/// Type trait that checks whether \p T has a static summaryName() method
+/// returning SummaryName. Used to enforce the convention on SummaryView
+/// subclasses at instantiation time.
+template <typename T, typename = void>
+struct HasSummaryName : std::false_type {};
+
+template <typename T>
+struct HasSummaryName<T, std::void_t<decltype(T::summaryName())>>
+    : std::is_same<decltype(T::summaryName()), SummaryName> {};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWTRAITS_H
diff --git a/clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp b/clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp
index a3db29be840d6..c742879c9eb93 100644
--- a/clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp
+++ b/clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp
@@ -14,25 +14,12 @@
 using namespace clang;
 using namespace ssaf;
 
-static std::unique_ptr<SummaryViewBuilderBase>
-instantiateBuilder(const SummaryName &SN) {
-  for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
-    if (Entry.getName() == SN.str()) {
-      return Entry.instantiate();
-    }
-  }
-  return nullptr;
-}
-
 void LUSummaryConsumer::run(const SummaryName &SN, EntityDataMap &Data) {
-  auto Builder = instantiateBuilder(SN);
+  auto Builder = SummaryViewBuilderRegistry::instantiate(SN.str());
   if (!Builder) {
     return;
   }
 
-  assert(Builder->summaryName() == SN &&
-         "registry entry name must match SummaryViewBuilder::summaryName()");
-
   for (auto &[Id, Summary] : Data) {
     Builder->addSummary(Id, std::move(Summary));
   }
diff --git a/clang/lib/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.cpp b/clang/lib/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.cpp
index eb64394ba4401..7d7ef13a70c01 100644
--- a/clang/lib/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.cpp
+++ b/clang/lib/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.cpp
@@ -11,10 +11,21 @@
 using namespace clang;
 using namespace ssaf;
 
-LLVM_INSTANTIATE_REGISTRY(SummaryViewBuilderRegistry)
+using RegistryT = llvm::Registry<SummaryViewBuilderBase>;
+LLVM_INSTANTIATE_REGISTRY(RegistryT)
 
-bool ssaf::isSummaryViewBuilderRegistered(llvm::StringRef Name) {
-  for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
+std::unique_ptr<SummaryViewBuilderBase>
+SummaryViewBuilderRegistry::instantiate(llvm::StringRef Name) {
+  for (const auto &Entry : RegistryT::entries()) {
+    if (Entry.getName() == Name) {
+      return Entry.instantiate();
+    }
+  }
+  return nullptr;
+}
+
+bool SummaryViewBuilderRegistry::isRegistered(llvm::StringRef Name) {
+  for (const auto &Entry : RegistryT::entries()) {
     if (Entry.getName() == Name) {
       return true;
     }
diff --git a/clang/unittests/Analysis/Scalable/CMakeLists.txt b/clang/unittests/Analysis/Scalable/CMakeLists.txt
index 41e99dcca5fd2..7af03a9aec1c2 100644
--- a/clang/unittests/Analysis/Scalable/CMakeLists.txt
+++ b/clang/unittests/Analysis/Scalable/CMakeLists.txt
@@ -19,8 +19,7 @@ add_distinct_clang_unittest(ClangScalableAnalysisTests
   Serialization/JSONFormatTest/LUSummaryTest.cpp
   Serialization/JSONFormatTest/TUSummaryTest.cpp
   SummaryNameTest.cpp
-  SummaryView/LUSummaryConsumerTest.cpp
-  SummaryView/SummaryViewBuilderRegistryTest.cpp
+  SummaryView/SummaryViewTest.cpp
   TestFixture.cpp
   TUSummaryBuilderTest.cpp
 
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/LUSummaryConsumerTest.cpp b/clang/unittests/Analysis/Scalable/SummaryView/LUSummaryConsumerTest.cpp
deleted file mode 100644
index 3499d5bda1847..0000000000000
--- a/clang/unittests/Analysis/Scalable/SummaryView/LUSummaryConsumerTest.cpp
+++ /dev/null
@@ -1,279 +0,0 @@
-//===- LUSummaryConsumerTest.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/SummaryView/LUSummaryConsumer.h"
-#include "../TestFixture.h"
-#include "clang/Analysis/Scalable/EntityLinker/LUSummary.h"
-#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/EntityName.h"
-#include "clang/Analysis/Scalable/Model/SummaryName.h"
-#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
-#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
-#include "gtest/gtest.h"
-#include <algorithm>
-#include <memory>
-#include <utility>
-#include <vector>
-
-using namespace clang;
-using namespace ssaf;
-
-namespace {
-
-// ---------------------------------------------------------------------------
-// Instance counter
-// ---------------------------------------------------------------------------
-
-static int NextSummaryInstanceId = 0;
-
-// ---------------------------------------------------------------------------
-// Entity summaries
-// ---------------------------------------------------------------------------
-
-class Analysis1EntitySummary : public EntitySummary {
-public:
-  int InstanceId = NextSummaryInstanceId++;
-  SummaryName getSummaryName() const override {
-    return SummaryName("Analysis1");
-  }
-};
-
-class Analysis2EntitySummary : public EntitySummary {
-public:
-  int InstanceId = NextSummaryInstanceId++;
-  SummaryName getSummaryName() const override {
-    return SummaryName("Analysis2");
-  }
-};
-
-class Analysis4EntitySummary : public EntitySummary {
-public:
-  int InstanceId = NextSummaryInstanceId++;
-  SummaryName getSummaryName() const override {
-    return SummaryName("Analysis4");
-  }
-};
-
-// ---------------------------------------------------------------------------
-// Views
-// ---------------------------------------------------------------------------
-
-class Analysis1View : public SummaryView {
-public:
-  static SummaryName summaryName() { return SummaryName("Analysis1"); }
-  std::vector<std::pair<EntityId, int>> Entries;
-  bool WasFinalized = false;
-};
-
-class Analysis2View : public SummaryView {
-public:
-  static SummaryName summaryName() { return SummaryName("Analysis2"); }
-  std::vector<std::pair<EntityId, int>> Entries;
-  bool WasFinalized = false;
-};
-
-// No builder or registration for Analysis3. Data for Analysis3 is inserted
-// into the LUSummary to verify the consumer silently skips it.
-class Analysis3View : public SummaryView {
-public:
-  static SummaryName summaryName() { return SummaryName("Analysis3"); }
-};
-
-// Analysis4 has a registered builder but no data is inserted into the
-// LUSummary, so the builder is never invoked and getView returns nullptr.
-class Analysis4View : public SummaryView {
-public:
-  static SummaryName summaryName() { return SummaryName("Analysis4"); }
-};
-
-// ---------------------------------------------------------------------------
-// Builder destruction flags (reset in SetUp)
-// ---------------------------------------------------------------------------
-
-static bool Analysis1BuilderWasDestroyed = false;
-static bool Analysis2BuilderWasDestroyed = false;
-static bool Analysis4BuilderWasDestroyed = false;
-
-// ---------------------------------------------------------------------------
-// Builders
-// ---------------------------------------------------------------------------
-
-class Analysis1Builder
-    : public SummaryViewBuilder<Analysis1View, Analysis1EntitySummary> {
-public:
-  ~Analysis1Builder() { Analysis1BuilderWasDestroyed = true; }
-
-  void addSummary(EntityId Id,
-                  std::unique_ptr<Analysis1EntitySummary> S) override {
-    getView().Entries.push_back({Id, S->InstanceId});
-  }
-
-  void finalize() override { getView().WasFinalized = true; }
-};
-
-static SummaryViewBuilderRegistry::Add<Analysis1Builder>
-    RegAnalysis1("Analysis1", "Builder for Analysis1");
-
-class Analysis2Builder
-    : public SummaryViewBuilder<Analysis2View, Analysis2EntitySummary> {
-public:
-  ~Analysis2Builder() { Analysis2BuilderWasDestroyed = true; }
-
-  void addSummary(EntityId Id,
-                  std::unique_ptr<Analysis2EntitySummary> S) override {
-    getView().Entries.push_back({Id, S->InstanceId});
-  }
-
-  void finalize() override { getView().WasFinalized = true; }
-};
-
-static SummaryViewBuilderRegistry::Add<Analysis2Builder>
-    RegAnalysis2("Analysis2", "Builder for Analysis2");
-
-class Analysis4Builder
-    : public SummaryViewBuilder<Analysis4View, Analysis4EntitySummary> {
-public:
-  ~Analysis4Builder() { Analysis4BuilderWasDestroyed = true; }
-
-  void addSummary(EntityId Id,
-                  std::unique_ptr<Analysis4EntitySummary> S) override {
-    getView().Entries.push_back({Id, S->InstanceId});
-  }
-
-  void finalize() override { getView().WasFinalized = true; }
-};
-
-static SummaryViewBuilderRegistry::Add<Analysis4Builder>
-    RegAnalysis4("Analysis4", "Builder for Analysis4");
-
-// ---------------------------------------------------------------------------
-// Fixture
-// ---------------------------------------------------------------------------
-
-class LUSummaryConsumerTest : public TestFixture {
-protected:
-  static constexpr EntityLinkage ExternalLinkage =
-      EntityLinkage(EntityLinkageType::External);
-
-  void SetUp() override {
-    NextSummaryInstanceId = 0;
-    Analysis1BuilderWasDestroyed = false;
-    Analysis2BuilderWasDestroyed = false;
-    Analysis4BuilderWasDestroyed = false;
-  }
-
-  std::unique_ptr<LUSummary> makeLUSummary() {
-    NestedBuildNamespace NS(
-        {BuildNamespace(BuildNamespaceKind::LinkUnit, "TestLU")});
-    return std::make_unique<LUSummary>(std::move(NS));
-  }
-
-  EntityId addEntity(LUSummary &LU, llvm::StringRef USR) {
-    NestedBuildNamespace NS(
-        {BuildNamespace(BuildNamespaceKind::LinkUnit, "TestLU")});
-    EntityName Name(USR.str(), "", NS);
-    EntityId Id = getIdTable(LU).getId(Name);
-    getLinkageTable(LU).insert({Id, ExternalLinkage});
-    return Id;
-  }
-
-  static bool hasEntry(const std::vector<std::pair<EntityId, int>> &Entries,
-                       EntityId Id, int InstanceId) {
-    return std::find(Entries.begin(), Entries.end(),
-                     std::make_pair(Id, InstanceId)) != Entries.end();
-  }
-
-  /// Inserts a freshly constructed SummaryT for the given entity and returns
-  /// the summary's InstanceId so the test can verify delivery later.
-  template <typename SummaryT>
-  int insertSummary(LUSummary &LU, llvm::StringRef SN, EntityId Id) {
-    auto S = std::make_unique<SummaryT>();
-    int InstanceId = S->InstanceId;
-    getData(LU)[SummaryName(SN.str())][Id] = std::move(S);
-    return InstanceId;
-  }
-};
-
-// ---------------------------------------------------------------------------
-// Tests
-// ---------------------------------------------------------------------------
-
-TEST_F(LUSummaryConsumerTest, Run) {
-  auto LU = makeLUSummary();
-  const auto E1 = addEntity(*LU, "Entity1");
-  const auto E2 = addEntity(*LU, "Entity2");
-  const auto E3 = addEntity(*LU, "Entity3");
-
-  int s1a = insertSummary<Analysis1EntitySummary>(*LU, "Analysis1", E1);
-  int s1b = insertSummary<Analysis1EntitySummary>(*LU, "Analysis1", E2);
-  int s2a = insertSummary<Analysis2EntitySummary>(*LU, "Analysis2", E2);
-  int s2b = insertSummary<Analysis2EntitySummary>(*LU, "Analysis2", E3);
-
-  // no registered builder
-  [[maybe_unused]] int s3a =
-      insertSummary<Analysis1EntitySummary>(*LU, "Analysis3", E1);
-
-  LUSummaryConsumer Consumer(std::move(LU));
-  Consumer.run();
-
-  // Analysis1
-  {
-    auto View1 = Consumer.getView<Analysis1View>();
-    ASSERT_NE(View1, nullptr);
-
-    // getView ownership transfer — second call returns nullptr
-    EXPECT_EQ(Consumer.getView<Analysis1View>(), nullptr);
-
-    EXPECT_EQ(View1->Entries.size(), 2u);
-    EXPECT_TRUE(hasEntry(View1->Entries, E1, s1a));
-    EXPECT_TRUE(hasEntry(View1->Entries, E2, s1b));
-
-    EXPECT_TRUE(View1->WasFinalized);
-
-    // Builder lifetime
-    EXPECT_TRUE(Analysis1BuilderWasDestroyed);
-  }
-
-  // Analysis2
-  {
-    auto View2 = Consumer.getView<Analysis2View>();
-    ASSERT_NE(View2, nullptr);
-
-    // getView ownership transfer — second call returns nullptr
-    EXPECT_EQ(Consumer.getView<Analysis2View>(), nullptr);
-
-    EXPECT_EQ(View2->Entries.size(), 2u);
-    EXPECT_TRUE(hasEntry(View2->Entries, E2, s2a));
-    EXPECT_TRUE(hasEntry(View2->Entries, E3, s2b));
-
-    EXPECT_TRUE(View2->WasFinalized);
-
-    // Builder lifetime
-    EXPECT_TRUE(Analysis2BuilderWasDestroyed);
-  }
-
-  // Analysis 3
-  {
-    // Unregistered builder — Analysis3 data silently skipped
-    EXPECT_EQ(Consumer.getView<Analysis3View>(), nullptr);
-  }
-
-  // Analysis4
-  {
-    // Registered builder but no data in LUSummary — builder never invoked
-    EXPECT_EQ(Consumer.getView<Analysis4View>(), nullptr);
-
-    // Builder lifetime
-    EXPECT_FALSE(Analysis4BuilderWasDestroyed);
-  }
-}
-
-} // namespace
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp b/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp
deleted file mode 100644
index 30d3606247b2e..0000000000000
--- a/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-//===- SummaryViewBuilderRegistryTest.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/SummaryView/SummaryViewBuilderRegistry.h"
-#include "llvm/ADT/StringRef.h"
-#include "gtest/gtest.h"
-#include <memory>
-#include <set>
-
-using namespace clang;
-using namespace ssaf;
-
-namespace {
-
-class SummaryViewBuilderRegistryTest : public ::testing::Test {};
-
-TEST_F(SummaryViewBuilderRegistryTest, isSummaryViewBuilderRegistered) {
-  EXPECT_FALSE(isSummaryViewBuilderRegistered("Non-existent-builder"));
-  EXPECT_TRUE(isSummaryViewBuilderRegistered("Analysis1"));
-  EXPECT_TRUE(isSummaryViewBuilderRegistered("Analysis2"));
-  EXPECT_TRUE(isSummaryViewBuilderRegistered("Analysis4"));
-}
-
-TEST_F(SummaryViewBuilderRegistryTest, EnumeratingRegistryEntries) {
-  std::set<llvm::StringRef> ActualNames;
-  for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
-    bool Inserted = ActualNames.insert(Entry.getName()).second;
-    EXPECT_TRUE(Inserted);
-  }
-
-  EXPECT_EQ(ActualNames,
-            (std::set<llvm::StringRef>{"Analysis1", "Analysis2", "Analysis4"}));
-}
-
-TEST_F(SummaryViewBuilderRegistryTest, InstantiatingBuilder_Analysis1) {
-  std::unique_ptr<SummaryViewBuilderBase> B;
-  for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
-    if (Entry.getName() == "Analysis1")
-      B = Entry.instantiate();
-  }
-
-  ASSERT_NE(B, nullptr);
-  EXPECT_EQ(B->summaryName(), SummaryName("Analysis1"));
-}
-
-TEST_F(SummaryViewBuilderRegistryTest, InstantiatingBuilder_Analysis2) {
-  std::unique_ptr<SummaryViewBuilderBase> B;
-  for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
-    if (Entry.getName() == "Analysis2")
-      B = Entry.instantiate();
-  }
-
-  ASSERT_NE(B, nullptr);
-  EXPECT_EQ(B->summaryName(), SummaryName("Analysis2"));
-}
-
-} // namespace

>From 883b8f55d425c7cb49cf6a787797de06c3e92f43 Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Wed, 11 Mar 2026 16:18:15 -0700
Subject: [PATCH 5/8] Fix

---
 .../Analysis/Scalable/SummaryView/SummaryViewBuilder.h |  6 ++++--
 .../Scalable/SummaryView/SummaryViewBuilderRegistry.h  | 10 ++++++++++
 .../Scalable/SummaryView/LUSummaryConsumer.cpp         |  1 -
 3 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h
index 15c40236c0b99..14231eec17753 100644
--- a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h
@@ -16,6 +16,7 @@
 #define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDER_H
 
 #include "clang/Analysis/Scalable/Model/EntityId.h"
+#include "clang/Analysis/Scalable/Model/SummaryName.h"
 #include "clang/Analysis/Scalable/SummaryView/SummaryView.h"
 #include "clang/Analysis/Scalable/SummaryView/SummaryViewTraits.h"
 #include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
@@ -52,8 +53,9 @@ class SummaryViewBuilderBase {
 };
 
 /// Typed intermediate template that concrete builders inherit from.
-/// Concrete builders only need to implement the typed
-/// \c addSummary(EntityId, unique_ptr<SummaryT>) overload.
+/// Concrete builders must implement the typed
+/// \c addSummary(EntityId, unique_ptr<SummaryT>) overload, and may override
+/// \c finalize() for any post-processing needed after all entities are added.
 template <typename ViewT, typename SummaryT>
 class SummaryViewBuilder : public SummaryViewBuilderBase {
   static_assert(std::is_base_of_v<SummaryView, ViewT>,
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h
index 1cfe6fa730eec..2c84ee1573874 100644
--- a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h
@@ -8,6 +8,15 @@
 //
 // Registry for SummaryViewBuilders.
 //
+// To register a builder, add a static Add<BuilderT> in the builder's
+// translation unit:
+//
+//   static SummaryViewBuilderRegistry::Add<MyViewBuilder>
+//       Registered("View builder for MyAnalysis");
+//
+// The registry entry name is derived automatically from
+// MyViewBuilder::summaryName(), which returns MyView::summaryName().
+//
 //===----------------------------------------------------------------------===//
 
 #ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDERREGISTRY_H
@@ -16,6 +25,7 @@
 #include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h"
 #include "llvm/Support/Registry.h"
 #include <memory>
+#include <string>
 
 namespace clang::ssaf {
 
diff --git a/clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp b/clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp
index c742879c9eb93..1be43eee5e923 100644
--- a/clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp
+++ b/clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp
@@ -9,7 +9,6 @@
 #include "clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h"
 #include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
 #include <cassert>
-#include <memory>
 
 using namespace clang;
 using namespace ssaf;

>From 37098472c07db53b56ca3bda2ddc37bba83b38d0 Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Wed, 11 Mar 2026 16:23:10 -0700
Subject: [PATCH 6/8] Turns out you need the test file to actually run the
 tests on CI.

---
 .../Scalable/SummaryView/SummaryViewTest.cpp  | 295 ++++++++++++++++++
 1 file changed, 295 insertions(+)
 create mode 100644 clang/unittests/Analysis/Scalable/SummaryView/SummaryViewTest.cpp

diff --git a/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewTest.cpp b/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewTest.cpp
new file mode 100644
index 0000000000000..3b89e2f6d6966
--- /dev/null
+++ b/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewTest.cpp
@@ -0,0 +1,295 @@
+//===- LUSummaryConsumerTest.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/EntityLinker/LUSummary.h"
+#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/EntityName.h"
+#include "clang/Analysis/Scalable/Model/SummaryName.h"
+#include "clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h"
+#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
+#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
+#include "gtest/gtest.h"
+#include <algorithm>
+#include <memory>
+#include <utility>
+#include <vector>
+
+using namespace clang;
+using namespace ssaf;
+
+namespace {
+
+// ---------------------------------------------------------------------------
+// Instance counter
+// ---------------------------------------------------------------------------
+
+static int NextSummaryInstanceId = 0;
+
+// ---------------------------------------------------------------------------
+// Entity summaries
+// ---------------------------------------------------------------------------
+
+class Analysis1EntitySummary : public EntitySummary {
+public:
+  int InstanceId = NextSummaryInstanceId++;
+  SummaryName getSummaryName() const override {
+    return SummaryName("Analysis1");
+  }
+};
+
+class Analysis2EntitySummary : public EntitySummary {
+public:
+  int InstanceId = NextSummaryInstanceId++;
+  SummaryName getSummaryName() const override {
+    return SummaryName("Analysis2");
+  }
+};
+
+class Analysis4EntitySummary : public EntitySummary {
+public:
+  int InstanceId = NextSummaryInstanceId++;
+  SummaryName getSummaryName() const override {
+    return SummaryName("Analysis4");
+  }
+};
+
+// ---------------------------------------------------------------------------
+// Views
+// ---------------------------------------------------------------------------
+
+class Analysis1View : public SummaryView {
+public:
+  static SummaryName summaryName() { return SummaryName("Analysis1"); }
+  std::vector<std::pair<EntityId, int>> Entries;
+  bool WasFinalized = false;
+};
+
+class Analysis2View : public SummaryView {
+public:
+  static SummaryName summaryName() { return SummaryName("Analysis2"); }
+  std::vector<std::pair<EntityId, int>> Entries;
+  bool WasFinalized = false;
+};
+
+// No builder or registration for Analysis3. Data for Analysis3 is inserted
+// into the LUSummary to verify the consumer silently skips it.
+class Analysis3View : public SummaryView {
+public:
+  static SummaryName summaryName() { return SummaryName("Analysis3"); }
+};
+
+// Analysis4 has a registered builder but no data is inserted into the
+// LUSummary, so the builder is never invoked and getView returns nullptr.
+class Analysis4View : public SummaryView {
+public:
+  static SummaryName summaryName() { return SummaryName("Analysis4"); }
+  std::vector<std::pair<EntityId, int>> Entries;
+  bool WasFinalized = false;
+};
+
+// ---------------------------------------------------------------------------
+// Builder destruction flags (reset in SetUp)
+// ---------------------------------------------------------------------------
+
+static bool Analysis1BuilderWasDestroyed = false;
+static bool Analysis2BuilderWasDestroyed = false;
+static bool Analysis4BuilderWasDestroyed = false;
+
+// ---------------------------------------------------------------------------
+// Builders
+// ---------------------------------------------------------------------------
+
+class Analysis1Builder
+    : public SummaryViewBuilder<Analysis1View, Analysis1EntitySummary> {
+public:
+  ~Analysis1Builder() { Analysis1BuilderWasDestroyed = true; }
+
+  void addSummary(EntityId Id,
+                  std::unique_ptr<Analysis1EntitySummary> S) override {
+    getView().Entries.push_back({Id, S->InstanceId});
+  }
+
+  void finalize() override { getView().WasFinalized = true; }
+};
+
+static SummaryViewBuilderRegistry::Add<Analysis1Builder>
+    RegAnalysis1("Builder for Analysis1");
+
+class Analysis2Builder
+    : public SummaryViewBuilder<Analysis2View, Analysis2EntitySummary> {
+public:
+  ~Analysis2Builder() { Analysis2BuilderWasDestroyed = true; }
+
+  void addSummary(EntityId Id,
+                  std::unique_ptr<Analysis2EntitySummary> S) override {
+    getView().Entries.push_back({Id, S->InstanceId});
+  }
+
+  void finalize() override { getView().WasFinalized = true; }
+};
+
+static SummaryViewBuilderRegistry::Add<Analysis2Builder>
+    RegAnalysis2("Builder for Analysis2");
+
+class Analysis4Builder
+    : public SummaryViewBuilder<Analysis4View, Analysis4EntitySummary> {
+public:
+  ~Analysis4Builder() { Analysis4BuilderWasDestroyed = true; }
+
+  void addSummary(EntityId Id,
+                  std::unique_ptr<Analysis4EntitySummary> S) override {
+    getView().Entries.push_back({Id, S->InstanceId});
+  }
+
+  void finalize() override { getView().WasFinalized = true; }
+};
+
+static SummaryViewBuilderRegistry::Add<Analysis4Builder>
+    RegAnalysis4("Builder for Analysis4");
+
+// ---------------------------------------------------------------------------
+// Fixture
+// ---------------------------------------------------------------------------
+
+class LUSummaryConsumerTest : public TestFixture {
+protected:
+  static constexpr EntityLinkage ExternalLinkage =
+      EntityLinkage(EntityLinkageType::External);
+
+  void SetUp() override {
+    NextSummaryInstanceId = 0;
+    Analysis1BuilderWasDestroyed = false;
+    Analysis2BuilderWasDestroyed = false;
+    Analysis4BuilderWasDestroyed = false;
+  }
+
+  std::unique_ptr<LUSummary> makeLUSummary() {
+    NestedBuildNamespace NS(
+        {BuildNamespace(BuildNamespaceKind::LinkUnit, "TestLU")});
+    return std::make_unique<LUSummary>(std::move(NS));
+  }
+
+  EntityId addEntity(LUSummary &LU, llvm::StringRef USR) {
+    NestedBuildNamespace NS(
+        {BuildNamespace(BuildNamespaceKind::LinkUnit, "TestLU")});
+    EntityName Name(USR.str(), "", NS);
+    EntityId Id = getIdTable(LU).getId(Name);
+    getLinkageTable(LU).insert({Id, ExternalLinkage});
+    return Id;
+  }
+
+  static bool hasEntry(const std::vector<std::pair<EntityId, int>> &Entries,
+                       EntityId Id, int InstanceId) {
+    return std::find(Entries.begin(), Entries.end(),
+                     std::make_pair(Id, InstanceId)) != Entries.end();
+  }
+
+  /// Inserts a freshly constructed SummaryT for the given entity and returns
+  /// the summary's InstanceId so the test can verify delivery later.
+  template <typename SummaryT>
+  int insertSummary(LUSummary &LU, llvm::StringRef SN, EntityId Id) {
+    auto S = std::make_unique<SummaryT>();
+    int InstanceId = S->InstanceId;
+    getData(LU)[SummaryName(SN.str())][Id] = std::move(S);
+    return InstanceId;
+  }
+};
+
+// ---------------------------------------------------------------------------
+// Tests
+// ---------------------------------------------------------------------------
+
+TEST(SummaryViewBuilderRegistryTest, BuilderIsRegistered) {
+  EXPECT_FALSE(SummaryViewBuilderRegistry::isRegistered("Analysis10"));
+  EXPECT_TRUE(SummaryViewBuilderRegistry::isRegistered("Analysis1"));
+  EXPECT_TRUE(SummaryViewBuilderRegistry::isRegistered("Analysis2"));
+  EXPECT_TRUE(SummaryViewBuilderRegistry::isRegistered("Analysis4"));
+}
+
+TEST(SummaryViewBuilderRegistryTest, BuilderCanBeInstantiated) {
+  EXPECT_EQ(SummaryViewBuilderRegistry::instantiate("Analysis10"), nullptr);
+  EXPECT_NE(SummaryViewBuilderRegistry::instantiate("Analysis1"), nullptr);
+  EXPECT_NE(SummaryViewBuilderRegistry::instantiate("Analysis2"), nullptr);
+  EXPECT_NE(SummaryViewBuilderRegistry::instantiate("Analysis4"), nullptr);
+}
+
+TEST_F(LUSummaryConsumerTest, Run) {
+  auto LU = makeLUSummary();
+  const auto E1 = addEntity(*LU, "Entity1");
+  const auto E2 = addEntity(*LU, "Entity2");
+  const auto E3 = addEntity(*LU, "Entity3");
+
+  int s1a = insertSummary<Analysis1EntitySummary>(*LU, "Analysis1", E1);
+  int s1b = insertSummary<Analysis1EntitySummary>(*LU, "Analysis1", E2);
+  int s2a = insertSummary<Analysis2EntitySummary>(*LU, "Analysis2", E2);
+  int s2b = insertSummary<Analysis2EntitySummary>(*LU, "Analysis2", E3);
+
+  // no registered builder
+  [[maybe_unused]] int s3a =
+      insertSummary<Analysis1EntitySummary>(*LU, "Analysis3", E1);
+
+  LUSummaryConsumer Consumer(std::move(LU));
+  Consumer.run();
+
+  // Analysis1
+  {
+    auto View1 = Consumer.getView<Analysis1View>();
+    ASSERT_NE(View1, nullptr);
+
+    // getView ownership transfer — second call returns nullptr
+    EXPECT_EQ(Consumer.getView<Analysis1View>(), nullptr);
+
+    EXPECT_EQ(View1->Entries.size(), 2u);
+    EXPECT_TRUE(hasEntry(View1->Entries, E1, s1a));
+    EXPECT_TRUE(hasEntry(View1->Entries, E2, s1b));
+
+    EXPECT_TRUE(View1->WasFinalized);
+
+    // Builder lifetime
+    EXPECT_TRUE(Analysis1BuilderWasDestroyed);
+  }
+
+  // Analysis2
+  {
+    auto View2 = Consumer.getView<Analysis2View>();
+    ASSERT_NE(View2, nullptr);
+
+    // getView ownership transfer — second call returns nullptr
+    EXPECT_EQ(Consumer.getView<Analysis2View>(), nullptr);
+
+    EXPECT_EQ(View2->Entries.size(), 2u);
+    EXPECT_TRUE(hasEntry(View2->Entries, E2, s2a));
+    EXPECT_TRUE(hasEntry(View2->Entries, E3, s2b));
+
+    EXPECT_TRUE(View2->WasFinalized);
+
+    // Builder lifetime
+    EXPECT_TRUE(Analysis2BuilderWasDestroyed);
+  }
+
+  // Analysis 3
+  {
+    // Unregistered builder — Analysis3 data silently skipped
+    EXPECT_EQ(Consumer.getView<Analysis3View>(), nullptr);
+  }
+
+  // Analysis4
+  {
+    // Registered builder but no data in LUSummary — builder never invoked
+    EXPECT_EQ(Consumer.getView<Analysis4View>(), nullptr);
+
+    // Builder lifetime
+    EXPECT_FALSE(Analysis4BuilderWasDestroyed);
+  }
+}
+
+} // namespace

>From 9ec4a63f5c732bef2e5c342cf03a2dc2abd4bb9e Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Wed, 11 Mar 2026 18:56:10 -0700
Subject: [PATCH 7/8] Rename

---
 .../LUSummaryConsumer.h                       |  46 ++++----
 .../SummaryData.h}                            |  16 +--
 .../SummaryDataBuilder.h}                     |  54 +++++-----
 .../SummaryDataBuilderRegistry.h}             |  26 ++---
 .../SummaryDataTraits.h}                      |  12 +--
 clang/lib/Analysis/Scalable/CMakeLists.txt    |   4 +-
 .../LUSummaryConsumer.cpp                     |  16 +--
 .../SummaryDataBuilderRegistry.cpp}           |  12 +--
 .../Analysis/Scalable/CMakeLists.txt          |   2 +-
 .../SummaryDataTest.cpp}                      | 100 +++++++++---------
 10 files changed, 144 insertions(+), 144 deletions(-)
 rename clang/include/clang/Analysis/Scalable/{SummaryView => SummaryData}/LUSummaryConsumer.h (55%)
 rename clang/include/clang/Analysis/Scalable/{SummaryView/SummaryView.h => SummaryData/SummaryData.h} (56%)
 rename clang/include/clang/Analysis/Scalable/{SummaryView/SummaryViewBuilder.h => SummaryData/SummaryDataBuilder.h} (58%)
 rename clang/include/clang/Analysis/Scalable/{SummaryView/SummaryViewBuilderRegistry.h => SummaryData/SummaryDataBuilderRegistry.h} (67%)
 rename clang/include/clang/Analysis/Scalable/{SummaryView/SummaryViewTraits.h => SummaryData/SummaryDataTraits.h} (75%)
 rename clang/lib/Analysis/Scalable/{SummaryView => SummaryData}/LUSummaryConsumer.cpp (66%)
 rename clang/lib/Analysis/Scalable/{SummaryView/SummaryViewBuilderRegistry.cpp => SummaryData/SummaryDataBuilderRegistry.cpp} (66%)
 rename clang/unittests/Analysis/Scalable/{SummaryView/SummaryViewTest.cpp => SummaryData/SummaryDataTest.cpp} (73%)

diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h b/clang/include/clang/Analysis/Scalable/SummaryData/LUSummaryConsumer.h
similarity index 55%
rename from clang/include/clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h
rename to clang/include/clang/Analysis/Scalable/SummaryData/LUSummaryConsumer.h
index a440f05999e0c..8e93ff025f2f6 100644
--- a/clang/include/clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h
+++ b/clang/include/clang/Analysis/Scalable/SummaryData/LUSummaryConsumer.h
@@ -6,19 +6,19 @@
 //
 //===----------------------------------------------------------------------===//
 //
-// LUSummaryConsumer constructs SummaryView objects by routing LUSummary entity
-// data to the corresponding SummaryViewBuilder objects.
+// LUSummaryConsumer constructs SummaryData objects by routing LUSummary entity
+// data to the corresponding SummaryDataBuilder objects.
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_LUSUMMARYCONSUMER_H
-#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_LUSUMMARYCONSUMER_H
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_LUSUMMARYCONSUMER_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_LUSUMMARYCONSUMER_H
 
 #include "clang/Analysis/Scalable/EntityLinker/LUSummary.h"
 #include "clang/Analysis/Scalable/Model/EntityId.h"
 #include "clang/Analysis/Scalable/Model/SummaryName.h"
-#include "clang/Analysis/Scalable/SummaryView/SummaryView.h"
-#include "clang/Analysis/Scalable/SummaryView/SummaryViewTraits.h"
+#include "clang/Analysis/Scalable/SummaryData/SummaryData.h"
+#include "clang/Analysis/Scalable/SummaryData/SummaryDataTraits.h"
 #include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
 #include <map>
 #include <memory>
@@ -26,44 +26,44 @@
 namespace clang::ssaf {
 
 /// Consumes a LUSummary by dispatching its entity data to all registered
-/// SummaryViewBuilders and collecting the resulting views.
+/// SummaryDataBuilders and collecting the resulting data.
 class LUSummaryConsumer final {
 public:
   explicit LUSummaryConsumer(std::unique_ptr<LUSummary> LU)
       : LU(std::move(LU)) {}
 
   /// Instantiates a builder for each SummaryName present in the LUSummary,
-  /// delivers its entities, finalizes it, and stores the resulting view. Each
+  /// delivers its entities, finalizes it, and stores the resulting data. Each
   /// builder is fully processed before the next SummaryName is visited.
   /// Builders are discarded on return.
   ///
   /// \pre Must be called exactly once.
   void run();
 
-  /// Transfers ownership of the view for \p ViewT to the caller.
+  /// Transfers ownership of the data for \p DataT to the caller.
   ///
-  /// Returns nullptr if no builder for \p ViewT was registered or run() has
-  /// not been called. A second call for the same ViewT also returns nullptr.
-  template <typename ViewT> [[nodiscard]] std::unique_ptr<ViewT> getView() {
-    static_assert(std::is_base_of_v<SummaryView, ViewT>,
-                  "ViewT must derive from SummaryView");
-    static_assert(HasSummaryName<ViewT>::value,
-                  "ViewT must have a static summaryName() method");
+  /// Returns nullptr if no builder for \p DataT was registered or run() has
+  /// not been called. A second call for the same DataT also returns nullptr.
+  template <typename DataT> [[nodiscard]] std::unique_ptr<DataT> getData() {
+    static_assert(std::is_base_of_v<SummaryData, DataT>,
+                  "DataT must derive from SummaryData");
+    static_assert(HasSummaryName<DataT>::value,
+                  "DataT must have a static summaryName() method");
 
-    auto It = Views.find(ViewT::summaryName());
-    if (It == Views.end()) {
+    auto It = Data.find(DataT::summaryName());
+    if (It == Data.end()) {
       return nullptr;
     }
-    auto *RawPtr = static_cast<ViewT *>(It->second.release());
-    Views.erase(It);
-    return std::unique_ptr<ViewT>(RawPtr);
+    auto *RawPtr = static_cast<DataT *>(It->second.release());
+    Data.erase(It);
+    return std::unique_ptr<DataT>(RawPtr);
   }
 
 private:
   using EntityDataMap = std::map<EntityId, std::unique_ptr<EntitySummary>>;
 
   std::unique_ptr<LUSummary> LU;
-  std::map<SummaryName, std::unique_ptr<SummaryView>> Views;
+  std::map<SummaryName, std::unique_ptr<SummaryData>> Data;
   bool WasRun = false;
 
   void run(const SummaryName &SN, EntityDataMap &Data);
@@ -71,4 +71,4 @@ class LUSummaryConsumer final {
 
 } // namespace clang::ssaf
 
-#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_LUSUMMARYCONSUMER_H
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_LUSUMMARYCONSUMER_H
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h b/clang/include/clang/Analysis/Scalable/SummaryData/SummaryData.h
similarity index 56%
rename from clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h
rename to clang/include/clang/Analysis/Scalable/SummaryData/SummaryData.h
index dfbdab4814ac7..6f76a5b47cf7c 100644
--- a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h
+++ b/clang/include/clang/Analysis/Scalable/SummaryData/SummaryData.h
@@ -1,4 +1,4 @@
-//===- SummaryView.h ------------------------------------------------------===//
+//===- SummaryData.h ------------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -6,23 +6,23 @@
 //
 //===----------------------------------------------------------------------===//
 //
-// Abstract base class for all whole-program analysis views built from
+// Abstract base class for all whole-program analysis data built from
 // LUSummary data. Carries no query API — all analysis-specific methods live
 // on concrete subclasses.
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEW_H
-#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEW_H
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATA_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATA_H
 
 namespace clang::ssaf {
 
-/// Abstract base class for whole-program analysis views.
-class SummaryView {
+/// Abstract base class for whole-program analysis data.
+class SummaryData {
 public:
-  virtual ~SummaryView() = default;
+  virtual ~SummaryData() = default;
 };
 
 } // namespace clang::ssaf
 
-#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEW_H
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATA_H
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h b/clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataBuilder.h
similarity index 58%
rename from clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h
rename to clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataBuilder.h
index 14231eec17753..fd68a9f9ea0d2 100644
--- a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h
+++ b/clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataBuilder.h
@@ -1,4 +1,4 @@
-//===- SummaryViewBuilder.h -----------------------------------------------===//
+//===- SummaryDataBuilder.h -----------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -6,19 +6,19 @@
 //
 //===----------------------------------------------------------------------===//
 //
-// This file defines SummaryViewBuilderBase (abstract base known to the
+// This file defines SummaryDataBuilderBase (abstract base known to the
 // registry and LUSummaryConsumer) and the typed intermediate template
-// SummaryViewBuilder<ViewT, SummaryT> that concrete builders inherit from.
+// SummaryDataBuilder<DataT, SummaryT> that concrete builders inherit from.
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDER_H
-#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDER_H
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATABUILDER_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATABUILDER_H
 
 #include "clang/Analysis/Scalable/Model/EntityId.h"
 #include "clang/Analysis/Scalable/Model/SummaryName.h"
-#include "clang/Analysis/Scalable/SummaryView/SummaryView.h"
-#include "clang/Analysis/Scalable/SummaryView/SummaryViewTraits.h"
+#include "clang/Analysis/Scalable/SummaryData/SummaryData.h"
+#include "clang/Analysis/Scalable/SummaryData/SummaryDataTraits.h"
 #include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
 #include <memory>
 
@@ -26,16 +26,16 @@ namespace clang::ssaf {
 
 class LUSummaryConsumer;
 
-/// Abstract base class for all summary view builders.
+/// Abstract base class for all summary data builders.
 ///
 /// Known to the registry and LUSummaryConsumer. Receives entities one at a
 /// time via \c addSummary(), is finalized via \c finalize(), and transfers
-/// ownership of the built view via \c getView().
-class SummaryViewBuilderBase {
+/// ownership of the built data via \c getData().
+class SummaryDataBuilderBase {
   friend class LUSummaryConsumer;
 
 public:
-  virtual ~SummaryViewBuilderBase() = default;
+  virtual ~SummaryDataBuilderBase() = default;
 
 private:
   /// Called once per entity belonging to this builder's analysis.
@@ -46,40 +46,40 @@ class SummaryViewBuilderBase {
   /// Called after all entities have been added.
   virtual void finalize() {}
 
-  /// Transfers ownership of the built view. Called by LUSummaryConsumer after
+  /// Transfers ownership of the built data. Called by LUSummaryConsumer after
   /// finalize(). The rvalue ref-qualifier enforces single use — the builder
   /// cannot be accessed after this call.
-  virtual std::unique_ptr<SummaryView> getView() && = 0;
+  virtual std::unique_ptr<SummaryData> getData() && = 0;
 };
 
 /// Typed intermediate template that concrete builders inherit from.
 /// Concrete builders must implement the typed
 /// \c addSummary(EntityId, unique_ptr<SummaryT>) overload, and may override
 /// \c finalize() for any post-processing needed after all entities are added.
-template <typename ViewT, typename SummaryT>
-class SummaryViewBuilder : public SummaryViewBuilderBase {
-  static_assert(std::is_base_of_v<SummaryView, ViewT>,
-                "ViewT must derive from SummaryView");
-  static_assert(HasSummaryName<ViewT>::value,
-                "ViewT must have a static summaryName() method");
+template <typename DataT, typename SummaryT>
+class SummaryDataBuilder : public SummaryDataBuilderBase {
+  static_assert(std::is_base_of_v<SummaryData, DataT>,
+                "DataT must derive from SummaryData");
+  static_assert(HasSummaryName<DataT>::value,
+                "DataT must have a static summaryName() method");
 
-  std::unique_ptr<ViewT> View;
+  std::unique_ptr<DataT> Data;
 
 public:
-  SummaryViewBuilder() : View(std::make_unique<ViewT>()) {}
+  SummaryDataBuilder() : Data(std::make_unique<DataT>()) {}
 
-  /// Returns the SummaryName of the view this builder produces.
-  /// Used by SummaryViewBuilderRegistry::Add to derive the registry entry name.
-  static SummaryName summaryName() { return ViewT::summaryName(); }
+  /// Returns the SummaryName of the data this builder produces.
+  /// Used by SummaryDataBuilderRegistry::Add to derive the registry entry name.
+  static SummaryName summaryName() { return DataT::summaryName(); }
 
   /// Typed customization point — concrete builders override this.
   virtual void addSummary(EntityId Id, std::unique_ptr<SummaryT> Summary) = 0;
 
 protected:
-  ViewT &getView() & { return *View; }
+  DataT &getData() & { return *Data; }
 
 private:
-  std::unique_ptr<SummaryView> getView() && override { return std::move(View); }
+  std::unique_ptr<SummaryData> getData() && override { return std::move(Data); }
 
   /// Seals the base overload, downcasts, and dispatches to the typed overload.
   void addSummary(EntityId Id, std::unique_ptr<EntitySummary> Summary) final {
@@ -90,4 +90,4 @@ class SummaryViewBuilder : public SummaryViewBuilderBase {
 
 } // namespace clang::ssaf
 
-#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDER_H
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATABUILDER_H
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h b/clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataBuilderRegistry.h
similarity index 67%
rename from clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h
rename to clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataBuilderRegistry.h
index 2c84ee1573874..76e136f8bae40 100644
--- a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h
+++ b/clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataBuilderRegistry.h
@@ -1,4 +1,4 @@
-//===- SummaryViewBuilderRegistry.h ---------------------------------------===//
+//===- SummaryDataBuilderRegistry.h ---------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -6,36 +6,36 @@
 //
 //===----------------------------------------------------------------------===//
 //
-// Registry for SummaryViewBuilders.
+// Registry for SummaryDataBuilders.
 //
 // To register a builder, add a static Add<BuilderT> in the builder's
 // translation unit:
 //
-//   static SummaryViewBuilderRegistry::Add<MyViewBuilder>
-//       Registered("View builder for MyAnalysis");
+//   static SummaryDataBuilderRegistry::Add<MyDataBuilder>
+//       Registered("Data builder for MyAnalysis");
 //
 // The registry entry name is derived automatically from
-// MyViewBuilder::summaryName(), which returns MyView::summaryName().
+// MyDataBuilder::summaryName(), which returns MyData::summaryName().
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDERREGISTRY_H
-#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDERREGISTRY_H
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATABUILDERREGISTRY_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATABUILDERREGISTRY_H
 
-#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h"
+#include "clang/Analysis/Scalable/SummaryData/SummaryDataBuilder.h"
 #include "llvm/Support/Registry.h"
 #include <memory>
 #include <string>
 
 namespace clang::ssaf {
 
-/// Registry for SummaryViewBuilder implementations.
+/// Registry for SummaryDataBuilder implementations.
 ///
 /// Provides an Add helper that derives the registry entry name from
 /// BuilderT::summaryName(), eliminating the possibility of registering a
 /// builder under the wrong name.
-class SummaryViewBuilderRegistry {
-  using RegistryT = llvm::Registry<SummaryViewBuilderBase>;
+class SummaryDataBuilderRegistry {
+  using RegistryT = llvm::Registry<SummaryDataBuilderBase>;
 
 public:
   /// Registers \p BuilderT under the name returned by
@@ -54,10 +54,10 @@ class SummaryViewBuilderRegistry {
 
   /// Instantiates the builder registered under \p Name, or returns nullptr
   /// if no such builder is registered.
-  static std::unique_ptr<SummaryViewBuilderBase>
+  static std::unique_ptr<SummaryDataBuilderBase>
   instantiate(llvm::StringRef Name);
 };
 
 } // namespace clang::ssaf
 
-#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDERREGISTRY_H
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATABUILDERREGISTRY_H
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewTraits.h b/clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataTraits.h
similarity index 75%
rename from clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewTraits.h
rename to clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataTraits.h
index 542714b8b21a7..3d791a9bc8706 100644
--- a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewTraits.h
+++ b/clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataTraits.h
@@ -1,4 +1,4 @@
-//===- SummaryViewTraits.h ------------------------------------------------===//
+//===- SummaryDataTraits.h ------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -6,12 +6,12 @@
 //
 //===----------------------------------------------------------------------===//
 //
-// Type traits for SummaryView subclasses.
+// Type traits for SummaryData subclasses.
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWTRAITS_H
-#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWTRAITS_H
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATATRAITS_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATATRAITS_H
 
 #include "clang/Analysis/Scalable/Model/SummaryName.h"
 #include <type_traits>
@@ -19,7 +19,7 @@
 namespace clang::ssaf {
 
 /// Type trait that checks whether \p T has a static summaryName() method
-/// returning SummaryName. Used to enforce the convention on SummaryView
+/// returning SummaryName. Used to enforce the convention on SummaryData
 /// subclasses at instantiation time.
 template <typename T, typename = void>
 struct HasSummaryName : std::false_type {};
@@ -30,4 +30,4 @@ struct HasSummaryName<T, std::void_t<decltype(T::summaryName())>>
 
 } // namespace clang::ssaf
 
-#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWTRAITS_H
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATATRAITS_H
diff --git a/clang/lib/Analysis/Scalable/CMakeLists.txt b/clang/lib/Analysis/Scalable/CMakeLists.txt
index 3da854558f760..63e066be6a8d2 100644
--- a/clang/lib/Analysis/Scalable/CMakeLists.txt
+++ b/clang/lib/Analysis/Scalable/CMakeLists.txt
@@ -18,8 +18,8 @@ add_clang_library(clangAnalysisScalable
   Serialization/JSONFormat/TUSummary.cpp
   Serialization/JSONFormat/TUSummaryEncoding.cpp
   Serialization/SerializationFormatRegistry.cpp
-  SummaryView/LUSummaryConsumer.cpp
-  SummaryView/SummaryViewBuilderRegistry.cpp
+  SummaryData/LUSummaryConsumer.cpp
+  SummaryData/SummaryDataBuilderRegistry.cpp
   Support/ErrorBuilder.cpp
   TUSummary/ExtractorRegistry.cpp
   TUSummary/TUSummaryBuilder.cpp
diff --git a/clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp b/clang/lib/Analysis/Scalable/SummaryData/LUSummaryConsumer.cpp
similarity index 66%
rename from clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp
rename to clang/lib/Analysis/Scalable/SummaryData/LUSummaryConsumer.cpp
index 1be43eee5e923..1efbcaea24dca 100644
--- a/clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp
+++ b/clang/lib/Analysis/Scalable/SummaryData/LUSummaryConsumer.cpp
@@ -6,32 +6,32 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h"
-#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
+#include "clang/Analysis/Scalable/SummaryData/LUSummaryConsumer.h"
+#include "clang/Analysis/Scalable/SummaryData/SummaryDataBuilderRegistry.h"
 #include <cassert>
 
 using namespace clang;
 using namespace ssaf;
 
-void LUSummaryConsumer::run(const SummaryName &SN, EntityDataMap &Data) {
-  auto Builder = SummaryViewBuilderRegistry::instantiate(SN.str());
+void LUSummaryConsumer::run(const SummaryName &SN, EntityDataMap &EntityData) {
+  auto Builder = SummaryDataBuilderRegistry::instantiate(SN.str());
   if (!Builder) {
     return;
   }
 
-  for (auto &[Id, Summary] : Data) {
+  for (auto &[Id, Summary] : EntityData) {
     Builder->addSummary(Id, std::move(Summary));
   }
 
   Builder->finalize();
 
-  Views.emplace(SN, std::move(*Builder).getView());
+  Data.emplace(SN, std::move(*Builder).getData());
 }
 
 void LUSummaryConsumer::run() {
   assert(!WasRun && "run() must be called exactly once");
   WasRun = true;
-  for (auto &[SN, Data] : LU->Data) {
-    run(SN, Data);
+  for (auto &[SN, EntityData] : LU->Data) {
+    run(SN, EntityData);
   }
 }
diff --git a/clang/lib/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.cpp b/clang/lib/Analysis/Scalable/SummaryData/SummaryDataBuilderRegistry.cpp
similarity index 66%
rename from clang/lib/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.cpp
rename to clang/lib/Analysis/Scalable/SummaryData/SummaryDataBuilderRegistry.cpp
index 7d7ef13a70c01..de8e7ecbb2d84 100644
--- a/clang/lib/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.cpp
+++ b/clang/lib/Analysis/Scalable/SummaryData/SummaryDataBuilderRegistry.cpp
@@ -1,4 +1,4 @@
-//===- SummaryViewBuilderRegistry.cpp -------------------------------------===//
+//===- SummaryDataBuilderRegistry.cpp -------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -6,16 +6,16 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
+#include "clang/Analysis/Scalable/SummaryData/SummaryDataBuilderRegistry.h"
 
 using namespace clang;
 using namespace ssaf;
 
-using RegistryT = llvm::Registry<SummaryViewBuilderBase>;
+using RegistryT = llvm::Registry<SummaryDataBuilderBase>;
 LLVM_INSTANTIATE_REGISTRY(RegistryT)
 
-std::unique_ptr<SummaryViewBuilderBase>
-SummaryViewBuilderRegistry::instantiate(llvm::StringRef Name) {
+std::unique_ptr<SummaryDataBuilderBase>
+SummaryDataBuilderRegistry::instantiate(llvm::StringRef Name) {
   for (const auto &Entry : RegistryT::entries()) {
     if (Entry.getName() == Name) {
       return Entry.instantiate();
@@ -24,7 +24,7 @@ SummaryViewBuilderRegistry::instantiate(llvm::StringRef Name) {
   return nullptr;
 }
 
-bool SummaryViewBuilderRegistry::isRegistered(llvm::StringRef Name) {
+bool SummaryDataBuilderRegistry::isRegistered(llvm::StringRef Name) {
   for (const auto &Entry : RegistryT::entries()) {
     if (Entry.getName() == Name) {
       return true;
diff --git a/clang/unittests/Analysis/Scalable/CMakeLists.txt b/clang/unittests/Analysis/Scalable/CMakeLists.txt
index 7af03a9aec1c2..209e46898326d 100644
--- a/clang/unittests/Analysis/Scalable/CMakeLists.txt
+++ b/clang/unittests/Analysis/Scalable/CMakeLists.txt
@@ -19,7 +19,7 @@ add_distinct_clang_unittest(ClangScalableAnalysisTests
   Serialization/JSONFormatTest/LUSummaryTest.cpp
   Serialization/JSONFormatTest/TUSummaryTest.cpp
   SummaryNameTest.cpp
-  SummaryView/SummaryViewTest.cpp
+  SummaryData/SummaryDataTest.cpp
   TestFixture.cpp
   TUSummaryBuilderTest.cpp
 
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewTest.cpp b/clang/unittests/Analysis/Scalable/SummaryData/SummaryDataTest.cpp
similarity index 73%
rename from clang/unittests/Analysis/Scalable/SummaryView/SummaryViewTest.cpp
rename to clang/unittests/Analysis/Scalable/SummaryData/SummaryDataTest.cpp
index 3b89e2f6d6966..6400f11a7d2be 100644
--- a/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewTest.cpp
+++ b/clang/unittests/Analysis/Scalable/SummaryData/SummaryDataTest.cpp
@@ -1,4 +1,4 @@
-//===- LUSummaryConsumerTest.cpp ------------------------------------------===//
+//===- SummaryDataTest.cpp ------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -14,8 +14,8 @@
 #include "clang/Analysis/Scalable/Model/EntityLinkage.h"
 #include "clang/Analysis/Scalable/Model/EntityName.h"
 #include "clang/Analysis/Scalable/Model/SummaryName.h"
-#include "clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h"
-#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
+#include "clang/Analysis/Scalable/SummaryData/LUSummaryConsumer.h"
+#include "clang/Analysis/Scalable/SummaryData/SummaryDataBuilderRegistry.h"
 #include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
 #include "gtest/gtest.h"
 #include <algorithm>
@@ -63,17 +63,17 @@ class Analysis4EntitySummary : public EntitySummary {
 };
 
 // ---------------------------------------------------------------------------
-// Views
+// Data
 // ---------------------------------------------------------------------------
 
-class Analysis1View : public SummaryView {
+class Analysis1Data : public SummaryData {
 public:
   static SummaryName summaryName() { return SummaryName("Analysis1"); }
   std::vector<std::pair<EntityId, int>> Entries;
   bool WasFinalized = false;
 };
 
-class Analysis2View : public SummaryView {
+class Analysis2Data : public SummaryData {
 public:
   static SummaryName summaryName() { return SummaryName("Analysis2"); }
   std::vector<std::pair<EntityId, int>> Entries;
@@ -82,14 +82,14 @@ class Analysis2View : public SummaryView {
 
 // No builder or registration for Analysis3. Data for Analysis3 is inserted
 // into the LUSummary to verify the consumer silently skips it.
-class Analysis3View : public SummaryView {
+class Analysis3Data : public SummaryData {
 public:
   static SummaryName summaryName() { return SummaryName("Analysis3"); }
 };
 
 // Analysis4 has a registered builder but no data is inserted into the
-// LUSummary, so the builder is never invoked and getView returns nullptr.
-class Analysis4View : public SummaryView {
+// LUSummary, so the builder is never invoked and getData returns nullptr.
+class Analysis4Data : public SummaryData {
 public:
   static SummaryName summaryName() { return SummaryName("Analysis4"); }
   std::vector<std::pair<EntityId, int>> Entries;
@@ -109,51 +109,51 @@ static bool Analysis4BuilderWasDestroyed = false;
 // ---------------------------------------------------------------------------
 
 class Analysis1Builder
-    : public SummaryViewBuilder<Analysis1View, Analysis1EntitySummary> {
+    : public SummaryDataBuilder<Analysis1Data, Analysis1EntitySummary> {
 public:
   ~Analysis1Builder() { Analysis1BuilderWasDestroyed = true; }
 
   void addSummary(EntityId Id,
                   std::unique_ptr<Analysis1EntitySummary> S) override {
-    getView().Entries.push_back({Id, S->InstanceId});
+    getData().Entries.push_back({Id, S->InstanceId});
   }
 
-  void finalize() override { getView().WasFinalized = true; }
+  void finalize() override { getData().WasFinalized = true; }
 };
 
-static SummaryViewBuilderRegistry::Add<Analysis1Builder>
+static SummaryDataBuilderRegistry::Add<Analysis1Builder>
     RegAnalysis1("Builder for Analysis1");
 
 class Analysis2Builder
-    : public SummaryViewBuilder<Analysis2View, Analysis2EntitySummary> {
+    : public SummaryDataBuilder<Analysis2Data, Analysis2EntitySummary> {
 public:
   ~Analysis2Builder() { Analysis2BuilderWasDestroyed = true; }
 
   void addSummary(EntityId Id,
                   std::unique_ptr<Analysis2EntitySummary> S) override {
-    getView().Entries.push_back({Id, S->InstanceId});
+    getData().Entries.push_back({Id, S->InstanceId});
   }
 
-  void finalize() override { getView().WasFinalized = true; }
+  void finalize() override { getData().WasFinalized = true; }
 };
 
-static SummaryViewBuilderRegistry::Add<Analysis2Builder>
+static SummaryDataBuilderRegistry::Add<Analysis2Builder>
     RegAnalysis2("Builder for Analysis2");
 
 class Analysis4Builder
-    : public SummaryViewBuilder<Analysis4View, Analysis4EntitySummary> {
+    : public SummaryDataBuilder<Analysis4Data, Analysis4EntitySummary> {
 public:
   ~Analysis4Builder() { Analysis4BuilderWasDestroyed = true; }
 
   void addSummary(EntityId Id,
                   std::unique_ptr<Analysis4EntitySummary> S) override {
-    getView().Entries.push_back({Id, S->InstanceId});
+    getData().Entries.push_back({Id, S->InstanceId});
   }
 
-  void finalize() override { getView().WasFinalized = true; }
+  void finalize() override { getData().WasFinalized = true; }
 };
 
-static SummaryViewBuilderRegistry::Add<Analysis4Builder>
+static SummaryDataBuilderRegistry::Add<Analysis4Builder>
     RegAnalysis4("Builder for Analysis4");
 
 // ---------------------------------------------------------------------------
@@ -208,18 +208,18 @@ class LUSummaryConsumerTest : public TestFixture {
 // Tests
 // ---------------------------------------------------------------------------
 
-TEST(SummaryViewBuilderRegistryTest, BuilderIsRegistered) {
-  EXPECT_FALSE(SummaryViewBuilderRegistry::isRegistered("Analysis10"));
-  EXPECT_TRUE(SummaryViewBuilderRegistry::isRegistered("Analysis1"));
-  EXPECT_TRUE(SummaryViewBuilderRegistry::isRegistered("Analysis2"));
-  EXPECT_TRUE(SummaryViewBuilderRegistry::isRegistered("Analysis4"));
+TEST(SummaryDataBuilderRegistryTest, BuilderIsRegistered) {
+  EXPECT_FALSE(SummaryDataBuilderRegistry::isRegistered("Analysis10"));
+  EXPECT_TRUE(SummaryDataBuilderRegistry::isRegistered("Analysis1"));
+  EXPECT_TRUE(SummaryDataBuilderRegistry::isRegistered("Analysis2"));
+  EXPECT_TRUE(SummaryDataBuilderRegistry::isRegistered("Analysis4"));
 }
 
-TEST(SummaryViewBuilderRegistryTest, BuilderCanBeInstantiated) {
-  EXPECT_EQ(SummaryViewBuilderRegistry::instantiate("Analysis10"), nullptr);
-  EXPECT_NE(SummaryViewBuilderRegistry::instantiate("Analysis1"), nullptr);
-  EXPECT_NE(SummaryViewBuilderRegistry::instantiate("Analysis2"), nullptr);
-  EXPECT_NE(SummaryViewBuilderRegistry::instantiate("Analysis4"), nullptr);
+TEST(SummaryDataBuilderRegistryTest, BuilderCanBeInstantiated) {
+  EXPECT_EQ(SummaryDataBuilderRegistry::instantiate("Analysis10"), nullptr);
+  EXPECT_NE(SummaryDataBuilderRegistry::instantiate("Analysis1"), nullptr);
+  EXPECT_NE(SummaryDataBuilderRegistry::instantiate("Analysis2"), nullptr);
+  EXPECT_NE(SummaryDataBuilderRegistry::instantiate("Analysis4"), nullptr);
 }
 
 TEST_F(LUSummaryConsumerTest, Run) {
@@ -242,17 +242,17 @@ TEST_F(LUSummaryConsumerTest, Run) {
 
   // Analysis1
   {
-    auto View1 = Consumer.getView<Analysis1View>();
-    ASSERT_NE(View1, nullptr);
+    auto Data1 = Consumer.getData<Analysis1Data>();
+    ASSERT_NE(Data1, nullptr);
 
-    // getView ownership transfer — second call returns nullptr
-    EXPECT_EQ(Consumer.getView<Analysis1View>(), nullptr);
+    // getData ownership transfer — second call returns nullptr
+    EXPECT_EQ(Consumer.getData<Analysis1Data>(), nullptr);
 
-    EXPECT_EQ(View1->Entries.size(), 2u);
-    EXPECT_TRUE(hasEntry(View1->Entries, E1, s1a));
-    EXPECT_TRUE(hasEntry(View1->Entries, E2, s1b));
+    EXPECT_EQ(Data1->Entries.size(), 2u);
+    EXPECT_TRUE(hasEntry(Data1->Entries, E1, s1a));
+    EXPECT_TRUE(hasEntry(Data1->Entries, E2, s1b));
 
-    EXPECT_TRUE(View1->WasFinalized);
+    EXPECT_TRUE(Data1->WasFinalized);
 
     // Builder lifetime
     EXPECT_TRUE(Analysis1BuilderWasDestroyed);
@@ -260,32 +260,32 @@ TEST_F(LUSummaryConsumerTest, Run) {
 
   // Analysis2
   {
-    auto View2 = Consumer.getView<Analysis2View>();
-    ASSERT_NE(View2, nullptr);
+    auto Data2 = Consumer.getData<Analysis2Data>();
+    ASSERT_NE(Data2, nullptr);
 
-    // getView ownership transfer — second call returns nullptr
-    EXPECT_EQ(Consumer.getView<Analysis2View>(), nullptr);
+    // getData ownership transfer — second call returns nullptr
+    EXPECT_EQ(Consumer.getData<Analysis2Data>(), nullptr);
 
-    EXPECT_EQ(View2->Entries.size(), 2u);
-    EXPECT_TRUE(hasEntry(View2->Entries, E2, s2a));
-    EXPECT_TRUE(hasEntry(View2->Entries, E3, s2b));
+    EXPECT_EQ(Data2->Entries.size(), 2u);
+    EXPECT_TRUE(hasEntry(Data2->Entries, E2, s2a));
+    EXPECT_TRUE(hasEntry(Data2->Entries, E3, s2b));
 
-    EXPECT_TRUE(View2->WasFinalized);
+    EXPECT_TRUE(Data2->WasFinalized);
 
     // Builder lifetime
     EXPECT_TRUE(Analysis2BuilderWasDestroyed);
   }
 
-  // Analysis 3
+  // Analysis3
   {
     // Unregistered builder — Analysis3 data silently skipped
-    EXPECT_EQ(Consumer.getView<Analysis3View>(), nullptr);
+    EXPECT_EQ(Consumer.getData<Analysis3Data>(), nullptr);
   }
 
   // Analysis4
   {
     // Registered builder but no data in LUSummary — builder never invoked
-    EXPECT_EQ(Consumer.getView<Analysis4View>(), nullptr);
+    EXPECT_EQ(Consumer.getData<Analysis4Data>(), nullptr);
 
     // Builder lifetime
     EXPECT_FALSE(Analysis4BuilderWasDestroyed);

>From 69e640fc4e1e682ce50b2c11aa2e11e909956405 Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Wed, 11 Mar 2026 21:56:56 -0700
Subject: [PATCH 8/8] Add store

---
 .../Scalable/SummaryData/LUSummaryConsumer.h  |  72 +++----
 .../Scalable/SummaryData/SummaryDataStore.h   | 114 ++++++++++++
 .../SummaryData/LUSummaryConsumer.cpp         |  58 ++++--
 .../Scalable/SummaryData/SummaryDataTest.cpp  | 175 ++++++++++++++----
 4 files changed, 335 insertions(+), 84 deletions(-)
 create mode 100644 clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataStore.h

diff --git a/clang/include/clang/Analysis/Scalable/SummaryData/LUSummaryConsumer.h b/clang/include/clang/Analysis/Scalable/SummaryData/LUSummaryConsumer.h
index 8e93ff025f2f6..bed70c9a989d6 100644
--- a/clang/include/clang/Analysis/Scalable/SummaryData/LUSummaryConsumer.h
+++ b/clang/include/clang/Analysis/Scalable/SummaryData/LUSummaryConsumer.h
@@ -15,58 +15,64 @@
 #define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_LUSUMMARYCONSUMER_H
 
 #include "clang/Analysis/Scalable/EntityLinker/LUSummary.h"
-#include "clang/Analysis/Scalable/Model/EntityId.h"
 #include "clang/Analysis/Scalable/Model/SummaryName.h"
-#include "clang/Analysis/Scalable/SummaryData/SummaryData.h"
-#include "clang/Analysis/Scalable/SummaryData/SummaryDataTraits.h"
-#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
-#include <map>
+#include "clang/Analysis/Scalable/SummaryData/SummaryDataStore.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/Support/Error.h"
 #include <memory>
 
 namespace clang::ssaf {
 
-/// Consumes a LUSummary by dispatching its entity data to all registered
-/// SummaryDataBuilders and collecting the resulting data.
+/// Consumes a LUSummary by dispatching its entity data to registered
+/// SummaryDataBuilders and returning the results in a SummaryDataStore.
+///
+/// Three consumption patterns are supported:
+///   - run()            — processes all registered analyses in the LUSummary,
+///                        silently skipping any with missing data or builders.
+///   - run(names)       — processes a named subset; returns an error if any
+///                        name has no data in the LUSummary or no registered
+///                        builder.
+///   - run<DataTs...>() — type-safe variant of run(names) with the same error
+///                        semantics.
+///
+/// All patterns consume the underlying LUSummary data, so each analysis can
+/// only be retrieved once across all patterns.
 class LUSummaryConsumer final {
 public:
   explicit LUSummaryConsumer(std::unique_ptr<LUSummary> LU)
       : LU(std::move(LU)) {}
 
-  /// Instantiates a builder for each SummaryName present in the LUSummary,
-  /// delivers its entities, finalizes it, and stores the resulting data. Each
-  /// builder is fully processed before the next SummaryName is visited.
-  /// Builders are discarded on return.
+  /// Processes all registered analyses in LUSummary and returns the results.
+  /// Silently skips analyses with no data or no registered builder.
   ///
-  /// \pre Must be called exactly once.
-  void run();
+  /// Requires an rvalue consumer (call as \c std::move(Consumer).run()) because
+  /// this pattern exhausts all remaining LUSummary data.
+  [[nodiscard]] SummaryDataStore run() &&;
 
-  /// Transfers ownership of the data for \p DataT to the caller.
+  /// Processes the named analyses and returns the results.
   ///
-  /// Returns nullptr if no builder for \p DataT was registered or run() has
-  /// not been called. A second call for the same DataT also returns nullptr.
-  template <typename DataT> [[nodiscard]] std::unique_ptr<DataT> getData() {
-    static_assert(std::is_base_of_v<SummaryData, DataT>,
-                  "DataT must derive from SummaryData");
-    static_assert(HasSummaryName<DataT>::value,
-                  "DataT must have a static summaryName() method");
+  /// Returns an error if any name has no data in the LUSummary or no
+  /// registered builder.
+  [[nodiscard]] llvm::Expected<SummaryDataStore>
+  run(llvm::ArrayRef<SummaryName> Names);
 
-    auto It = Data.find(DataT::summaryName());
-    if (It == Data.end()) {
-      return nullptr;
-    }
-    auto *RawPtr = static_cast<DataT *>(It->second.release());
-    Data.erase(It);
-    return std::unique_ptr<DataT>(RawPtr);
+  /// Processes analyses for each of the given types and returns the results.
+  ///
+  /// Returns an error if any type has no data in the LUSummary or no
+  /// registered builder.
+  template <typename... DataTs>
+  [[nodiscard]] llvm::Expected<SummaryDataStore> run() {
+    return run({DataTs::summaryName()...});
   }
 
 private:
-  using EntityDataMap = std::map<EntityId, std::unique_ptr<EntitySummary>>;
-
   std::unique_ptr<LUSummary> LU;
-  std::map<SummaryName, std::unique_ptr<SummaryData>> Data;
-  bool WasRun = false;
 
-  void run(const SummaryName &SN, EntityDataMap &Data);
+  /// Looks up \p SN in the LUSummary, instantiates the registered builder,
+  /// delivers all entities, finalizes, and returns the built data.
+  /// Returns an error if no data for \p SN exists or no builder is registered.
+  /// Consumes the LUSummary entry for \p SN on success.
+  llvm::Expected<std::unique_ptr<SummaryData>> build(const SummaryName &SN);
 };
 
 } // namespace clang::ssaf
diff --git a/clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataStore.h b/clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataStore.h
new file mode 100644
index 0000000000000..1ac26e5789fa2
--- /dev/null
+++ b/clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataStore.h
@@ -0,0 +1,114 @@
+//===- SummaryDataStore.h -------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Owns a collection of SummaryData objects keyed by SummaryName.
+// Produced by LUSummaryConsumer::run() variants.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATASTORE_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATASTORE_H
+
+#include "clang/Analysis/Scalable/Model/SummaryName.h"
+#include "clang/Analysis/Scalable/SummaryData/SummaryData.h"
+#include "clang/Analysis/Scalable/SummaryData/SummaryDataTraits.h"
+#include "clang/Analysis/Scalable/Support/ErrorBuilder.h"
+#include <map>
+#include <memory>
+
+namespace clang::ssaf {
+
+class LUSummaryConsumer;
+
+/// Owns a collection of SummaryData objects keyed by SummaryName.
+/// Produced by LUSummaryConsumer::run() variants.
+class SummaryDataStore {
+  friend class LUSummaryConsumer;
+
+  std::map<SummaryName, std::unique_ptr<SummaryData>> Data;
+
+public:
+  /// Returns true if data for \p Name is stored.
+  [[nodiscard]] bool contains(const SummaryName &Name) const {
+    return Data.find(Name) != Data.end();
+  }
+
+  /// Returns true if data for \p DataT is stored.
+  template <typename DataT> [[nodiscard]] bool contains() const {
+    static_assert(std::is_base_of_v<SummaryData, DataT>,
+                  "DataT must derive from SummaryData");
+    static_assert(HasSummaryName<DataT>::value,
+                  "DataT must have a static summaryName() method");
+
+    return contains(DataT::summaryName());
+  }
+
+  /// Returns a reference to the data for \p DataT, or an error if
+  /// no data for \p DataT is stored.
+  template <typename DataT> [[nodiscard]] llvm::Expected<DataT &> get() {
+    static_assert(std::is_base_of_v<SummaryData, DataT>,
+                  "DataT must derive from SummaryData");
+    static_assert(HasSummaryName<DataT>::value,
+                  "DataT must have a static summaryName() method");
+
+    auto Result = get(DataT::summaryName());
+    if (!Result) {
+      return Result.takeError();
+    }
+    return static_cast<DataT &>(*Result);
+  }
+
+  /// Returns a reference to the data for \p Name, or an error if
+  /// no data for \p Name is stored.
+  [[nodiscard]] llvm::Expected<SummaryData &> get(const SummaryName &Name) {
+    auto It = Data.find(Name);
+    if (It == Data.end()) {
+      return ErrorBuilder::create(std::errc::invalid_argument,
+                                  "no data for analysis '{0}' in store",
+                                  Name.str())
+          .build();
+    }
+    return *It->second;
+  }
+
+  /// Transfers ownership of the data for \p DataT to the caller, or returns
+  /// an error if no data for \p DataT is stored.
+  template <typename DataT>
+  [[nodiscard]] llvm::Expected<std::unique_ptr<DataT>> take() {
+    static_assert(std::is_base_of_v<SummaryData, DataT>,
+                  "DataT must derive from SummaryData");
+    static_assert(HasSummaryName<DataT>::value,
+                  "DataT must have a static summaryName() method");
+
+    auto Result = take(DataT::summaryName());
+    if (!Result) {
+      return Result.takeError();
+    }
+    return std::unique_ptr<DataT>(static_cast<DataT *>(Result->release()));
+  }
+
+  /// Transfers ownership of the data for \p Name to the caller, or returns
+  /// an error if no data for \p Name is stored.
+  [[nodiscard]] llvm::Expected<std::unique_ptr<SummaryData>>
+  take(const SummaryName &Name) {
+    auto It = Data.find(Name);
+    if (It == Data.end()) {
+      return ErrorBuilder::create(std::errc::invalid_argument,
+                                  "no data for analysis '{0}' in store",
+                                  Name.str())
+          .build();
+    }
+    auto Ptr = std::move(It->second);
+    Data.erase(It);
+    return Ptr;
+  }
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATASTORE_H
diff --git a/clang/lib/Analysis/Scalable/SummaryData/LUSummaryConsumer.cpp b/clang/lib/Analysis/Scalable/SummaryData/LUSummaryConsumer.cpp
index 1efbcaea24dca..12c8af08aec36 100644
--- a/clang/lib/Analysis/Scalable/SummaryData/LUSummaryConsumer.cpp
+++ b/clang/lib/Analysis/Scalable/SummaryData/LUSummaryConsumer.cpp
@@ -8,30 +8,66 @@
 
 #include "clang/Analysis/Scalable/SummaryData/LUSummaryConsumer.h"
 #include "clang/Analysis/Scalable/SummaryData/SummaryDataBuilderRegistry.h"
-#include <cassert>
+#include "clang/Analysis/Scalable/Support/ErrorBuilder.h"
+#include <vector>
 
 using namespace clang;
 using namespace ssaf;
 
-void LUSummaryConsumer::run(const SummaryName &SN, EntityDataMap &EntityData) {
+llvm::Expected<std::unique_ptr<SummaryData>>
+LUSummaryConsumer::build(const SummaryName &SN) {
+  auto LUIt = LU->Data.find(SN);
+  if (LUIt == LU->Data.end()) {
+    return ErrorBuilder::create(std::errc::invalid_argument,
+                                "no data for analysis '{0}' in LUSummary",
+                                SN.str())
+        .build();
+  }
+
   auto Builder = SummaryDataBuilderRegistry::instantiate(SN.str());
   if (!Builder) {
-    return;
+    return ErrorBuilder::create(std::errc::invalid_argument,
+                                "no builder registered for analysis '{0}'",
+                                SN.str())
+        .build();
   }
 
-  for (auto &[Id, Summary] : EntityData) {
+  for (auto &[Id, Summary] : LUIt->second) {
     Builder->addSummary(Id, std::move(Summary));
   }
-
   Builder->finalize();
+  LU->Data.erase(LUIt);
 
-  Data.emplace(SN, std::move(*Builder).getData());
+  return std::move(*Builder).getData();
 }
 
-void LUSummaryConsumer::run() {
-  assert(!WasRun && "run() must be called exactly once");
-  WasRun = true;
-  for (auto &[SN, EntityData] : LU->Data) {
-    run(SN, EntityData);
+llvm::Expected<SummaryDataStore>
+LUSummaryConsumer::run(llvm::ArrayRef<SummaryName> Names) {
+  SummaryDataStore Store;
+  for (const auto &SN : Names) {
+    auto Result = build(SN);
+    if (!Result)
+      return Result.takeError();
+    Store.Data.emplace(SN, std::move(*Result));
+  }
+  return Store;
+}
+
+SummaryDataStore LUSummaryConsumer::run() && {
+  SummaryDataStore Store;
+  // Snapshot names first: build() erases entries from LU->Data, so iterating
+  // directly over LU->Data while calling build() would invalidate iterators.
+  std::vector<SummaryName> Names;
+  for (const auto &[SN, _] : LU->Data) {
+    Names.push_back(SN);
+  }
+  for (const auto &SN : Names) {
+    auto Result = build(SN);
+    if (!Result) {
+      llvm::consumeError(Result.takeError());
+      continue;
+    }
+    Store.Data.emplace(SN, std::move(*Result));
   }
+  return Store;
 }
diff --git a/clang/unittests/Analysis/Scalable/SummaryData/SummaryDataTest.cpp b/clang/unittests/Analysis/Scalable/SummaryData/SummaryDataTest.cpp
index 6400f11a7d2be..3170186f09cfb 100644
--- a/clang/unittests/Analysis/Scalable/SummaryData/SummaryDataTest.cpp
+++ b/clang/unittests/Analysis/Scalable/SummaryData/SummaryDataTest.cpp
@@ -17,6 +17,7 @@
 #include "clang/Analysis/Scalable/SummaryData/LUSummaryConsumer.h"
 #include "clang/Analysis/Scalable/SummaryData/SummaryDataBuilderRegistry.h"
 #include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
+#include "llvm/Testing/Support/Error.h"
 #include "gtest/gtest.h"
 #include <algorithm>
 #include <memory>
@@ -222,7 +223,8 @@ TEST(SummaryDataBuilderRegistryTest, BuilderCanBeInstantiated) {
   EXPECT_NE(SummaryDataBuilderRegistry::instantiate("Analysis4"), nullptr);
 }
 
-TEST_F(LUSummaryConsumerTest, Run) {
+// run() — processes all registered analyses present in the LUSummary.
+TEST_F(LUSummaryConsumerTest, RunAll) {
   auto LU = makeLUSummary();
   const auto E1 = addEntity(*LU, "Entity1");
   const auto E2 = addEntity(*LU, "Entity2");
@@ -233,63 +235,156 @@ TEST_F(LUSummaryConsumerTest, Run) {
   int s2a = insertSummary<Analysis2EntitySummary>(*LU, "Analysis2", E2);
   int s2b = insertSummary<Analysis2EntitySummary>(*LU, "Analysis2", E3);
 
-  // no registered builder
+  // No registered builder — Analysis3 data silently skipped.
   [[maybe_unused]] int s3a =
       insertSummary<Analysis1EntitySummary>(*LU, "Analysis3", E1);
 
   LUSummaryConsumer Consumer(std::move(LU));
-  Consumer.run();
+  SummaryDataStore Store = std::move(Consumer).run();
 
-  // Analysis1
   {
-    auto Data1 = Consumer.getData<Analysis1Data>();
-    ASSERT_NE(Data1, nullptr);
-
-    // getData ownership transfer — second call returns nullptr
-    EXPECT_EQ(Consumer.getData<Analysis1Data>(), nullptr);
+    auto Data1OrErr = Store.get<Analysis1Data>();
+    ASSERT_THAT_EXPECTED(Data1OrErr, llvm::Succeeded());
+    auto &Data1 = *Data1OrErr;
+
+    EXPECT_EQ(Data1.Entries.size(), 2u);
+    EXPECT_TRUE(hasEntry(Data1.Entries, E1, s1a));
+    EXPECT_TRUE(hasEntry(Data1.Entries, E2, s1b));
+    EXPECT_TRUE(Data1.WasFinalized);
+    EXPECT_TRUE(Analysis1BuilderWasDestroyed);
 
-    EXPECT_EQ(Data1->Entries.size(), 2u);
-    EXPECT_TRUE(hasEntry(Data1->Entries, E1, s1a));
-    EXPECT_TRUE(hasEntry(Data1->Entries, E2, s1b));
+    // take() transfers ownership — subsequent get() returns an error.
+    auto Take1OrErr = Store.take<Analysis1Data>();
+    ASSERT_THAT_EXPECTED(Take1OrErr, llvm::Succeeded());
+    EXPECT_NE(*Take1OrErr, nullptr);
+    EXPECT_THAT_EXPECTED(Store.get<Analysis1Data>(), llvm::Failed());
+  }
 
-    EXPECT_TRUE(Data1->WasFinalized);
+  {
+    auto Data2OrErr = Store.get<Analysis2Data>();
+    ASSERT_THAT_EXPECTED(Data2OrErr, llvm::Succeeded());
+    auto &Data2 = *Data2OrErr;
+    EXPECT_EQ(Data2.Entries.size(), 2u);
+    EXPECT_TRUE(hasEntry(Data2.Entries, E2, s2a));
+    EXPECT_TRUE(hasEntry(Data2.Entries, E3, s2b));
+    EXPECT_TRUE(Data2.WasFinalized);
+    EXPECT_TRUE(Analysis2BuilderWasDestroyed);
 
-    // Builder lifetime
-    EXPECT_TRUE(Analysis1BuilderWasDestroyed);
+    auto Take2OrErr = Store.take<Analysis2Data>();
+    ASSERT_THAT_EXPECTED(Take2OrErr, llvm::Succeeded());
+    EXPECT_NE(*Take2OrErr, nullptr);
+    EXPECT_THAT_EXPECTED(Store.get<Analysis2Data>(), llvm::Failed());
   }
 
-  // Analysis2
-  {
-    auto Data2 = Consumer.getData<Analysis2Data>();
-    ASSERT_NE(Data2, nullptr);
+  // Unregistered — not present in store.
+  EXPECT_THAT_EXPECTED(Store.get<Analysis3Data>(), llvm::Failed());
 
-    // getData ownership transfer — second call returns nullptr
-    EXPECT_EQ(Consumer.getData<Analysis2Data>(), nullptr);
+  // Registered builder but no data in LUSummary — not present in store.
+  EXPECT_THAT_EXPECTED(Store.get<Analysis4Data>(), llvm::Failed());
+  EXPECT_FALSE(Analysis4BuilderWasDestroyed);
+}
 
-    EXPECT_EQ(Data2->Entries.size(), 2u);
-    EXPECT_TRUE(hasEntry(Data2->Entries, E2, s2a));
-    EXPECT_TRUE(hasEntry(Data2->Entries, E3, s2b));
+// run(names) — processes only the analyses for the given names.
+TEST_F(LUSummaryConsumerTest, RunByName) {
+  auto LU = makeLUSummary();
+  const auto E1 = addEntity(*LU, "Entity1");
+  const auto E2 = addEntity(*LU, "Entity2");
 
-    EXPECT_TRUE(Data2->WasFinalized);
+  int s1a = insertSummary<Analysis1EntitySummary>(*LU, "Analysis1", E1);
+  insertSummary<Analysis2EntitySummary>(*LU, "Analysis2", E2);
 
-    // Builder lifetime
-    EXPECT_TRUE(Analysis2BuilderWasDestroyed);
-  }
+  LUSummaryConsumer Consumer(std::move(LU));
+  auto StoreOrErr = Consumer.run({SummaryName("Analysis1")});
+  ASSERT_THAT_EXPECTED(StoreOrErr, llvm::Succeeded());
+
+  // Analysis1 was requested and has data — present.
+  auto Data1OrErr = StoreOrErr->get<Analysis1Data>();
+  ASSERT_THAT_EXPECTED(Data1OrErr, llvm::Succeeded());
+  auto &Data1 = *Data1OrErr;
+  EXPECT_EQ(Data1.Entries.size(), 1u);
+  EXPECT_TRUE(hasEntry(Data1.Entries, E1, s1a));
+  EXPECT_TRUE(Data1.WasFinalized);
+
+  // Analysis2 was not requested — not present even though data exists.
+  EXPECT_THAT_EXPECTED(StoreOrErr->get<Analysis2Data>(), llvm::Failed());
+}
 
-  // Analysis3
-  {
-    // Unregistered builder — Analysis3 data silently skipped
-    EXPECT_EQ(Consumer.getData<Analysis3Data>(), nullptr);
-  }
+// run(names) — error when a requested name has no data in LUSummary.
+TEST_F(LUSummaryConsumerTest, RunByNameErrorMissingData) {
+  auto LU = makeLUSummary();
+  LUSummaryConsumer Consumer(std::move(LU));
 
-  // Analysis4
-  {
-    // Registered builder but no data in LUSummary — builder never invoked
-    EXPECT_EQ(Consumer.getData<Analysis4Data>(), nullptr);
+  EXPECT_THAT_EXPECTED(Consumer.run({SummaryName("Analysis1")}),
+                       llvm::Failed());
+}
 
-    // Builder lifetime
-    EXPECT_FALSE(Analysis4BuilderWasDestroyed);
-  }
+// run(names) — error when a requested name has no registered builder.
+TEST_F(LUSummaryConsumerTest, RunByNameErrorMissingBuilder) {
+  auto LU = makeLUSummary();
+  const auto E1 = addEntity(*LU, "Entity1");
+  insertSummary<Analysis1EntitySummary>(*LU, "Analysis3", E1);
+
+  LUSummaryConsumer Consumer(std::move(LU));
+
+  // Analysis3 has data but no registered builder.
+  EXPECT_THAT_EXPECTED(Consumer.run({SummaryName("Analysis3")}),
+                       llvm::Failed());
+}
+
+// run<DataTs...>() — type-safe subset.
+TEST_F(LUSummaryConsumerTest, RunByType) {
+  auto LU = makeLUSummary();
+  const auto E1 = addEntity(*LU, "Entity1");
+  const auto E2 = addEntity(*LU, "Entity2");
+
+  int s1a = insertSummary<Analysis1EntitySummary>(*LU, "Analysis1", E1);
+  insertSummary<Analysis2EntitySummary>(*LU, "Analysis2", E2);
+
+  LUSummaryConsumer Consumer(std::move(LU));
+  auto StoreOrErr = Consumer.run<Analysis1Data>();
+  ASSERT_THAT_EXPECTED(StoreOrErr, llvm::Succeeded());
+
+  // Analysis1 was requested — present.
+  auto Data1OrErr = StoreOrErr->get<Analysis1Data>();
+  ASSERT_THAT_EXPECTED(Data1OrErr, llvm::Succeeded());
+  auto &Data1 = *Data1OrErr;
+  EXPECT_EQ(Data1.Entries.size(), 1u);
+  EXPECT_TRUE(hasEntry(Data1.Entries, E1, s1a));
+  EXPECT_TRUE(Data1.WasFinalized);
+
+  // Analysis2 was not requested — not present even though data exists.
+  EXPECT_THAT_EXPECTED(StoreOrErr->get<Analysis2Data>(), llvm::Failed());
+}
+
+// run<DataTs...>() — error when a requested type has no data in LUSummary.
+TEST_F(LUSummaryConsumerTest, RunByTypeErrorMissingData) {
+  auto LU = makeLUSummary();
+  LUSummaryConsumer Consumer(std::move(LU));
+
+  EXPECT_THAT_EXPECTED(Consumer.run<Analysis1Data>(), llvm::Failed());
+}
+
+// contains() — present entries return true; absent entries return false.
+TEST_F(LUSummaryConsumerTest, Contains) {
+  auto LU = makeLUSummary();
+  const auto E1 = addEntity(*LU, "Entity1");
+  insertSummary<Analysis1EntitySummary>(*LU, "Analysis1", E1);
+
+  LUSummaryConsumer Consumer(std::move(LU));
+  SummaryDataStore Store = std::move(Consumer).run();
+
+  // Type-safe variant.
+  EXPECT_TRUE(Store.contains<Analysis1Data>());
+  EXPECT_FALSE(Store.contains<Analysis2Data>());
+
+  // Name-based variant.
+  EXPECT_TRUE(Store.contains(SummaryName("Analysis1")));
+  EXPECT_FALSE(Store.contains(SummaryName("Analysis2")));
+
+  // After take(), contains() returns false.
+  llvm::cantFail(Store.take<Analysis1Data>());
+  EXPECT_FALSE(Store.contains<Analysis1Data>());
+  EXPECT_FALSE(Store.contains(SummaryName("Analysis1")));
 }
 
 } // namespace



More information about the cfe-commits mailing list