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 <[email protected]>
Date: Tue, 10 Mar 2026 22:17:34 -0700
Subject: [PATCH 1/4] 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 <[email protected]>
Date: Tue, 10 Mar 2026 22:33:38 -0700
Subject: [PATCH 2/4] 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 <[email protected]>
Date: Wed, 11 Mar 2026 13:40:46 -0700
Subject: [PATCH 3/4] 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 <[email protected]>
Date: Wed, 11 Mar 2026 15:57:27 -0700
Subject: [PATCH 4/4] 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

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

Reply via email to