[clang] [clang][ssaf] Add LUSummary consumer APIs (PR #185803)
Aviral Goel via cfe-commits
cfe-commits at lists.llvm.org
Wed Mar 11 21:57:28 PDT 2026
https://github.com/aviralg updated https://github.com/llvm/llvm-project/pull/185803
>From 23e4142de20f0ae2ea6b8d800a115d40456fba5f Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Tue, 10 Mar 2026 22:17:34 -0700
Subject: [PATCH 1/8] Initial implementation
---
.../Scalable/EntityLinker/LUSummary.h | 1 +
.../Scalable/SummaryView/LUSummaryConsumer.h | 81 +++++++
.../Scalable/SummaryView/SummaryView.h | 32 +++
.../Scalable/SummaryView/SummaryViewBuilder.h | 104 +++++++++
.../SummaryView/SummaryViewBuilderRegistry.h | 44 ++++
clang/lib/Analysis/Scalable/CMakeLists.txt | 2 +
.../SummaryView/LUSummaryConsumer.cpp | 51 +++++
.../SummaryViewBuilderRegistry.cpp | 23 ++
.../Analysis/Scalable/CMakeLists.txt | 4 +
.../SummaryView/LUSummaryConsumerTest.cpp | 215 ++++++++++++++++++
.../SummaryView/MockSummaryViewBuilder1.cpp | 45 ++++
.../SummaryView/MockSummaryViewBuilder2.cpp | 41 ++++
.../SummaryView/MockSummaryViewBuilders.h | 68 ++++++
.../SummaryViewBuilderRegistryTest.cpp | 82 +++++++
14 files changed, 793 insertions(+)
create mode 100644 clang/include/clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h
create mode 100644 clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h
create mode 100644 clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h
create mode 100644 clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h
create mode 100644 clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp
create mode 100644 clang/lib/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.cpp
create mode 100644 clang/unittests/Analysis/Scalable/SummaryView/LUSummaryConsumerTest.cpp
create mode 100644 clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder1.cpp
create mode 100644 clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder2.cpp
create mode 100644 clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilders.h
create mode 100644 clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp
diff --git a/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummary.h b/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummary.h
index e24f7f581185c..7d0ec92f5c9e7 100644
--- a/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummary.h
+++ b/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummary.h
@@ -31,6 +31,7 @@ namespace clang::ssaf {
/// together. It contains deduplicated entities with their linkage information
/// and the merged entity summaries.
class LUSummary {
+ friend class LUSummaryConsumer;
friend class SerializationFormat;
friend class TestFixture;
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h b/clang/include/clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h
new file mode 100644
index 0000000000000..eb71b2add386d
--- /dev/null
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h
@@ -0,0 +1,81 @@
+//===- LUSummaryConsumer.h ------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// LUSummaryConsumer constructs SummaryView objects by routing LUSummary entity
+// data to the corresponding SummaryViewBuilder objects.
+//
+// Typical usage:
+//
+// auto LU = Format->readLUSummary(Path);
+//
+// LUSummaryConsumer Consumer(std::move(LU));
+// Consumer.run();
+//
+// auto View = Consumer.getView<MyView>();
+// // View is std::unique_ptr<MyView>; Consumer no longer holds it.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_LUSUMMARYCONSUMER_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_LUSUMMARYCONSUMER_H
+
+#include "clang/Analysis/Scalable/EntityLinker/LUSummary.h"
+#include "clang/Analysis/Scalable/Model/EntityId.h"
+#include "clang/Analysis/Scalable/Model/SummaryName.h"
+#include "clang/Analysis/Scalable/SummaryView/SummaryView.h"
+#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
+#include <map>
+#include <memory>
+
+namespace clang::ssaf {
+
+/// Consumes a LUSummary by dispatching its entity data to all registered
+/// SummaryViewBuilders and collecting the resulting views.
+class LUSummaryConsumer final {
+ std::unique_ptr<LUSummary> LU;
+ std::map<SummaryName, std::unique_ptr<SummaryView>> Views;
+ bool WasRun = false;
+
+public:
+ explicit LUSummaryConsumer(std::unique_ptr<LUSummary> LU)
+ : LU(std::move(LU)) {}
+
+ /// Instantiates a builder for each SummaryName present in the LUSummary,
+ /// delivers its entities, finalizes it, and stores the resulting view.
+ /// Each builder is fully processed (addSummary → finalize → getView) before
+ /// the next SummaryName is visited. Builders are discarded on return.
+ ///
+ /// \pre Must be called exactly once.
+ void run();
+
+ /// Transfers ownership of the view for \p ViewT to the caller.
+ ///
+ /// Returns nullptr if no builder for \p ViewT was registered or run() has
+ /// not been called. A second call for the same ViewT also returns nullptr.
+ template <typename ViewT> [[nodiscard]] std::unique_ptr<ViewT> getView() {
+ auto It = Views.find(ViewT::summaryName());
+ if (It == Views.end()) {
+ return nullptr;
+ }
+ auto *RawPtr = static_cast<ViewT *>(It->second.release());
+ Views.erase(It);
+ return std::unique_ptr<ViewT>(RawPtr);
+ }
+
+private:
+ using EntityDataMap = std::map<EntityId, std::unique_ptr<EntitySummary>>;
+
+ /// Processes all entities for a single SummaryName: instantiates the
+ /// registered builder, delivers entities, finalizes, and stores the view.
+ /// Does nothing if no builder is registered for \p SN.
+ void run(const SummaryName &SN, EntityDataMap &Data);
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_LUSUMMARYCONSUMER_H
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h
new file mode 100644
index 0000000000000..34accf5be3c20
--- /dev/null
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h
@@ -0,0 +1,32 @@
+//===- SummaryView.h
+//-------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Abstract base class for all whole-program analysis views built from
+// LUSummary data. Carries no query API — all analysis-specific methods live
+// on concrete subclasses.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEW_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEW_H
+
+namespace clang::ssaf {
+
+/// Abstract base class for whole-program analysis views.
+///
+/// Concrete view classes inherit from this and add a static
+/// \c summaryName() method along with their analysis-specific query API.
+class SummaryView {
+public:
+ virtual ~SummaryView() = default;
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEW_H
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h
new file mode 100644
index 0000000000000..d3d05587b30d9
--- /dev/null
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h
@@ -0,0 +1,104 @@
+//===- SummaryViewBuilder.h
+//------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines SummaryViewBuilderBase (abstract base known to the
+// registry and LUSummaryConsumer) and the typed intermediate template
+// SummaryViewBuilder<ViewT, SummaryT> that concrete builders inherit from.
+//
+// To implement a view builder, inherit from SummaryViewBuilder:
+//
+// class MyViewBuilder
+// : public SummaryViewBuilder<MyView, MyEntitySummary> {
+// public:
+// void addSummary(EntityId Id,
+// std::unique_ptr<MyEntitySummary> Summary) override {
+// // accumulate into getView()
+// }
+// // summaryName() and getView() && provided by the intermediate.
+// // override finalize() if post-processing is needed.
+// };
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDER_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDER_H
+
+#include "clang/Analysis/Scalable/Model/EntityId.h"
+#include "clang/Analysis/Scalable/Model/SummaryName.h"
+#include "clang/Analysis/Scalable/SummaryView/SummaryView.h"
+#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
+#include <memory>
+
+namespace clang::ssaf {
+
+/// Abstract base class for all summary view builders.
+///
+/// Known to the registry and LUSummaryConsumer. Receives entities one at a
+/// time via \c addSummary(), is finalized via \c finalize(), and transfers
+/// ownership of the built view via \c getView() &&.
+class SummaryViewBuilderBase {
+public:
+ virtual ~SummaryViewBuilderBase() = default;
+
+ /// Returns the SummaryName this builder handles.
+ virtual SummaryName summaryName() const = 0;
+
+ /// Called once per entity belonging to this builder's analysis.
+ /// Takes ownership of the summary data.
+ virtual void addSummary(EntityId Id,
+ std::unique_ptr<EntitySummary> Summary) = 0;
+
+ /// Called after all entities have been added.
+ virtual void finalize() {}
+
+ /// Transfers ownership of the built view (type-erased).
+ /// Called by LUSummaryConsumer after finalize(). The rvalue ref-qualifier
+ /// enforces single use — the builder cannot be accessed after this call.
+ virtual std::unique_ptr<SummaryView> getView() && = 0;
+};
+
+/// Typed intermediate template that concrete builders inherit from.
+///
+/// Provides:
+/// - \c summaryName() derived from \c ViewT::summaryName().
+/// - \c getView() && which moves out the built view.
+/// - A protected \c getView() accessor for use during construction.
+/// - NVI dispatch: seals the base \c addSummary(EntityId, EntitySummary) as
+/// \c final, downcasts, and dispatches to the typed overload.
+///
+/// Concrete builders only need to implement the typed
+/// \c addSummary(EntityId, unique_ptr<SummaryT>) overload.
+template <typename ViewT, typename SummaryT>
+class SummaryViewBuilder : public SummaryViewBuilderBase {
+ std::unique_ptr<ViewT> View;
+
+protected:
+ ViewT &getView() & { return *View; }
+
+public:
+ SummaryViewBuilder() : View(std::make_unique<ViewT>()) {}
+
+ SummaryName summaryName() const override { return ViewT::summaryName(); }
+
+ std::unique_ptr<SummaryView> getView() && override { return std::move(View); }
+
+ /// Typed customization point — concrete builders override this.
+ virtual void addSummary(EntityId Id, std::unique_ptr<SummaryT> Summary) = 0;
+
+private:
+ /// Seals the base overload, downcasts, and dispatches to the typed overload.
+ void addSummary(EntityId Id, std::unique_ptr<EntitySummary> Summary) final {
+ addSummary(Id, std::unique_ptr<SummaryT>(
+ static_cast<SummaryT *>(Summary.release())));
+ }
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDER_H
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h
new file mode 100644
index 0000000000000..c02843ad940d3
--- /dev/null
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h
@@ -0,0 +1,44 @@
+//===- SummaryViewBuilderRegistry.h
+//----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Registry for SummaryViewBuilders and helper functions.
+//
+// To register a builder, add this to the builder's translation unit:
+//
+// static SummaryViewBuilderRegistry::Add<MyViewBuilder>
+// Register("MyAnalysis", "View builder for MyAnalysis");
+//
+// The registry entry name must match MyView::summaryName().
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDERREGISTRY_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDERREGISTRY_H
+
+#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h"
+#include "clang/Support/Compiler.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Registry.h"
+
+namespace clang::ssaf {
+
+/// Check if a SummaryViewBuilder was registered with a given name.
+bool isSummaryViewBuilderRegistered(llvm::StringRef Name);
+
+/// Registry for adding new SummaryViewBuilder implementations.
+using SummaryViewBuilderRegistry = llvm::Registry<SummaryViewBuilderBase>;
+
+} // namespace clang::ssaf
+
+namespace llvm {
+extern template class CLANG_TEMPLATE_ABI
+ Registry<clang::ssaf::SummaryViewBuilderBase>;
+} // namespace llvm
+
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDERREGISTRY_H
diff --git a/clang/lib/Analysis/Scalable/CMakeLists.txt b/clang/lib/Analysis/Scalable/CMakeLists.txt
index 4593fbcd515b5..3da854558f760 100644
--- a/clang/lib/Analysis/Scalable/CMakeLists.txt
+++ b/clang/lib/Analysis/Scalable/CMakeLists.txt
@@ -18,6 +18,8 @@ add_clang_library(clangAnalysisScalable
Serialization/JSONFormat/TUSummary.cpp
Serialization/JSONFormat/TUSummaryEncoding.cpp
Serialization/SerializationFormatRegistry.cpp
+ SummaryView/LUSummaryConsumer.cpp
+ SummaryView/SummaryViewBuilderRegistry.cpp
Support/ErrorBuilder.cpp
TUSummary/ExtractorRegistry.cpp
TUSummary/TUSummaryBuilder.cpp
diff --git a/clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp b/clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp
new file mode 100644
index 0000000000000..a3db29be840d6
--- /dev/null
+++ b/clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp
@@ -0,0 +1,51 @@
+//===- LUSummaryConsumer.cpp ----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h"
+#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
+#include <cassert>
+#include <memory>
+
+using namespace clang;
+using namespace ssaf;
+
+static std::unique_ptr<SummaryViewBuilderBase>
+instantiateBuilder(const SummaryName &SN) {
+ for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
+ if (Entry.getName() == SN.str()) {
+ return Entry.instantiate();
+ }
+ }
+ return nullptr;
+}
+
+void LUSummaryConsumer::run(const SummaryName &SN, EntityDataMap &Data) {
+ auto Builder = instantiateBuilder(SN);
+ if (!Builder) {
+ return;
+ }
+
+ assert(Builder->summaryName() == SN &&
+ "registry entry name must match SummaryViewBuilder::summaryName()");
+
+ for (auto &[Id, Summary] : Data) {
+ Builder->addSummary(Id, std::move(Summary));
+ }
+
+ Builder->finalize();
+
+ Views.emplace(SN, std::move(*Builder).getView());
+}
+
+void LUSummaryConsumer::run() {
+ assert(!WasRun && "run() must be called exactly once");
+ WasRun = true;
+ for (auto &[SN, Data] : LU->Data) {
+ run(SN, Data);
+ }
+}
diff --git a/clang/lib/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.cpp b/clang/lib/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.cpp
new file mode 100644
index 0000000000000..eb64394ba4401
--- /dev/null
+++ b/clang/lib/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.cpp
@@ -0,0 +1,23 @@
+//===- SummaryViewBuilderRegistry.cpp -------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
+
+using namespace clang;
+using namespace ssaf;
+
+LLVM_INSTANTIATE_REGISTRY(SummaryViewBuilderRegistry)
+
+bool ssaf::isSummaryViewBuilderRegistered(llvm::StringRef Name) {
+ for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
+ if (Entry.getName() == Name) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/clang/unittests/Analysis/Scalable/CMakeLists.txt b/clang/unittests/Analysis/Scalable/CMakeLists.txt
index 54f9de71884dd..fbeefea15b421 100644
--- a/clang/unittests/Analysis/Scalable/CMakeLists.txt
+++ b/clang/unittests/Analysis/Scalable/CMakeLists.txt
@@ -19,6 +19,10 @@ add_distinct_clang_unittest(ClangScalableAnalysisTests
Serialization/JSONFormatTest/LUSummaryTest.cpp
Serialization/JSONFormatTest/TUSummaryTest.cpp
SummaryNameTest.cpp
+ SummaryView/LUSummaryConsumerTest.cpp
+ SummaryView/MockSummaryViewBuilder1.cpp
+ SummaryView/MockSummaryViewBuilder2.cpp
+ SummaryView/SummaryViewBuilderRegistryTest.cpp
TestFixture.cpp
TUSummaryBuilderTest.cpp
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/LUSummaryConsumerTest.cpp b/clang/unittests/Analysis/Scalable/SummaryView/LUSummaryConsumerTest.cpp
new file mode 100644
index 0000000000000..bab8730ec3343
--- /dev/null
+++ b/clang/unittests/Analysis/Scalable/SummaryView/LUSummaryConsumerTest.cpp
@@ -0,0 +1,215 @@
+//===- LUSummaryConsumerTest.cpp ------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h"
+#include "../TestFixture.h"
+#include "MockSummaryViewBuilders.h"
+#include "clang/Analysis/Scalable/EntityLinker/LUSummary.h"
+#include "clang/Analysis/Scalable/Model/BuildNamespace.h"
+#include "clang/Analysis/Scalable/Model/EntityId.h"
+#include "clang/Analysis/Scalable/Model/EntityIdTable.h"
+#include "clang/Analysis/Scalable/Model/EntityName.h"
+#include "clang/Analysis/Scalable/Model/SummaryName.h"
+#include "gtest/gtest.h"
+#include <memory>
+
+using namespace clang;
+using namespace ssaf;
+
+namespace {
+
+class LUSummaryConsumerTest : public TestFixture {
+protected:
+ void SetUp() override { clearMockBuilderLog(); }
+
+ std::unique_ptr<LUSummary> makeLUSummary() {
+ NestedBuildNamespace NS(
+ {BuildNamespace(BuildNamespaceKind::LinkUnit, "TestLU")});
+ return std::make_unique<LUSummary>(std::move(NS));
+ }
+
+ /// Add an entity to the LUSummary's id table and return its EntityId.
+ EntityId addEntity(LUSummary &LU, llvm::StringRef USR) {
+ NestedBuildNamespace NS(
+ {BuildNamespace(BuildNamespaceKind::LinkUnit, "TestLU")});
+ EntityName Name(USR.str(), "", NS);
+ return getIdTable(LU).getId(Name);
+ }
+
+ /// Insert a pre-built EntitySummary into LUSummary::Data.
+ void insertSummary(LUSummary &LU, llvm::StringRef SummaryNameStr, EntityId Id,
+ std::unique_ptr<EntitySummary> Summary) {
+ getData(LU)[SummaryName(SummaryNameStr.str())][Id] = std::move(Summary);
+ }
+};
+
+// ---------------------------------------------------------------------------
+// No matching data in LUSummary
+// ---------------------------------------------------------------------------
+
+TEST_F(LUSummaryConsumerTest, NoMatchingData_AddSummaryNeverCalled) {
+ auto LU = makeLUSummary();
+ // Insert data for a summary name that has no registered builder.
+ auto Id = addEntity(*LU, "A");
+ insertSummary(*LU, "UnregisteredAnalysis", Id,
+ std::make_unique<MockEntitySummary1>());
+
+ LUSummaryConsumer Consumer(std::move(LU));
+ Consumer.run();
+
+ EXPECT_EQ(MockBuilderLog.find("addSummary"), std::string::npos);
+}
+
+TEST_F(LUSummaryConsumerTest, NoMatchingData_FinalizeNotCalled) {
+ auto LU = makeLUSummary(); // empty — no Mock1 data
+
+ LUSummaryConsumer Consumer(std::move(LU));
+ Consumer.run();
+
+ EXPECT_EQ(MockBuilderLog.find("finalize"), std::string::npos);
+}
+
+TEST_F(LUSummaryConsumerTest, NoMatchingData_GetViewReturnsNull) {
+ auto LU = makeLUSummary();
+
+ LUSummaryConsumer Consumer(std::move(LU));
+ Consumer.run();
+
+ EXPECT_EQ(Consumer.getView<MockView1>(), nullptr);
+}
+
+// ---------------------------------------------------------------------------
+// Matching data delivered correctly
+// ---------------------------------------------------------------------------
+
+TEST_F(LUSummaryConsumerTest, MatchingData_AddSummaryCalledPerEntity) {
+ auto LU = makeLUSummary();
+ auto Id1 = addEntity(*LU, "A");
+ auto Id2 = addEntity(*LU, "B");
+ insertSummary(*LU, "Mock1", Id1, std::make_unique<MockEntitySummary1>());
+ insertSummary(*LU, "Mock1", Id2, std::make_unique<MockEntitySummary1>());
+
+ LUSummaryConsumer Consumer(std::move(LU));
+ Consumer.run();
+
+ auto View = Consumer.getView<MockView1>();
+ ASSERT_NE(View, nullptr);
+ EXPECT_EQ(View->Ids.size(), 2u);
+ EXPECT_NE(std::find(View->Ids.begin(), View->Ids.end(), Id1),
+ View->Ids.end());
+ EXPECT_NE(std::find(View->Ids.begin(), View->Ids.end(), Id2),
+ View->Ids.end());
+}
+
+TEST_F(LUSummaryConsumerTest, MatchingData_FinalizeCalledAfterAllAddSummary) {
+ auto LU = makeLUSummary();
+ auto Id = addEntity(*LU, "A");
+ insertSummary(*LU, "Mock1", Id, std::make_unique<MockEntitySummary1>());
+
+ LUSummaryConsumer Consumer(std::move(LU));
+ Consumer.run();
+
+ // Verify ordering in the log: all addSummary entries must precede all
+ // finalize entries.
+ auto AddSummaryPos = MockBuilderLog.find("addSummary");
+ auto FinalizePos = MockBuilderLog.find("finalize");
+ ASSERT_NE(AddSummaryPos, std::string::npos);
+ ASSERT_NE(FinalizePos, std::string::npos);
+ EXPECT_LT(AddSummaryPos, FinalizePos);
+}
+
+// ---------------------------------------------------------------------------
+// Multiple builders receive only their own entities
+// ---------------------------------------------------------------------------
+
+TEST_F(LUSummaryConsumerTest, MultipleBuilders_IndependentEntities) {
+ auto LU = makeLUSummary();
+ auto Id1 = addEntity(*LU, "A");
+ auto Id2 = addEntity(*LU, "B");
+ insertSummary(*LU, "Mock1", Id1, std::make_unique<MockEntitySummary1>());
+ insertSummary(*LU, "Mock2", Id2, std::make_unique<MockEntitySummary2>());
+
+ LUSummaryConsumer Consumer(std::move(LU));
+ Consumer.run();
+
+ auto View1 = Consumer.getView<MockView1>();
+ ASSERT_NE(View1, nullptr);
+ ASSERT_EQ(View1->Ids.size(), 1u);
+ EXPECT_EQ(View1->Ids[0], Id1);
+
+ auto View2 = Consumer.getView<MockView2>();
+ ASSERT_NE(View2, nullptr);
+ ASSERT_EQ(View2->Ids.size(), 1u);
+ EXPECT_EQ(View2->Ids[0], Id2);
+}
+
+// ---------------------------------------------------------------------------
+// getView ownership transfer
+// ---------------------------------------------------------------------------
+
+TEST_F(LUSummaryConsumerTest, GetView_FirstCallReturnsNonNull) {
+ auto LU = makeLUSummary();
+ auto Id = addEntity(*LU, "A");
+ insertSummary(*LU, "Mock1", Id, std::make_unique<MockEntitySummary1>());
+
+ LUSummaryConsumer Consumer(std::move(LU));
+ Consumer.run();
+
+ EXPECT_NE(Consumer.getView<MockView1>(), nullptr);
+}
+
+TEST_F(LUSummaryConsumerTest, GetView_SecondCallReturnsNull) {
+ auto LU = makeLUSummary();
+ auto Id = addEntity(*LU, "A");
+ insertSummary(*LU, "Mock1", Id, std::make_unique<MockEntitySummary1>());
+
+ LUSummaryConsumer Consumer(std::move(LU));
+ Consumer.run();
+
+ auto First = Consumer.getView<MockView1>();
+ EXPECT_NE(First, nullptr);
+ EXPECT_EQ(Consumer.getView<MockView1>(), nullptr);
+}
+
+TEST_F(LUSummaryConsumerTest, GetView_UnregisteredViewReturnsNull) {
+ // MockView1 is registered; a hypothetical OtherView is not.
+ struct OtherView : public SummaryView {
+ static SummaryName summaryName() { return SummaryName("Other"); }
+ };
+
+ auto LU = makeLUSummary();
+ LUSummaryConsumer Consumer(std::move(LU));
+ Consumer.run();
+
+ EXPECT_EQ(Consumer.getView<OtherView>(), nullptr);
+}
+
+// ---------------------------------------------------------------------------
+// Builder lifetime
+// ---------------------------------------------------------------------------
+
+TEST_F(LUSummaryConsumerTest, BuildersDestroyedAfterRun) {
+ auto LU = makeLUSummary();
+ auto Id1 = addEntity(*LU, "A");
+ auto Id2 = addEntity(*LU, "B");
+ insertSummary(*LU, "Mock1", Id1, std::make_unique<MockEntitySummary1>());
+ insertSummary(*LU, "Mock2", Id2, std::make_unique<MockEntitySummary2>());
+
+ LUSummaryConsumer Consumer(std::move(LU));
+ Consumer.run();
+
+ // Both destructors must have been called by the time run() returns.
+ EXPECT_NE(
+ MockBuilderLog.find("MockSummaryViewBuilder1 destructor was invoked"),
+ std::string::npos);
+ EXPECT_NE(
+ MockBuilderLog.find("MockSummaryViewBuilder2 destructor was invoked"),
+ std::string::npos);
+}
+
+} // namespace
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder1.cpp b/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder1.cpp
new file mode 100644
index 0000000000000..2bfc526969393
--- /dev/null
+++ b/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder1.cpp
@@ -0,0 +1,45 @@
+//===- MockSummaryViewBuilder1.cpp ----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MockSummaryViewBuilders.h"
+#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
+
+using namespace clang;
+using namespace ssaf;
+
+std::string clang::ssaf::MockBuilderLog;
+
+void clang::ssaf::clearMockBuilderLog() { MockBuilderLog.clear(); }
+
+namespace {
+
+class MockSummaryViewBuilder1
+ : public SummaryViewBuilder<MockView1, MockEntitySummary1> {
+public:
+ MockSummaryViewBuilder1() {
+ MockBuilderLog += "MockSummaryViewBuilder1 constructor was invoked\n";
+ }
+
+ ~MockSummaryViewBuilder1() {
+ MockBuilderLog += "MockSummaryViewBuilder1 destructor was invoked\n";
+ }
+
+ void addSummary(EntityId Id, std::unique_ptr<MockEntitySummary1>) override {
+ MockBuilderLog += "MockSummaryViewBuilder1 addSummary was invoked\n";
+ getView().Ids.push_back(Id);
+ }
+
+ void finalize() override {
+ MockBuilderLog += "MockSummaryViewBuilder1 finalize was invoked\n";
+ }
+};
+
+static SummaryViewBuilderRegistry::Add<MockSummaryViewBuilder1>
+ Register("Mock1", "Mock view builder 1");
+
+} // namespace
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder2.cpp b/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder2.cpp
new file mode 100644
index 0000000000000..b43e7f69bbb8e
--- /dev/null
+++ b/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder2.cpp
@@ -0,0 +1,41 @@
+//===- MockSummaryViewBuilder2.cpp ----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MockSummaryViewBuilders.h"
+#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
+
+using namespace clang;
+using namespace ssaf;
+
+namespace {
+
+class MockSummaryViewBuilder2
+ : public SummaryViewBuilder<MockView2, MockEntitySummary2> {
+public:
+ MockSummaryViewBuilder2() {
+ MockBuilderLog += "MockSummaryViewBuilder2 constructor was invoked\n";
+ }
+
+ ~MockSummaryViewBuilder2() {
+ MockBuilderLog += "MockSummaryViewBuilder2 destructor was invoked\n";
+ }
+
+ void addSummary(EntityId Id, std::unique_ptr<MockEntitySummary2>) override {
+ MockBuilderLog += "MockSummaryViewBuilder2 addSummary was invoked\n";
+ getView().Ids.push_back(Id);
+ }
+
+ void finalize() override {
+ MockBuilderLog += "MockSummaryViewBuilder2 finalize was invoked\n";
+ }
+};
+
+static SummaryViewBuilderRegistry::Add<MockSummaryViewBuilder2>
+ Register("Mock2", "Mock view builder 2");
+
+} // namespace
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilders.h b/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilders.h
new file mode 100644
index 0000000000000..f6a46a65d2256
--- /dev/null
+++ b/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilders.h
@@ -0,0 +1,68 @@
+//===- MockSummaryViewBuilders.h
+//-------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Shared mock types for SummaryView tests. Two mock analyses ("Mock1" and
+// "Mock2") are defined here, with their builders registered in
+// MockSummaryViewBuilder1.cpp and MockSummaryViewBuilder2.cpp.
+//
+// Tests observe builder behaviour via MockBuilderLog, which records lifecycle
+// events (constructor, addSummary, finalize, destructor) as newline-terminated
+// strings. Call clearMockBuilderLog() in SetUp() to isolate tests.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef CLANG_UNITTESTS_ANALYSIS_SCALABLE_SUMMARYVIEW_MOCKSUMMARYVIEWBUILDERS_H
+#define CLANG_UNITTESTS_ANALYSIS_SCALABLE_SUMMARYVIEW_MOCKSUMMARYVIEWBUILDERS_H
+
+#include "clang/Analysis/Scalable/Model/EntityId.h"
+#include "clang/Analysis/Scalable/Model/SummaryName.h"
+#include "clang/Analysis/Scalable/SummaryView/SummaryView.h"
+#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h"
+#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
+#include <string>
+#include <vector>
+
+namespace clang::ssaf {
+
+// ---- Mock entity summaries -----------------------------------------------
+
+class MockEntitySummary1 : public EntitySummary {
+public:
+ SummaryName getSummaryName() const override { return SummaryName("Mock1"); }
+};
+
+class MockEntitySummary2 : public EntitySummary {
+public:
+ SummaryName getSummaryName() const override { return SummaryName("Mock2"); }
+};
+
+// ---- Mock views ------------------------------------------------------------
+
+class MockView1 : public SummaryView {
+public:
+ static SummaryName summaryName() { return SummaryName("Mock1"); }
+ std::vector<EntityId> Ids;
+};
+
+class MockView2 : public SummaryView {
+public:
+ static SummaryName summaryName() { return SummaryName("Mock2"); }
+ std::vector<EntityId> Ids;
+};
+
+// ---- Shared log ------------------------------------------------------------
+// Defined in MockSummaryViewBuilder1.cpp; written by both mock builders.
+
+extern std::string MockBuilderLog;
+
+void clearMockBuilderLog();
+
+} // namespace clang::ssaf
+
+#endif // CLANG_UNITTESTS_ANALYSIS_SCALABLE_SUMMARYVIEW_MOCKSUMMARYVIEWBUILDERS_H
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp b/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp
new file mode 100644
index 0000000000000..99803ef6ffff7
--- /dev/null
+++ b/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp
@@ -0,0 +1,82 @@
+//===- SummaryViewBuilderRegistryTest.cpp ---------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
+#include "MockSummaryViewBuilders.h"
+#include "llvm/ADT/StringRef.h"
+#include "gtest/gtest.h"
+#include <set>
+
+using namespace clang;
+using namespace ssaf;
+
+namespace {
+
+class SummaryViewBuilderRegistryTest : public ::testing::Test {
+protected:
+ void SetUp() override { clearMockBuilderLog(); }
+};
+
+TEST_F(SummaryViewBuilderRegistryTest, isSummaryViewBuilderRegistered) {
+ EXPECT_FALSE(isSummaryViewBuilderRegistered("Non-existent-builder"));
+ EXPECT_TRUE(isSummaryViewBuilderRegistered("Mock1"));
+ EXPECT_TRUE(isSummaryViewBuilderRegistered("Mock2"));
+}
+
+TEST_F(SummaryViewBuilderRegistryTest, EnumeratingRegistryEntries) {
+ std::set<llvm::StringRef> ActualNames;
+ for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
+ bool Inserted = ActualNames.insert(Entry.getName()).second;
+ EXPECT_TRUE(Inserted);
+ }
+
+ EXPECT_EQ(ActualNames, (std::set<llvm::StringRef>{"Mock1", "Mock2"}));
+}
+
+TEST_F(SummaryViewBuilderRegistryTest, InstantiatingBuilder1) {
+ {
+ // Find Mock1 entry explicitly.
+ std::unique_ptr<SummaryViewBuilderBase> B1;
+ for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
+ if (Entry.getName() == "Mock1") {
+ B1 = Entry.instantiate();
+ }
+ }
+
+ ASSERT_TRUE(B1);
+ EXPECT_EQ(B1->summaryName(), SummaryName("Mock1"));
+ EXPECT_TRUE(MockBuilderLog.find(
+ "MockSummaryViewBuilder1 constructor was invoked") !=
+ std::string::npos);
+ }
+ EXPECT_TRUE(
+ MockBuilderLog.find("MockSummaryViewBuilder1 destructor was invoked") !=
+ std::string::npos);
+}
+
+TEST_F(SummaryViewBuilderRegistryTest, InstantiatingBuilder2) {
+ {
+ std::unique_ptr<SummaryViewBuilderBase> B2;
+ for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
+ if (Entry.getName() == "Mock2") {
+ B2 = Entry.instantiate();
+ }
+ }
+
+ ASSERT_TRUE(B2);
+ EXPECT_EQ(B2->summaryName(), SummaryName("Mock2"));
+ EXPECT_TRUE(MockBuilderLog.find(
+ "MockSummaryViewBuilder2 constructor was invoked") !=
+ std::string::npos);
+ }
+ EXPECT_TRUE(
+ MockBuilderLog.find("MockSummaryViewBuilder2 destructor was invoked") !=
+ std::string::npos);
+}
+
+} // namespace
>From 3e97ae7a0ad8ab63d0ea17c55f3cd2b2438532c7 Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Tue, 10 Mar 2026 22:33:38 -0700
Subject: [PATCH 2/8] Fix
---
.../Scalable/SummaryView/SummaryView.h | 3 +--
.../Scalable/SummaryView/SummaryViewBuilder.h | 3 +--
.../SummaryView/SummaryViewBuilderRegistry.h | 3 +--
.../SummaryView/MockSummaryViewBuilders.h | 3 +--
.../SummaryViewBuilderRegistryTest.cpp | 20 +++++++++----------
5 files changed, 14 insertions(+), 18 deletions(-)
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h
index 34accf5be3c20..46778e94976f3 100644
--- a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h
@@ -1,5 +1,4 @@
-//===- SummaryView.h
-//-------------------------------------------------------===//
+//===- SummaryView.h ------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h
index d3d05587b30d9..c71652662f4d9 100644
--- a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h
@@ -1,5 +1,4 @@
-//===- SummaryViewBuilder.h
-//------------------------------------------------===//
+//===- SummaryViewBuilder.h -----------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h
index c02843ad940d3..d3fc08a71a446 100644
--- a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h
@@ -1,5 +1,4 @@
-//===- SummaryViewBuilderRegistry.h
-//----------------------------------------===//
+//===- SummaryViewBuilderRegistry.h ---------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilders.h b/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilders.h
index f6a46a65d2256..8bdcb27ca93f6 100644
--- a/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilders.h
+++ b/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilders.h
@@ -1,5 +1,4 @@
-//===- MockSummaryViewBuilders.h
-//-------------------------------------------===//
+//===- MockSummaryViewBuilders.h ------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp b/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp
index 99803ef6ffff7..b48682500cd39 100644
--- a/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp
+++ b/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp
@@ -50,12 +50,12 @@ TEST_F(SummaryViewBuilderRegistryTest, InstantiatingBuilder1) {
ASSERT_TRUE(B1);
EXPECT_EQ(B1->summaryName(), SummaryName("Mock1"));
- EXPECT_TRUE(MockBuilderLog.find(
- "MockSummaryViewBuilder1 constructor was invoked") !=
- std::string::npos);
+ EXPECT_NE(
+ MockBuilderLog.find("MockSummaryViewBuilder1 constructor was invoked"),
+ std::string::npos);
}
- EXPECT_TRUE(
- MockBuilderLog.find("MockSummaryViewBuilder1 destructor was invoked") !=
+ EXPECT_NE(
+ MockBuilderLog.find("MockSummaryViewBuilder1 destructor was invoked"),
std::string::npos);
}
@@ -70,12 +70,12 @@ TEST_F(SummaryViewBuilderRegistryTest, InstantiatingBuilder2) {
ASSERT_TRUE(B2);
EXPECT_EQ(B2->summaryName(), SummaryName("Mock2"));
- EXPECT_TRUE(MockBuilderLog.find(
- "MockSummaryViewBuilder2 constructor was invoked") !=
- std::string::npos);
+ EXPECT_NE(
+ MockBuilderLog.find("MockSummaryViewBuilder2 constructor was invoked"),
+ std::string::npos);
}
- EXPECT_TRUE(
- MockBuilderLog.find("MockSummaryViewBuilder2 destructor was invoked") !=
+ EXPECT_NE(
+ MockBuilderLog.find("MockSummaryViewBuilder2 destructor was invoked"),
std::string::npos);
}
>From edf933a1d8355485a56f605f6606be57d622d4e8 Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Wed, 11 Mar 2026 13:40:46 -0700
Subject: [PATCH 3/8] Testing
---
.../Analysis/Scalable/CMakeLists.txt | 2 -
.../SummaryView/LUSummaryConsumerTest.cpp | 336 +++++++++++-------
.../SummaryView/MockSummaryViewBuilder1.cpp | 45 ---
.../SummaryView/MockSummaryViewBuilder2.cpp | 41 ---
.../SummaryView/MockSummaryViewBuilders.h | 67 ----
.../SummaryViewBuilderRegistryTest.cpp | 66 ++--
6 files changed, 223 insertions(+), 334 deletions(-)
delete mode 100644 clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder1.cpp
delete mode 100644 clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder2.cpp
delete mode 100644 clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilders.h
diff --git a/clang/unittests/Analysis/Scalable/CMakeLists.txt b/clang/unittests/Analysis/Scalable/CMakeLists.txt
index fbeefea15b421..41e99dcca5fd2 100644
--- a/clang/unittests/Analysis/Scalable/CMakeLists.txt
+++ b/clang/unittests/Analysis/Scalable/CMakeLists.txt
@@ -20,8 +20,6 @@ add_distinct_clang_unittest(ClangScalableAnalysisTests
Serialization/JSONFormatTest/TUSummaryTest.cpp
SummaryNameTest.cpp
SummaryView/LUSummaryConsumerTest.cpp
- SummaryView/MockSummaryViewBuilder1.cpp
- SummaryView/MockSummaryViewBuilder2.cpp
SummaryView/SummaryViewBuilderRegistryTest.cpp
TestFixture.cpp
TUSummaryBuilderTest.cpp
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/LUSummaryConsumerTest.cpp b/clang/unittests/Analysis/Scalable/SummaryView/LUSummaryConsumerTest.cpp
index bab8730ec3343..3499d5bda1847 100644
--- a/clang/unittests/Analysis/Scalable/SummaryView/LUSummaryConsumerTest.cpp
+++ b/clang/unittests/Analysis/Scalable/SummaryView/LUSummaryConsumerTest.cpp
@@ -8,208 +8,272 @@
#include "clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h"
#include "../TestFixture.h"
-#include "MockSummaryViewBuilders.h"
#include "clang/Analysis/Scalable/EntityLinker/LUSummary.h"
#include "clang/Analysis/Scalable/Model/BuildNamespace.h"
#include "clang/Analysis/Scalable/Model/EntityId.h"
#include "clang/Analysis/Scalable/Model/EntityIdTable.h"
+#include "clang/Analysis/Scalable/Model/EntityLinkage.h"
#include "clang/Analysis/Scalable/Model/EntityName.h"
#include "clang/Analysis/Scalable/Model/SummaryName.h"
+#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
+#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
#include "gtest/gtest.h"
+#include <algorithm>
#include <memory>
+#include <utility>
+#include <vector>
using namespace clang;
using namespace ssaf;
namespace {
-class LUSummaryConsumerTest : public TestFixture {
-protected:
- void SetUp() override { clearMockBuilderLog(); }
+// ---------------------------------------------------------------------------
+// Instance counter
+// ---------------------------------------------------------------------------
- std::unique_ptr<LUSummary> makeLUSummary() {
- NestedBuildNamespace NS(
- {BuildNamespace(BuildNamespaceKind::LinkUnit, "TestLU")});
- return std::make_unique<LUSummary>(std::move(NS));
+static int NextSummaryInstanceId = 0;
+
+// ---------------------------------------------------------------------------
+// Entity summaries
+// ---------------------------------------------------------------------------
+
+class Analysis1EntitySummary : public EntitySummary {
+public:
+ int InstanceId = NextSummaryInstanceId++;
+ SummaryName getSummaryName() const override {
+ return SummaryName("Analysis1");
}
+};
- /// Add an entity to the LUSummary's id table and return its EntityId.
- EntityId addEntity(LUSummary &LU, llvm::StringRef USR) {
- NestedBuildNamespace NS(
- {BuildNamespace(BuildNamespaceKind::LinkUnit, "TestLU")});
- EntityName Name(USR.str(), "", NS);
- return getIdTable(LU).getId(Name);
+class Analysis2EntitySummary : public EntitySummary {
+public:
+ int InstanceId = NextSummaryInstanceId++;
+ SummaryName getSummaryName() const override {
+ return SummaryName("Analysis2");
}
+};
- /// Insert a pre-built EntitySummary into LUSummary::Data.
- void insertSummary(LUSummary &LU, llvm::StringRef SummaryNameStr, EntityId Id,
- std::unique_ptr<EntitySummary> Summary) {
- getData(LU)[SummaryName(SummaryNameStr.str())][Id] = std::move(Summary);
+class Analysis4EntitySummary : public EntitySummary {
+public:
+ int InstanceId = NextSummaryInstanceId++;
+ SummaryName getSummaryName() const override {
+ return SummaryName("Analysis4");
}
};
// ---------------------------------------------------------------------------
-// No matching data in LUSummary
+// Views
// ---------------------------------------------------------------------------
-TEST_F(LUSummaryConsumerTest, NoMatchingData_AddSummaryNeverCalled) {
- auto LU = makeLUSummary();
- // Insert data for a summary name that has no registered builder.
- auto Id = addEntity(*LU, "A");
- insertSummary(*LU, "UnregisteredAnalysis", Id,
- std::make_unique<MockEntitySummary1>());
+class Analysis1View : public SummaryView {
+public:
+ static SummaryName summaryName() { return SummaryName("Analysis1"); }
+ std::vector<std::pair<EntityId, int>> Entries;
+ bool WasFinalized = false;
+};
- LUSummaryConsumer Consumer(std::move(LU));
- Consumer.run();
+class Analysis2View : public SummaryView {
+public:
+ static SummaryName summaryName() { return SummaryName("Analysis2"); }
+ std::vector<std::pair<EntityId, int>> Entries;
+ bool WasFinalized = false;
+};
- EXPECT_EQ(MockBuilderLog.find("addSummary"), std::string::npos);
-}
+// No builder or registration for Analysis3. Data for Analysis3 is inserted
+// into the LUSummary to verify the consumer silently skips it.
+class Analysis3View : public SummaryView {
+public:
+ static SummaryName summaryName() { return SummaryName("Analysis3"); }
+};
-TEST_F(LUSummaryConsumerTest, NoMatchingData_FinalizeNotCalled) {
- auto LU = makeLUSummary(); // empty — no Mock1 data
+// Analysis4 has a registered builder but no data is inserted into the
+// LUSummary, so the builder is never invoked and getView returns nullptr.
+class Analysis4View : public SummaryView {
+public:
+ static SummaryName summaryName() { return SummaryName("Analysis4"); }
+};
- LUSummaryConsumer Consumer(std::move(LU));
- Consumer.run();
+// ---------------------------------------------------------------------------
+// Builder destruction flags (reset in SetUp)
+// ---------------------------------------------------------------------------
- EXPECT_EQ(MockBuilderLog.find("finalize"), std::string::npos);
-}
+static bool Analysis1BuilderWasDestroyed = false;
+static bool Analysis2BuilderWasDestroyed = false;
+static bool Analysis4BuilderWasDestroyed = false;
-TEST_F(LUSummaryConsumerTest, NoMatchingData_GetViewReturnsNull) {
- auto LU = makeLUSummary();
+// ---------------------------------------------------------------------------
+// Builders
+// ---------------------------------------------------------------------------
- LUSummaryConsumer Consumer(std::move(LU));
- Consumer.run();
+class Analysis1Builder
+ : public SummaryViewBuilder<Analysis1View, Analysis1EntitySummary> {
+public:
+ ~Analysis1Builder() { Analysis1BuilderWasDestroyed = true; }
- EXPECT_EQ(Consumer.getView<MockView1>(), nullptr);
-}
+ void addSummary(EntityId Id,
+ std::unique_ptr<Analysis1EntitySummary> S) override {
+ getView().Entries.push_back({Id, S->InstanceId});
+ }
-// ---------------------------------------------------------------------------
-// Matching data delivered correctly
-// ---------------------------------------------------------------------------
+ void finalize() override { getView().WasFinalized = true; }
+};
-TEST_F(LUSummaryConsumerTest, MatchingData_AddSummaryCalledPerEntity) {
- auto LU = makeLUSummary();
- auto Id1 = addEntity(*LU, "A");
- auto Id2 = addEntity(*LU, "B");
- insertSummary(*LU, "Mock1", Id1, std::make_unique<MockEntitySummary1>());
- insertSummary(*LU, "Mock1", Id2, std::make_unique<MockEntitySummary1>());
+static SummaryViewBuilderRegistry::Add<Analysis1Builder>
+ RegAnalysis1("Analysis1", "Builder for Analysis1");
- LUSummaryConsumer Consumer(std::move(LU));
- Consumer.run();
+class Analysis2Builder
+ : public SummaryViewBuilder<Analysis2View, Analysis2EntitySummary> {
+public:
+ ~Analysis2Builder() { Analysis2BuilderWasDestroyed = true; }
- auto View = Consumer.getView<MockView1>();
- ASSERT_NE(View, nullptr);
- EXPECT_EQ(View->Ids.size(), 2u);
- EXPECT_NE(std::find(View->Ids.begin(), View->Ids.end(), Id1),
- View->Ids.end());
- EXPECT_NE(std::find(View->Ids.begin(), View->Ids.end(), Id2),
- View->Ids.end());
-}
+ void addSummary(EntityId Id,
+ std::unique_ptr<Analysis2EntitySummary> S) override {
+ getView().Entries.push_back({Id, S->InstanceId});
+ }
-TEST_F(LUSummaryConsumerTest, MatchingData_FinalizeCalledAfterAllAddSummary) {
- auto LU = makeLUSummary();
- auto Id = addEntity(*LU, "A");
- insertSummary(*LU, "Mock1", Id, std::make_unique<MockEntitySummary1>());
+ void finalize() override { getView().WasFinalized = true; }
+};
- LUSummaryConsumer Consumer(std::move(LU));
- Consumer.run();
+static SummaryViewBuilderRegistry::Add<Analysis2Builder>
+ RegAnalysis2("Analysis2", "Builder for Analysis2");
- // Verify ordering in the log: all addSummary entries must precede all
- // finalize entries.
- auto AddSummaryPos = MockBuilderLog.find("addSummary");
- auto FinalizePos = MockBuilderLog.find("finalize");
- ASSERT_NE(AddSummaryPos, std::string::npos);
- ASSERT_NE(FinalizePos, std::string::npos);
- EXPECT_LT(AddSummaryPos, FinalizePos);
-}
+class Analysis4Builder
+ : public SummaryViewBuilder<Analysis4View, Analysis4EntitySummary> {
+public:
+ ~Analysis4Builder() { Analysis4BuilderWasDestroyed = true; }
+
+ void addSummary(EntityId Id,
+ std::unique_ptr<Analysis4EntitySummary> S) override {
+ getView().Entries.push_back({Id, S->InstanceId});
+ }
+
+ void finalize() override { getView().WasFinalized = true; }
+};
+
+static SummaryViewBuilderRegistry::Add<Analysis4Builder>
+ RegAnalysis4("Analysis4", "Builder for Analysis4");
// ---------------------------------------------------------------------------
-// Multiple builders receive only their own entities
+// Fixture
// ---------------------------------------------------------------------------
-TEST_F(LUSummaryConsumerTest, MultipleBuilders_IndependentEntities) {
- auto LU = makeLUSummary();
- auto Id1 = addEntity(*LU, "A");
- auto Id2 = addEntity(*LU, "B");
- insertSummary(*LU, "Mock1", Id1, std::make_unique<MockEntitySummary1>());
- insertSummary(*LU, "Mock2", Id2, std::make_unique<MockEntitySummary2>());
+class LUSummaryConsumerTest : public TestFixture {
+protected:
+ static constexpr EntityLinkage ExternalLinkage =
+ EntityLinkage(EntityLinkageType::External);
+
+ void SetUp() override {
+ NextSummaryInstanceId = 0;
+ Analysis1BuilderWasDestroyed = false;
+ Analysis2BuilderWasDestroyed = false;
+ Analysis4BuilderWasDestroyed = false;
+ }
- LUSummaryConsumer Consumer(std::move(LU));
- Consumer.run();
+ std::unique_ptr<LUSummary> makeLUSummary() {
+ NestedBuildNamespace NS(
+ {BuildNamespace(BuildNamespaceKind::LinkUnit, "TestLU")});
+ return std::make_unique<LUSummary>(std::move(NS));
+ }
- auto View1 = Consumer.getView<MockView1>();
- ASSERT_NE(View1, nullptr);
- ASSERT_EQ(View1->Ids.size(), 1u);
- EXPECT_EQ(View1->Ids[0], Id1);
+ EntityId addEntity(LUSummary &LU, llvm::StringRef USR) {
+ NestedBuildNamespace NS(
+ {BuildNamespace(BuildNamespaceKind::LinkUnit, "TestLU")});
+ EntityName Name(USR.str(), "", NS);
+ EntityId Id = getIdTable(LU).getId(Name);
+ getLinkageTable(LU).insert({Id, ExternalLinkage});
+ return Id;
+ }
- auto View2 = Consumer.getView<MockView2>();
- ASSERT_NE(View2, nullptr);
- ASSERT_EQ(View2->Ids.size(), 1u);
- EXPECT_EQ(View2->Ids[0], Id2);
-}
+ static bool hasEntry(const std::vector<std::pair<EntityId, int>> &Entries,
+ EntityId Id, int InstanceId) {
+ return std::find(Entries.begin(), Entries.end(),
+ std::make_pair(Id, InstanceId)) != Entries.end();
+ }
+
+ /// Inserts a freshly constructed SummaryT for the given entity and returns
+ /// the summary's InstanceId so the test can verify delivery later.
+ template <typename SummaryT>
+ int insertSummary(LUSummary &LU, llvm::StringRef SN, EntityId Id) {
+ auto S = std::make_unique<SummaryT>();
+ int InstanceId = S->InstanceId;
+ getData(LU)[SummaryName(SN.str())][Id] = std::move(S);
+ return InstanceId;
+ }
+};
// ---------------------------------------------------------------------------
-// getView ownership transfer
+// Tests
// ---------------------------------------------------------------------------
-TEST_F(LUSummaryConsumerTest, GetView_FirstCallReturnsNonNull) {
+TEST_F(LUSummaryConsumerTest, Run) {
auto LU = makeLUSummary();
- auto Id = addEntity(*LU, "A");
- insertSummary(*LU, "Mock1", Id, std::make_unique<MockEntitySummary1>());
+ const auto E1 = addEntity(*LU, "Entity1");
+ const auto E2 = addEntity(*LU, "Entity2");
+ const auto E3 = addEntity(*LU, "Entity3");
+
+ int s1a = insertSummary<Analysis1EntitySummary>(*LU, "Analysis1", E1);
+ int s1b = insertSummary<Analysis1EntitySummary>(*LU, "Analysis1", E2);
+ int s2a = insertSummary<Analysis2EntitySummary>(*LU, "Analysis2", E2);
+ int s2b = insertSummary<Analysis2EntitySummary>(*LU, "Analysis2", E3);
+
+ // no registered builder
+ [[maybe_unused]] int s3a =
+ insertSummary<Analysis1EntitySummary>(*LU, "Analysis3", E1);
LUSummaryConsumer Consumer(std::move(LU));
Consumer.run();
- EXPECT_NE(Consumer.getView<MockView1>(), nullptr);
-}
+ // Analysis1
+ {
+ auto View1 = Consumer.getView<Analysis1View>();
+ ASSERT_NE(View1, nullptr);
-TEST_F(LUSummaryConsumerTest, GetView_SecondCallReturnsNull) {
- auto LU = makeLUSummary();
- auto Id = addEntity(*LU, "A");
- insertSummary(*LU, "Mock1", Id, std::make_unique<MockEntitySummary1>());
+ // getView ownership transfer — second call returns nullptr
+ EXPECT_EQ(Consumer.getView<Analysis1View>(), nullptr);
- LUSummaryConsumer Consumer(std::move(LU));
- Consumer.run();
+ EXPECT_EQ(View1->Entries.size(), 2u);
+ EXPECT_TRUE(hasEntry(View1->Entries, E1, s1a));
+ EXPECT_TRUE(hasEntry(View1->Entries, E2, s1b));
- auto First = Consumer.getView<MockView1>();
- EXPECT_NE(First, nullptr);
- EXPECT_EQ(Consumer.getView<MockView1>(), nullptr);
-}
+ EXPECT_TRUE(View1->WasFinalized);
-TEST_F(LUSummaryConsumerTest, GetView_UnregisteredViewReturnsNull) {
- // MockView1 is registered; a hypothetical OtherView is not.
- struct OtherView : public SummaryView {
- static SummaryName summaryName() { return SummaryName("Other"); }
- };
+ // Builder lifetime
+ EXPECT_TRUE(Analysis1BuilderWasDestroyed);
+ }
- auto LU = makeLUSummary();
- LUSummaryConsumer Consumer(std::move(LU));
- Consumer.run();
+ // Analysis2
+ {
+ auto View2 = Consumer.getView<Analysis2View>();
+ ASSERT_NE(View2, nullptr);
- EXPECT_EQ(Consumer.getView<OtherView>(), nullptr);
-}
+ // getView ownership transfer — second call returns nullptr
+ EXPECT_EQ(Consumer.getView<Analysis2View>(), nullptr);
-// ---------------------------------------------------------------------------
-// Builder lifetime
-// ---------------------------------------------------------------------------
+ EXPECT_EQ(View2->Entries.size(), 2u);
+ EXPECT_TRUE(hasEntry(View2->Entries, E2, s2a));
+ EXPECT_TRUE(hasEntry(View2->Entries, E3, s2b));
-TEST_F(LUSummaryConsumerTest, BuildersDestroyedAfterRun) {
- auto LU = makeLUSummary();
- auto Id1 = addEntity(*LU, "A");
- auto Id2 = addEntity(*LU, "B");
- insertSummary(*LU, "Mock1", Id1, std::make_unique<MockEntitySummary1>());
- insertSummary(*LU, "Mock2", Id2, std::make_unique<MockEntitySummary2>());
+ EXPECT_TRUE(View2->WasFinalized);
- LUSummaryConsumer Consumer(std::move(LU));
- Consumer.run();
+ // Builder lifetime
+ EXPECT_TRUE(Analysis2BuilderWasDestroyed);
+ }
+
+ // Analysis 3
+ {
+ // Unregistered builder — Analysis3 data silently skipped
+ EXPECT_EQ(Consumer.getView<Analysis3View>(), nullptr);
+ }
+
+ // Analysis4
+ {
+ // Registered builder but no data in LUSummary — builder never invoked
+ EXPECT_EQ(Consumer.getView<Analysis4View>(), nullptr);
- // Both destructors must have been called by the time run() returns.
- EXPECT_NE(
- MockBuilderLog.find("MockSummaryViewBuilder1 destructor was invoked"),
- std::string::npos);
- EXPECT_NE(
- MockBuilderLog.find("MockSummaryViewBuilder2 destructor was invoked"),
- std::string::npos);
+ // Builder lifetime
+ EXPECT_FALSE(Analysis4BuilderWasDestroyed);
+ }
}
} // namespace
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder1.cpp b/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder1.cpp
deleted file mode 100644
index 2bfc526969393..0000000000000
--- a/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder1.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-//===- MockSummaryViewBuilder1.cpp ----------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "MockSummaryViewBuilders.h"
-#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
-
-using namespace clang;
-using namespace ssaf;
-
-std::string clang::ssaf::MockBuilderLog;
-
-void clang::ssaf::clearMockBuilderLog() { MockBuilderLog.clear(); }
-
-namespace {
-
-class MockSummaryViewBuilder1
- : public SummaryViewBuilder<MockView1, MockEntitySummary1> {
-public:
- MockSummaryViewBuilder1() {
- MockBuilderLog += "MockSummaryViewBuilder1 constructor was invoked\n";
- }
-
- ~MockSummaryViewBuilder1() {
- MockBuilderLog += "MockSummaryViewBuilder1 destructor was invoked\n";
- }
-
- void addSummary(EntityId Id, std::unique_ptr<MockEntitySummary1>) override {
- MockBuilderLog += "MockSummaryViewBuilder1 addSummary was invoked\n";
- getView().Ids.push_back(Id);
- }
-
- void finalize() override {
- MockBuilderLog += "MockSummaryViewBuilder1 finalize was invoked\n";
- }
-};
-
-static SummaryViewBuilderRegistry::Add<MockSummaryViewBuilder1>
- Register("Mock1", "Mock view builder 1");
-
-} // namespace
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder2.cpp b/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder2.cpp
deleted file mode 100644
index b43e7f69bbb8e..0000000000000
--- a/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder2.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-//===- MockSummaryViewBuilder2.cpp ----------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "MockSummaryViewBuilders.h"
-#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
-
-using namespace clang;
-using namespace ssaf;
-
-namespace {
-
-class MockSummaryViewBuilder2
- : public SummaryViewBuilder<MockView2, MockEntitySummary2> {
-public:
- MockSummaryViewBuilder2() {
- MockBuilderLog += "MockSummaryViewBuilder2 constructor was invoked\n";
- }
-
- ~MockSummaryViewBuilder2() {
- MockBuilderLog += "MockSummaryViewBuilder2 destructor was invoked\n";
- }
-
- void addSummary(EntityId Id, std::unique_ptr<MockEntitySummary2>) override {
- MockBuilderLog += "MockSummaryViewBuilder2 addSummary was invoked\n";
- getView().Ids.push_back(Id);
- }
-
- void finalize() override {
- MockBuilderLog += "MockSummaryViewBuilder2 finalize was invoked\n";
- }
-};
-
-static SummaryViewBuilderRegistry::Add<MockSummaryViewBuilder2>
- Register("Mock2", "Mock view builder 2");
-
-} // namespace
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilders.h b/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilders.h
deleted file mode 100644
index 8bdcb27ca93f6..0000000000000
--- a/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilders.h
+++ /dev/null
@@ -1,67 +0,0 @@
-//===- MockSummaryViewBuilders.h ------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-//
-// Shared mock types for SummaryView tests. Two mock analyses ("Mock1" and
-// "Mock2") are defined here, with their builders registered in
-// MockSummaryViewBuilder1.cpp and MockSummaryViewBuilder2.cpp.
-//
-// Tests observe builder behaviour via MockBuilderLog, which records lifecycle
-// events (constructor, addSummary, finalize, destructor) as newline-terminated
-// strings. Call clearMockBuilderLog() in SetUp() to isolate tests.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef CLANG_UNITTESTS_ANALYSIS_SCALABLE_SUMMARYVIEW_MOCKSUMMARYVIEWBUILDERS_H
-#define CLANG_UNITTESTS_ANALYSIS_SCALABLE_SUMMARYVIEW_MOCKSUMMARYVIEWBUILDERS_H
-
-#include "clang/Analysis/Scalable/Model/EntityId.h"
-#include "clang/Analysis/Scalable/Model/SummaryName.h"
-#include "clang/Analysis/Scalable/SummaryView/SummaryView.h"
-#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h"
-#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
-#include <string>
-#include <vector>
-
-namespace clang::ssaf {
-
-// ---- Mock entity summaries -----------------------------------------------
-
-class MockEntitySummary1 : public EntitySummary {
-public:
- SummaryName getSummaryName() const override { return SummaryName("Mock1"); }
-};
-
-class MockEntitySummary2 : public EntitySummary {
-public:
- SummaryName getSummaryName() const override { return SummaryName("Mock2"); }
-};
-
-// ---- Mock views ------------------------------------------------------------
-
-class MockView1 : public SummaryView {
-public:
- static SummaryName summaryName() { return SummaryName("Mock1"); }
- std::vector<EntityId> Ids;
-};
-
-class MockView2 : public SummaryView {
-public:
- static SummaryName summaryName() { return SummaryName("Mock2"); }
- std::vector<EntityId> Ids;
-};
-
-// ---- Shared log ------------------------------------------------------------
-// Defined in MockSummaryViewBuilder1.cpp; written by both mock builders.
-
-extern std::string MockBuilderLog;
-
-void clearMockBuilderLog();
-
-} // namespace clang::ssaf
-
-#endif // CLANG_UNITTESTS_ANALYSIS_SCALABLE_SUMMARYVIEW_MOCKSUMMARYVIEWBUILDERS_H
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp b/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp
index b48682500cd39..30d3606247b2e 100644
--- a/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp
+++ b/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp
@@ -7,9 +7,9 @@
//===----------------------------------------------------------------------===//
#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
-#include "MockSummaryViewBuilders.h"
#include "llvm/ADT/StringRef.h"
#include "gtest/gtest.h"
+#include <memory>
#include <set>
using namespace clang;
@@ -17,15 +17,13 @@ using namespace ssaf;
namespace {
-class SummaryViewBuilderRegistryTest : public ::testing::Test {
-protected:
- void SetUp() override { clearMockBuilderLog(); }
-};
+class SummaryViewBuilderRegistryTest : public ::testing::Test {};
TEST_F(SummaryViewBuilderRegistryTest, isSummaryViewBuilderRegistered) {
EXPECT_FALSE(isSummaryViewBuilderRegistered("Non-existent-builder"));
- EXPECT_TRUE(isSummaryViewBuilderRegistered("Mock1"));
- EXPECT_TRUE(isSummaryViewBuilderRegistered("Mock2"));
+ EXPECT_TRUE(isSummaryViewBuilderRegistered("Analysis1"));
+ EXPECT_TRUE(isSummaryViewBuilderRegistered("Analysis2"));
+ EXPECT_TRUE(isSummaryViewBuilderRegistered("Analysis4"));
}
TEST_F(SummaryViewBuilderRegistryTest, EnumeratingRegistryEntries) {
@@ -35,48 +33,30 @@ TEST_F(SummaryViewBuilderRegistryTest, EnumeratingRegistryEntries) {
EXPECT_TRUE(Inserted);
}
- EXPECT_EQ(ActualNames, (std::set<llvm::StringRef>{"Mock1", "Mock2"}));
+ EXPECT_EQ(ActualNames,
+ (std::set<llvm::StringRef>{"Analysis1", "Analysis2", "Analysis4"}));
}
-TEST_F(SummaryViewBuilderRegistryTest, InstantiatingBuilder1) {
- {
- // Find Mock1 entry explicitly.
- std::unique_ptr<SummaryViewBuilderBase> B1;
- for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
- if (Entry.getName() == "Mock1") {
- B1 = Entry.instantiate();
- }
- }
-
- ASSERT_TRUE(B1);
- EXPECT_EQ(B1->summaryName(), SummaryName("Mock1"));
- EXPECT_NE(
- MockBuilderLog.find("MockSummaryViewBuilder1 constructor was invoked"),
- std::string::npos);
+TEST_F(SummaryViewBuilderRegistryTest, InstantiatingBuilder_Analysis1) {
+ std::unique_ptr<SummaryViewBuilderBase> B;
+ for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
+ if (Entry.getName() == "Analysis1")
+ B = Entry.instantiate();
}
- EXPECT_NE(
- MockBuilderLog.find("MockSummaryViewBuilder1 destructor was invoked"),
- std::string::npos);
-}
-TEST_F(SummaryViewBuilderRegistryTest, InstantiatingBuilder2) {
- {
- std::unique_ptr<SummaryViewBuilderBase> B2;
- for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
- if (Entry.getName() == "Mock2") {
- B2 = Entry.instantiate();
- }
- }
+ ASSERT_NE(B, nullptr);
+ EXPECT_EQ(B->summaryName(), SummaryName("Analysis1"));
+}
- ASSERT_TRUE(B2);
- EXPECT_EQ(B2->summaryName(), SummaryName("Mock2"));
- EXPECT_NE(
- MockBuilderLog.find("MockSummaryViewBuilder2 constructor was invoked"),
- std::string::npos);
+TEST_F(SummaryViewBuilderRegistryTest, InstantiatingBuilder_Analysis2) {
+ std::unique_ptr<SummaryViewBuilderBase> B;
+ for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
+ if (Entry.getName() == "Analysis2")
+ B = Entry.instantiate();
}
- EXPECT_NE(
- MockBuilderLog.find("MockSummaryViewBuilder2 destructor was invoked"),
- std::string::npos);
+
+ ASSERT_NE(B, nullptr);
+ EXPECT_EQ(B->summaryName(), SummaryName("Analysis2"));
}
} // namespace
>From c6f2af8315d60bdb5adbcdabe0f9f088fadba648 Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Wed, 11 Mar 2026 15:57:27 -0700
Subject: [PATCH 4/8] Fix
---
.../Scalable/SummaryView/LUSummaryConsumer.h | 33 +--
.../Scalable/SummaryView/SummaryView.h | 3 -
.../Scalable/SummaryView/SummaryViewBuilder.h | 58 ++--
.../SummaryView/SummaryViewBuilderRegistry.h | 50 ++--
.../Scalable/SummaryView/SummaryViewTraits.h | 33 +++
.../SummaryView/LUSummaryConsumer.cpp | 15 +-
.../SummaryViewBuilderRegistry.cpp | 17 +-
.../Analysis/Scalable/CMakeLists.txt | 3 +-
.../SummaryView/LUSummaryConsumerTest.cpp | 279 ------------------
.../SummaryViewBuilderRegistryTest.cpp | 62 ----
10 files changed, 115 insertions(+), 438 deletions(-)
create mode 100644 clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewTraits.h
delete mode 100644 clang/unittests/Analysis/Scalable/SummaryView/LUSummaryConsumerTest.cpp
delete mode 100644 clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h b/clang/include/clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h
index eb71b2add386d..a440f05999e0c 100644
--- a/clang/include/clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h
@@ -9,16 +9,6 @@
// LUSummaryConsumer constructs SummaryView objects by routing LUSummary entity
// data to the corresponding SummaryViewBuilder objects.
//
-// Typical usage:
-//
-// auto LU = Format->readLUSummary(Path);
-//
-// LUSummaryConsumer Consumer(std::move(LU));
-// Consumer.run();
-//
-// auto View = Consumer.getView<MyView>();
-// // View is std::unique_ptr<MyView>; Consumer no longer holds it.
-//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_LUSUMMARYCONSUMER_H
@@ -28,6 +18,7 @@
#include "clang/Analysis/Scalable/Model/EntityId.h"
#include "clang/Analysis/Scalable/Model/SummaryName.h"
#include "clang/Analysis/Scalable/SummaryView/SummaryView.h"
+#include "clang/Analysis/Scalable/SummaryView/SummaryViewTraits.h"
#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
#include <map>
#include <memory>
@@ -37,18 +28,14 @@ namespace clang::ssaf {
/// Consumes a LUSummary by dispatching its entity data to all registered
/// SummaryViewBuilders and collecting the resulting views.
class LUSummaryConsumer final {
- std::unique_ptr<LUSummary> LU;
- std::map<SummaryName, std::unique_ptr<SummaryView>> Views;
- bool WasRun = false;
-
public:
explicit LUSummaryConsumer(std::unique_ptr<LUSummary> LU)
: LU(std::move(LU)) {}
/// Instantiates a builder for each SummaryName present in the LUSummary,
- /// delivers its entities, finalizes it, and stores the resulting view.
- /// Each builder is fully processed (addSummary → finalize → getView) before
- /// the next SummaryName is visited. Builders are discarded on return.
+ /// delivers its entities, finalizes it, and stores the resulting view. Each
+ /// builder is fully processed before the next SummaryName is visited.
+ /// Builders are discarded on return.
///
/// \pre Must be called exactly once.
void run();
@@ -58,6 +45,11 @@ class LUSummaryConsumer final {
/// Returns nullptr if no builder for \p ViewT was registered or run() has
/// not been called. A second call for the same ViewT also returns nullptr.
template <typename ViewT> [[nodiscard]] std::unique_ptr<ViewT> getView() {
+ static_assert(std::is_base_of_v<SummaryView, ViewT>,
+ "ViewT must derive from SummaryView");
+ static_assert(HasSummaryName<ViewT>::value,
+ "ViewT must have a static summaryName() method");
+
auto It = Views.find(ViewT::summaryName());
if (It == Views.end()) {
return nullptr;
@@ -70,9 +62,10 @@ class LUSummaryConsumer final {
private:
using EntityDataMap = std::map<EntityId, std::unique_ptr<EntitySummary>>;
- /// Processes all entities for a single SummaryName: instantiates the
- /// registered builder, delivers entities, finalizes, and stores the view.
- /// Does nothing if no builder is registered for \p SN.
+ std::unique_ptr<LUSummary> LU;
+ std::map<SummaryName, std::unique_ptr<SummaryView>> Views;
+ bool WasRun = false;
+
void run(const SummaryName &SN, EntityDataMap &Data);
};
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h
index 46778e94976f3..dfbdab4814ac7 100644
--- a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h
@@ -18,9 +18,6 @@
namespace clang::ssaf {
/// Abstract base class for whole-program analysis views.
-///
-/// Concrete view classes inherit from this and add a static
-/// \c summaryName() method along with their analysis-specific query API.
class SummaryView {
public:
virtual ~SummaryView() = default;
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h
index c71652662f4d9..15c40236c0b99 100644
--- a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h
@@ -10,44 +10,33 @@
// registry and LUSummaryConsumer) and the typed intermediate template
// SummaryViewBuilder<ViewT, SummaryT> that concrete builders inherit from.
//
-// To implement a view builder, inherit from SummaryViewBuilder:
-//
-// class MyViewBuilder
-// : public SummaryViewBuilder<MyView, MyEntitySummary> {
-// public:
-// void addSummary(EntityId Id,
-// std::unique_ptr<MyEntitySummary> Summary) override {
-// // accumulate into getView()
-// }
-// // summaryName() and getView() && provided by the intermediate.
-// // override finalize() if post-processing is needed.
-// };
-//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDER_H
#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDER_H
#include "clang/Analysis/Scalable/Model/EntityId.h"
-#include "clang/Analysis/Scalable/Model/SummaryName.h"
#include "clang/Analysis/Scalable/SummaryView/SummaryView.h"
+#include "clang/Analysis/Scalable/SummaryView/SummaryViewTraits.h"
#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
#include <memory>
namespace clang::ssaf {
+class LUSummaryConsumer;
+
/// Abstract base class for all summary view builders.
///
/// Known to the registry and LUSummaryConsumer. Receives entities one at a
/// time via \c addSummary(), is finalized via \c finalize(), and transfers
-/// ownership of the built view via \c getView() &&.
+/// ownership of the built view via \c getView().
class SummaryViewBuilderBase {
+ friend class LUSummaryConsumer;
+
public:
virtual ~SummaryViewBuilderBase() = default;
- /// Returns the SummaryName this builder handles.
- virtual SummaryName summaryName() const = 0;
-
+private:
/// Called once per entity belonging to this builder's analysis.
/// Takes ownership of the summary data.
virtual void addSummary(EntityId Id,
@@ -56,41 +45,40 @@ class SummaryViewBuilderBase {
/// Called after all entities have been added.
virtual void finalize() {}
- /// Transfers ownership of the built view (type-erased).
- /// Called by LUSummaryConsumer after finalize(). The rvalue ref-qualifier
- /// enforces single use — the builder cannot be accessed after this call.
+ /// Transfers ownership of the built view. Called by LUSummaryConsumer after
+ /// finalize(). The rvalue ref-qualifier enforces single use — the builder
+ /// cannot be accessed after this call.
virtual std::unique_ptr<SummaryView> getView() && = 0;
};
/// Typed intermediate template that concrete builders inherit from.
-///
-/// Provides:
-/// - \c summaryName() derived from \c ViewT::summaryName().
-/// - \c getView() && which moves out the built view.
-/// - A protected \c getView() accessor for use during construction.
-/// - NVI dispatch: seals the base \c addSummary(EntityId, EntitySummary) as
-/// \c final, downcasts, and dispatches to the typed overload.
-///
/// Concrete builders only need to implement the typed
/// \c addSummary(EntityId, unique_ptr<SummaryT>) overload.
template <typename ViewT, typename SummaryT>
class SummaryViewBuilder : public SummaryViewBuilderBase {
- std::unique_ptr<ViewT> View;
+ static_assert(std::is_base_of_v<SummaryView, ViewT>,
+ "ViewT must derive from SummaryView");
+ static_assert(HasSummaryName<ViewT>::value,
+ "ViewT must have a static summaryName() method");
-protected:
- ViewT &getView() & { return *View; }
+ std::unique_ptr<ViewT> View;
public:
SummaryViewBuilder() : View(std::make_unique<ViewT>()) {}
- SummaryName summaryName() const override { return ViewT::summaryName(); }
-
- std::unique_ptr<SummaryView> getView() && override { return std::move(View); }
+ /// Returns the SummaryName of the view this builder produces.
+ /// Used by SummaryViewBuilderRegistry::Add to derive the registry entry name.
+ static SummaryName summaryName() { return ViewT::summaryName(); }
/// Typed customization point — concrete builders override this.
virtual void addSummary(EntityId Id, std::unique_ptr<SummaryT> Summary) = 0;
+protected:
+ ViewT &getView() & { return *View; }
+
private:
+ std::unique_ptr<SummaryView> getView() && override { return std::move(View); }
+
/// Seals the base overload, downcasts, and dispatches to the typed overload.
void addSummary(EntityId Id, std::unique_ptr<EntitySummary> Summary) final {
addSummary(Id, std::unique_ptr<SummaryT>(
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h
index d3fc08a71a446..1cfe6fa730eec 100644
--- a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h
@@ -6,14 +6,7 @@
//
//===----------------------------------------------------------------------===//
//
-// Registry for SummaryViewBuilders and helper functions.
-//
-// To register a builder, add this to the builder's translation unit:
-//
-// static SummaryViewBuilderRegistry::Add<MyViewBuilder>
-// Register("MyAnalysis", "View builder for MyAnalysis");
-//
-// The registry entry name must match MyView::summaryName().
+// Registry for SummaryViewBuilders.
//
//===----------------------------------------------------------------------===//
@@ -21,23 +14,40 @@
#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDERREGISTRY_H
#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h"
-#include "clang/Support/Compiler.h"
-#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Registry.h"
+#include <memory>
namespace clang::ssaf {
-/// Check if a SummaryViewBuilder was registered with a given name.
-bool isSummaryViewBuilderRegistered(llvm::StringRef Name);
-
-/// Registry for adding new SummaryViewBuilder implementations.
-using SummaryViewBuilderRegistry = llvm::Registry<SummaryViewBuilderBase>;
+/// Registry for SummaryViewBuilder implementations.
+///
+/// Provides an Add helper that derives the registry entry name from
+/// BuilderT::summaryName(), eliminating the possibility of registering a
+/// builder under the wrong name.
+class SummaryViewBuilderRegistry {
+ using RegistryT = llvm::Registry<SummaryViewBuilderBase>;
+
+public:
+ /// Registers \p BuilderT under the name returned by
+ /// \c BuilderT::summaryName(). Only a description is required.
+ template <typename BuilderT> struct Add {
+ explicit Add(llvm::StringRef Desc)
+ : Name(BuilderT::summaryName().str().str()), Node(Name, Desc) {}
+
+ private:
+ std::string Name;
+ RegistryT::Add<BuilderT> Node;
+ };
+
+ /// Returns true if a builder is registered under \p Name.
+ static bool isRegistered(llvm::StringRef Name);
+
+ /// Instantiates the builder registered under \p Name, or returns nullptr
+ /// if no such builder is registered.
+ static std::unique_ptr<SummaryViewBuilderBase>
+ instantiate(llvm::StringRef Name);
+};
} // namespace clang::ssaf
-namespace llvm {
-extern template class CLANG_TEMPLATE_ABI
- Registry<clang::ssaf::SummaryViewBuilderBase>;
-} // namespace llvm
-
#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDERREGISTRY_H
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewTraits.h b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewTraits.h
new file mode 100644
index 0000000000000..542714b8b21a7
--- /dev/null
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewTraits.h
@@ -0,0 +1,33 @@
+//===- SummaryViewTraits.h ------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Type traits for SummaryView subclasses.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWTRAITS_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWTRAITS_H
+
+#include "clang/Analysis/Scalable/Model/SummaryName.h"
+#include <type_traits>
+
+namespace clang::ssaf {
+
+/// Type trait that checks whether \p T has a static summaryName() method
+/// returning SummaryName. Used to enforce the convention on SummaryView
+/// subclasses at instantiation time.
+template <typename T, typename = void>
+struct HasSummaryName : std::false_type {};
+
+template <typename T>
+struct HasSummaryName<T, std::void_t<decltype(T::summaryName())>>
+ : std::is_same<decltype(T::summaryName()), SummaryName> {};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWTRAITS_H
diff --git a/clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp b/clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp
index a3db29be840d6..c742879c9eb93 100644
--- a/clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp
+++ b/clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp
@@ -14,25 +14,12 @@
using namespace clang;
using namespace ssaf;
-static std::unique_ptr<SummaryViewBuilderBase>
-instantiateBuilder(const SummaryName &SN) {
- for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
- if (Entry.getName() == SN.str()) {
- return Entry.instantiate();
- }
- }
- return nullptr;
-}
-
void LUSummaryConsumer::run(const SummaryName &SN, EntityDataMap &Data) {
- auto Builder = instantiateBuilder(SN);
+ auto Builder = SummaryViewBuilderRegistry::instantiate(SN.str());
if (!Builder) {
return;
}
- assert(Builder->summaryName() == SN &&
- "registry entry name must match SummaryViewBuilder::summaryName()");
-
for (auto &[Id, Summary] : Data) {
Builder->addSummary(Id, std::move(Summary));
}
diff --git a/clang/lib/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.cpp b/clang/lib/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.cpp
index eb64394ba4401..7d7ef13a70c01 100644
--- a/clang/lib/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.cpp
+++ b/clang/lib/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.cpp
@@ -11,10 +11,21 @@
using namespace clang;
using namespace ssaf;
-LLVM_INSTANTIATE_REGISTRY(SummaryViewBuilderRegistry)
+using RegistryT = llvm::Registry<SummaryViewBuilderBase>;
+LLVM_INSTANTIATE_REGISTRY(RegistryT)
-bool ssaf::isSummaryViewBuilderRegistered(llvm::StringRef Name) {
- for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
+std::unique_ptr<SummaryViewBuilderBase>
+SummaryViewBuilderRegistry::instantiate(llvm::StringRef Name) {
+ for (const auto &Entry : RegistryT::entries()) {
+ if (Entry.getName() == Name) {
+ return Entry.instantiate();
+ }
+ }
+ return nullptr;
+}
+
+bool SummaryViewBuilderRegistry::isRegistered(llvm::StringRef Name) {
+ for (const auto &Entry : RegistryT::entries()) {
if (Entry.getName() == Name) {
return true;
}
diff --git a/clang/unittests/Analysis/Scalable/CMakeLists.txt b/clang/unittests/Analysis/Scalable/CMakeLists.txt
index 41e99dcca5fd2..7af03a9aec1c2 100644
--- a/clang/unittests/Analysis/Scalable/CMakeLists.txt
+++ b/clang/unittests/Analysis/Scalable/CMakeLists.txt
@@ -19,8 +19,7 @@ add_distinct_clang_unittest(ClangScalableAnalysisTests
Serialization/JSONFormatTest/LUSummaryTest.cpp
Serialization/JSONFormatTest/TUSummaryTest.cpp
SummaryNameTest.cpp
- SummaryView/LUSummaryConsumerTest.cpp
- SummaryView/SummaryViewBuilderRegistryTest.cpp
+ SummaryView/SummaryViewTest.cpp
TestFixture.cpp
TUSummaryBuilderTest.cpp
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/LUSummaryConsumerTest.cpp b/clang/unittests/Analysis/Scalable/SummaryView/LUSummaryConsumerTest.cpp
deleted file mode 100644
index 3499d5bda1847..0000000000000
--- a/clang/unittests/Analysis/Scalable/SummaryView/LUSummaryConsumerTest.cpp
+++ /dev/null
@@ -1,279 +0,0 @@
-//===- LUSummaryConsumerTest.cpp ------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h"
-#include "../TestFixture.h"
-#include "clang/Analysis/Scalable/EntityLinker/LUSummary.h"
-#include "clang/Analysis/Scalable/Model/BuildNamespace.h"
-#include "clang/Analysis/Scalable/Model/EntityId.h"
-#include "clang/Analysis/Scalable/Model/EntityIdTable.h"
-#include "clang/Analysis/Scalable/Model/EntityLinkage.h"
-#include "clang/Analysis/Scalable/Model/EntityName.h"
-#include "clang/Analysis/Scalable/Model/SummaryName.h"
-#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
-#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
-#include "gtest/gtest.h"
-#include <algorithm>
-#include <memory>
-#include <utility>
-#include <vector>
-
-using namespace clang;
-using namespace ssaf;
-
-namespace {
-
-// ---------------------------------------------------------------------------
-// Instance counter
-// ---------------------------------------------------------------------------
-
-static int NextSummaryInstanceId = 0;
-
-// ---------------------------------------------------------------------------
-// Entity summaries
-// ---------------------------------------------------------------------------
-
-class Analysis1EntitySummary : public EntitySummary {
-public:
- int InstanceId = NextSummaryInstanceId++;
- SummaryName getSummaryName() const override {
- return SummaryName("Analysis1");
- }
-};
-
-class Analysis2EntitySummary : public EntitySummary {
-public:
- int InstanceId = NextSummaryInstanceId++;
- SummaryName getSummaryName() const override {
- return SummaryName("Analysis2");
- }
-};
-
-class Analysis4EntitySummary : public EntitySummary {
-public:
- int InstanceId = NextSummaryInstanceId++;
- SummaryName getSummaryName() const override {
- return SummaryName("Analysis4");
- }
-};
-
-// ---------------------------------------------------------------------------
-// Views
-// ---------------------------------------------------------------------------
-
-class Analysis1View : public SummaryView {
-public:
- static SummaryName summaryName() { return SummaryName("Analysis1"); }
- std::vector<std::pair<EntityId, int>> Entries;
- bool WasFinalized = false;
-};
-
-class Analysis2View : public SummaryView {
-public:
- static SummaryName summaryName() { return SummaryName("Analysis2"); }
- std::vector<std::pair<EntityId, int>> Entries;
- bool WasFinalized = false;
-};
-
-// No builder or registration for Analysis3. Data for Analysis3 is inserted
-// into the LUSummary to verify the consumer silently skips it.
-class Analysis3View : public SummaryView {
-public:
- static SummaryName summaryName() { return SummaryName("Analysis3"); }
-};
-
-// Analysis4 has a registered builder but no data is inserted into the
-// LUSummary, so the builder is never invoked and getView returns nullptr.
-class Analysis4View : public SummaryView {
-public:
- static SummaryName summaryName() { return SummaryName("Analysis4"); }
-};
-
-// ---------------------------------------------------------------------------
-// Builder destruction flags (reset in SetUp)
-// ---------------------------------------------------------------------------
-
-static bool Analysis1BuilderWasDestroyed = false;
-static bool Analysis2BuilderWasDestroyed = false;
-static bool Analysis4BuilderWasDestroyed = false;
-
-// ---------------------------------------------------------------------------
-// Builders
-// ---------------------------------------------------------------------------
-
-class Analysis1Builder
- : public SummaryViewBuilder<Analysis1View, Analysis1EntitySummary> {
-public:
- ~Analysis1Builder() { Analysis1BuilderWasDestroyed = true; }
-
- void addSummary(EntityId Id,
- std::unique_ptr<Analysis1EntitySummary> S) override {
- getView().Entries.push_back({Id, S->InstanceId});
- }
-
- void finalize() override { getView().WasFinalized = true; }
-};
-
-static SummaryViewBuilderRegistry::Add<Analysis1Builder>
- RegAnalysis1("Analysis1", "Builder for Analysis1");
-
-class Analysis2Builder
- : public SummaryViewBuilder<Analysis2View, Analysis2EntitySummary> {
-public:
- ~Analysis2Builder() { Analysis2BuilderWasDestroyed = true; }
-
- void addSummary(EntityId Id,
- std::unique_ptr<Analysis2EntitySummary> S) override {
- getView().Entries.push_back({Id, S->InstanceId});
- }
-
- void finalize() override { getView().WasFinalized = true; }
-};
-
-static SummaryViewBuilderRegistry::Add<Analysis2Builder>
- RegAnalysis2("Analysis2", "Builder for Analysis2");
-
-class Analysis4Builder
- : public SummaryViewBuilder<Analysis4View, Analysis4EntitySummary> {
-public:
- ~Analysis4Builder() { Analysis4BuilderWasDestroyed = true; }
-
- void addSummary(EntityId Id,
- std::unique_ptr<Analysis4EntitySummary> S) override {
- getView().Entries.push_back({Id, S->InstanceId});
- }
-
- void finalize() override { getView().WasFinalized = true; }
-};
-
-static SummaryViewBuilderRegistry::Add<Analysis4Builder>
- RegAnalysis4("Analysis4", "Builder for Analysis4");
-
-// ---------------------------------------------------------------------------
-// Fixture
-// ---------------------------------------------------------------------------
-
-class LUSummaryConsumerTest : public TestFixture {
-protected:
- static constexpr EntityLinkage ExternalLinkage =
- EntityLinkage(EntityLinkageType::External);
-
- void SetUp() override {
- NextSummaryInstanceId = 0;
- Analysis1BuilderWasDestroyed = false;
- Analysis2BuilderWasDestroyed = false;
- Analysis4BuilderWasDestroyed = false;
- }
-
- std::unique_ptr<LUSummary> makeLUSummary() {
- NestedBuildNamespace NS(
- {BuildNamespace(BuildNamespaceKind::LinkUnit, "TestLU")});
- return std::make_unique<LUSummary>(std::move(NS));
- }
-
- EntityId addEntity(LUSummary &LU, llvm::StringRef USR) {
- NestedBuildNamespace NS(
- {BuildNamespace(BuildNamespaceKind::LinkUnit, "TestLU")});
- EntityName Name(USR.str(), "", NS);
- EntityId Id = getIdTable(LU).getId(Name);
- getLinkageTable(LU).insert({Id, ExternalLinkage});
- return Id;
- }
-
- static bool hasEntry(const std::vector<std::pair<EntityId, int>> &Entries,
- EntityId Id, int InstanceId) {
- return std::find(Entries.begin(), Entries.end(),
- std::make_pair(Id, InstanceId)) != Entries.end();
- }
-
- /// Inserts a freshly constructed SummaryT for the given entity and returns
- /// the summary's InstanceId so the test can verify delivery later.
- template <typename SummaryT>
- int insertSummary(LUSummary &LU, llvm::StringRef SN, EntityId Id) {
- auto S = std::make_unique<SummaryT>();
- int InstanceId = S->InstanceId;
- getData(LU)[SummaryName(SN.str())][Id] = std::move(S);
- return InstanceId;
- }
-};
-
-// ---------------------------------------------------------------------------
-// Tests
-// ---------------------------------------------------------------------------
-
-TEST_F(LUSummaryConsumerTest, Run) {
- auto LU = makeLUSummary();
- const auto E1 = addEntity(*LU, "Entity1");
- const auto E2 = addEntity(*LU, "Entity2");
- const auto E3 = addEntity(*LU, "Entity3");
-
- int s1a = insertSummary<Analysis1EntitySummary>(*LU, "Analysis1", E1);
- int s1b = insertSummary<Analysis1EntitySummary>(*LU, "Analysis1", E2);
- int s2a = insertSummary<Analysis2EntitySummary>(*LU, "Analysis2", E2);
- int s2b = insertSummary<Analysis2EntitySummary>(*LU, "Analysis2", E3);
-
- // no registered builder
- [[maybe_unused]] int s3a =
- insertSummary<Analysis1EntitySummary>(*LU, "Analysis3", E1);
-
- LUSummaryConsumer Consumer(std::move(LU));
- Consumer.run();
-
- // Analysis1
- {
- auto View1 = Consumer.getView<Analysis1View>();
- ASSERT_NE(View1, nullptr);
-
- // getView ownership transfer — second call returns nullptr
- EXPECT_EQ(Consumer.getView<Analysis1View>(), nullptr);
-
- EXPECT_EQ(View1->Entries.size(), 2u);
- EXPECT_TRUE(hasEntry(View1->Entries, E1, s1a));
- EXPECT_TRUE(hasEntry(View1->Entries, E2, s1b));
-
- EXPECT_TRUE(View1->WasFinalized);
-
- // Builder lifetime
- EXPECT_TRUE(Analysis1BuilderWasDestroyed);
- }
-
- // Analysis2
- {
- auto View2 = Consumer.getView<Analysis2View>();
- ASSERT_NE(View2, nullptr);
-
- // getView ownership transfer — second call returns nullptr
- EXPECT_EQ(Consumer.getView<Analysis2View>(), nullptr);
-
- EXPECT_EQ(View2->Entries.size(), 2u);
- EXPECT_TRUE(hasEntry(View2->Entries, E2, s2a));
- EXPECT_TRUE(hasEntry(View2->Entries, E3, s2b));
-
- EXPECT_TRUE(View2->WasFinalized);
-
- // Builder lifetime
- EXPECT_TRUE(Analysis2BuilderWasDestroyed);
- }
-
- // Analysis 3
- {
- // Unregistered builder — Analysis3 data silently skipped
- EXPECT_EQ(Consumer.getView<Analysis3View>(), nullptr);
- }
-
- // Analysis4
- {
- // Registered builder but no data in LUSummary — builder never invoked
- EXPECT_EQ(Consumer.getView<Analysis4View>(), nullptr);
-
- // Builder lifetime
- EXPECT_FALSE(Analysis4BuilderWasDestroyed);
- }
-}
-
-} // namespace
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp b/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp
deleted file mode 100644
index 30d3606247b2e..0000000000000
--- a/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-//===- SummaryViewBuilderRegistryTest.cpp ---------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
-#include "llvm/ADT/StringRef.h"
-#include "gtest/gtest.h"
-#include <memory>
-#include <set>
-
-using namespace clang;
-using namespace ssaf;
-
-namespace {
-
-class SummaryViewBuilderRegistryTest : public ::testing::Test {};
-
-TEST_F(SummaryViewBuilderRegistryTest, isSummaryViewBuilderRegistered) {
- EXPECT_FALSE(isSummaryViewBuilderRegistered("Non-existent-builder"));
- EXPECT_TRUE(isSummaryViewBuilderRegistered("Analysis1"));
- EXPECT_TRUE(isSummaryViewBuilderRegistered("Analysis2"));
- EXPECT_TRUE(isSummaryViewBuilderRegistered("Analysis4"));
-}
-
-TEST_F(SummaryViewBuilderRegistryTest, EnumeratingRegistryEntries) {
- std::set<llvm::StringRef> ActualNames;
- for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
- bool Inserted = ActualNames.insert(Entry.getName()).second;
- EXPECT_TRUE(Inserted);
- }
-
- EXPECT_EQ(ActualNames,
- (std::set<llvm::StringRef>{"Analysis1", "Analysis2", "Analysis4"}));
-}
-
-TEST_F(SummaryViewBuilderRegistryTest, InstantiatingBuilder_Analysis1) {
- std::unique_ptr<SummaryViewBuilderBase> B;
- for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
- if (Entry.getName() == "Analysis1")
- B = Entry.instantiate();
- }
-
- ASSERT_NE(B, nullptr);
- EXPECT_EQ(B->summaryName(), SummaryName("Analysis1"));
-}
-
-TEST_F(SummaryViewBuilderRegistryTest, InstantiatingBuilder_Analysis2) {
- std::unique_ptr<SummaryViewBuilderBase> B;
- for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
- if (Entry.getName() == "Analysis2")
- B = Entry.instantiate();
- }
-
- ASSERT_NE(B, nullptr);
- EXPECT_EQ(B->summaryName(), SummaryName("Analysis2"));
-}
-
-} // namespace
>From 883b8f55d425c7cb49cf6a787797de06c3e92f43 Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Wed, 11 Mar 2026 16:18:15 -0700
Subject: [PATCH 5/8] Fix
---
.../Analysis/Scalable/SummaryView/SummaryViewBuilder.h | 6 ++++--
.../Scalable/SummaryView/SummaryViewBuilderRegistry.h | 10 ++++++++++
.../Scalable/SummaryView/LUSummaryConsumer.cpp | 1 -
3 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h
index 15c40236c0b99..14231eec17753 100644
--- a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h
@@ -16,6 +16,7 @@
#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDER_H
#include "clang/Analysis/Scalable/Model/EntityId.h"
+#include "clang/Analysis/Scalable/Model/SummaryName.h"
#include "clang/Analysis/Scalable/SummaryView/SummaryView.h"
#include "clang/Analysis/Scalable/SummaryView/SummaryViewTraits.h"
#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
@@ -52,8 +53,9 @@ class SummaryViewBuilderBase {
};
/// Typed intermediate template that concrete builders inherit from.
-/// Concrete builders only need to implement the typed
-/// \c addSummary(EntityId, unique_ptr<SummaryT>) overload.
+/// Concrete builders must implement the typed
+/// \c addSummary(EntityId, unique_ptr<SummaryT>) overload, and may override
+/// \c finalize() for any post-processing needed after all entities are added.
template <typename ViewT, typename SummaryT>
class SummaryViewBuilder : public SummaryViewBuilderBase {
static_assert(std::is_base_of_v<SummaryView, ViewT>,
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h
index 1cfe6fa730eec..2c84ee1573874 100644
--- a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h
@@ -8,6 +8,15 @@
//
// Registry for SummaryViewBuilders.
//
+// To register a builder, add a static Add<BuilderT> in the builder's
+// translation unit:
+//
+// static SummaryViewBuilderRegistry::Add<MyViewBuilder>
+// Registered("View builder for MyAnalysis");
+//
+// The registry entry name is derived automatically from
+// MyViewBuilder::summaryName(), which returns MyView::summaryName().
+//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDERREGISTRY_H
@@ -16,6 +25,7 @@
#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h"
#include "llvm/Support/Registry.h"
#include <memory>
+#include <string>
namespace clang::ssaf {
diff --git a/clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp b/clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp
index c742879c9eb93..1be43eee5e923 100644
--- a/clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp
+++ b/clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp
@@ -9,7 +9,6 @@
#include "clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h"
#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
#include <cassert>
-#include <memory>
using namespace clang;
using namespace ssaf;
>From 37098472c07db53b56ca3bda2ddc37bba83b38d0 Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Wed, 11 Mar 2026 16:23:10 -0700
Subject: [PATCH 6/8] Turns out you need the test file to actually run the
tests on CI.
---
.../Scalable/SummaryView/SummaryViewTest.cpp | 295 ++++++++++++++++++
1 file changed, 295 insertions(+)
create mode 100644 clang/unittests/Analysis/Scalable/SummaryView/SummaryViewTest.cpp
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewTest.cpp b/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewTest.cpp
new file mode 100644
index 0000000000000..3b89e2f6d6966
--- /dev/null
+++ b/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewTest.cpp
@@ -0,0 +1,295 @@
+//===- LUSummaryConsumerTest.cpp ------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "../TestFixture.h"
+#include "clang/Analysis/Scalable/EntityLinker/LUSummary.h"
+#include "clang/Analysis/Scalable/Model/BuildNamespace.h"
+#include "clang/Analysis/Scalable/Model/EntityId.h"
+#include "clang/Analysis/Scalable/Model/EntityIdTable.h"
+#include "clang/Analysis/Scalable/Model/EntityLinkage.h"
+#include "clang/Analysis/Scalable/Model/EntityName.h"
+#include "clang/Analysis/Scalable/Model/SummaryName.h"
+#include "clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h"
+#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
+#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
+#include "gtest/gtest.h"
+#include <algorithm>
+#include <memory>
+#include <utility>
+#include <vector>
+
+using namespace clang;
+using namespace ssaf;
+
+namespace {
+
+// ---------------------------------------------------------------------------
+// Instance counter
+// ---------------------------------------------------------------------------
+
+static int NextSummaryInstanceId = 0;
+
+// ---------------------------------------------------------------------------
+// Entity summaries
+// ---------------------------------------------------------------------------
+
+class Analysis1EntitySummary : public EntitySummary {
+public:
+ int InstanceId = NextSummaryInstanceId++;
+ SummaryName getSummaryName() const override {
+ return SummaryName("Analysis1");
+ }
+};
+
+class Analysis2EntitySummary : public EntitySummary {
+public:
+ int InstanceId = NextSummaryInstanceId++;
+ SummaryName getSummaryName() const override {
+ return SummaryName("Analysis2");
+ }
+};
+
+class Analysis4EntitySummary : public EntitySummary {
+public:
+ int InstanceId = NextSummaryInstanceId++;
+ SummaryName getSummaryName() const override {
+ return SummaryName("Analysis4");
+ }
+};
+
+// ---------------------------------------------------------------------------
+// Views
+// ---------------------------------------------------------------------------
+
+class Analysis1View : public SummaryView {
+public:
+ static SummaryName summaryName() { return SummaryName("Analysis1"); }
+ std::vector<std::pair<EntityId, int>> Entries;
+ bool WasFinalized = false;
+};
+
+class Analysis2View : public SummaryView {
+public:
+ static SummaryName summaryName() { return SummaryName("Analysis2"); }
+ std::vector<std::pair<EntityId, int>> Entries;
+ bool WasFinalized = false;
+};
+
+// No builder or registration for Analysis3. Data for Analysis3 is inserted
+// into the LUSummary to verify the consumer silently skips it.
+class Analysis3View : public SummaryView {
+public:
+ static SummaryName summaryName() { return SummaryName("Analysis3"); }
+};
+
+// Analysis4 has a registered builder but no data is inserted into the
+// LUSummary, so the builder is never invoked and getView returns nullptr.
+class Analysis4View : public SummaryView {
+public:
+ static SummaryName summaryName() { return SummaryName("Analysis4"); }
+ std::vector<std::pair<EntityId, int>> Entries;
+ bool WasFinalized = false;
+};
+
+// ---------------------------------------------------------------------------
+// Builder destruction flags (reset in SetUp)
+// ---------------------------------------------------------------------------
+
+static bool Analysis1BuilderWasDestroyed = false;
+static bool Analysis2BuilderWasDestroyed = false;
+static bool Analysis4BuilderWasDestroyed = false;
+
+// ---------------------------------------------------------------------------
+// Builders
+// ---------------------------------------------------------------------------
+
+class Analysis1Builder
+ : public SummaryViewBuilder<Analysis1View, Analysis1EntitySummary> {
+public:
+ ~Analysis1Builder() { Analysis1BuilderWasDestroyed = true; }
+
+ void addSummary(EntityId Id,
+ std::unique_ptr<Analysis1EntitySummary> S) override {
+ getView().Entries.push_back({Id, S->InstanceId});
+ }
+
+ void finalize() override { getView().WasFinalized = true; }
+};
+
+static SummaryViewBuilderRegistry::Add<Analysis1Builder>
+ RegAnalysis1("Builder for Analysis1");
+
+class Analysis2Builder
+ : public SummaryViewBuilder<Analysis2View, Analysis2EntitySummary> {
+public:
+ ~Analysis2Builder() { Analysis2BuilderWasDestroyed = true; }
+
+ void addSummary(EntityId Id,
+ std::unique_ptr<Analysis2EntitySummary> S) override {
+ getView().Entries.push_back({Id, S->InstanceId});
+ }
+
+ void finalize() override { getView().WasFinalized = true; }
+};
+
+static SummaryViewBuilderRegistry::Add<Analysis2Builder>
+ RegAnalysis2("Builder for Analysis2");
+
+class Analysis4Builder
+ : public SummaryViewBuilder<Analysis4View, Analysis4EntitySummary> {
+public:
+ ~Analysis4Builder() { Analysis4BuilderWasDestroyed = true; }
+
+ void addSummary(EntityId Id,
+ std::unique_ptr<Analysis4EntitySummary> S) override {
+ getView().Entries.push_back({Id, S->InstanceId});
+ }
+
+ void finalize() override { getView().WasFinalized = true; }
+};
+
+static SummaryViewBuilderRegistry::Add<Analysis4Builder>
+ RegAnalysis4("Builder for Analysis4");
+
+// ---------------------------------------------------------------------------
+// Fixture
+// ---------------------------------------------------------------------------
+
+class LUSummaryConsumerTest : public TestFixture {
+protected:
+ static constexpr EntityLinkage ExternalLinkage =
+ EntityLinkage(EntityLinkageType::External);
+
+ void SetUp() override {
+ NextSummaryInstanceId = 0;
+ Analysis1BuilderWasDestroyed = false;
+ Analysis2BuilderWasDestroyed = false;
+ Analysis4BuilderWasDestroyed = false;
+ }
+
+ std::unique_ptr<LUSummary> makeLUSummary() {
+ NestedBuildNamespace NS(
+ {BuildNamespace(BuildNamespaceKind::LinkUnit, "TestLU")});
+ return std::make_unique<LUSummary>(std::move(NS));
+ }
+
+ EntityId addEntity(LUSummary &LU, llvm::StringRef USR) {
+ NestedBuildNamespace NS(
+ {BuildNamespace(BuildNamespaceKind::LinkUnit, "TestLU")});
+ EntityName Name(USR.str(), "", NS);
+ EntityId Id = getIdTable(LU).getId(Name);
+ getLinkageTable(LU).insert({Id, ExternalLinkage});
+ return Id;
+ }
+
+ static bool hasEntry(const std::vector<std::pair<EntityId, int>> &Entries,
+ EntityId Id, int InstanceId) {
+ return std::find(Entries.begin(), Entries.end(),
+ std::make_pair(Id, InstanceId)) != Entries.end();
+ }
+
+ /// Inserts a freshly constructed SummaryT for the given entity and returns
+ /// the summary's InstanceId so the test can verify delivery later.
+ template <typename SummaryT>
+ int insertSummary(LUSummary &LU, llvm::StringRef SN, EntityId Id) {
+ auto S = std::make_unique<SummaryT>();
+ int InstanceId = S->InstanceId;
+ getData(LU)[SummaryName(SN.str())][Id] = std::move(S);
+ return InstanceId;
+ }
+};
+
+// ---------------------------------------------------------------------------
+// Tests
+// ---------------------------------------------------------------------------
+
+TEST(SummaryViewBuilderRegistryTest, BuilderIsRegistered) {
+ EXPECT_FALSE(SummaryViewBuilderRegistry::isRegistered("Analysis10"));
+ EXPECT_TRUE(SummaryViewBuilderRegistry::isRegistered("Analysis1"));
+ EXPECT_TRUE(SummaryViewBuilderRegistry::isRegistered("Analysis2"));
+ EXPECT_TRUE(SummaryViewBuilderRegistry::isRegistered("Analysis4"));
+}
+
+TEST(SummaryViewBuilderRegistryTest, BuilderCanBeInstantiated) {
+ EXPECT_EQ(SummaryViewBuilderRegistry::instantiate("Analysis10"), nullptr);
+ EXPECT_NE(SummaryViewBuilderRegistry::instantiate("Analysis1"), nullptr);
+ EXPECT_NE(SummaryViewBuilderRegistry::instantiate("Analysis2"), nullptr);
+ EXPECT_NE(SummaryViewBuilderRegistry::instantiate("Analysis4"), nullptr);
+}
+
+TEST_F(LUSummaryConsumerTest, Run) {
+ auto LU = makeLUSummary();
+ const auto E1 = addEntity(*LU, "Entity1");
+ const auto E2 = addEntity(*LU, "Entity2");
+ const auto E3 = addEntity(*LU, "Entity3");
+
+ int s1a = insertSummary<Analysis1EntitySummary>(*LU, "Analysis1", E1);
+ int s1b = insertSummary<Analysis1EntitySummary>(*LU, "Analysis1", E2);
+ int s2a = insertSummary<Analysis2EntitySummary>(*LU, "Analysis2", E2);
+ int s2b = insertSummary<Analysis2EntitySummary>(*LU, "Analysis2", E3);
+
+ // no registered builder
+ [[maybe_unused]] int s3a =
+ insertSummary<Analysis1EntitySummary>(*LU, "Analysis3", E1);
+
+ LUSummaryConsumer Consumer(std::move(LU));
+ Consumer.run();
+
+ // Analysis1
+ {
+ auto View1 = Consumer.getView<Analysis1View>();
+ ASSERT_NE(View1, nullptr);
+
+ // getView ownership transfer — second call returns nullptr
+ EXPECT_EQ(Consumer.getView<Analysis1View>(), nullptr);
+
+ EXPECT_EQ(View1->Entries.size(), 2u);
+ EXPECT_TRUE(hasEntry(View1->Entries, E1, s1a));
+ EXPECT_TRUE(hasEntry(View1->Entries, E2, s1b));
+
+ EXPECT_TRUE(View1->WasFinalized);
+
+ // Builder lifetime
+ EXPECT_TRUE(Analysis1BuilderWasDestroyed);
+ }
+
+ // Analysis2
+ {
+ auto View2 = Consumer.getView<Analysis2View>();
+ ASSERT_NE(View2, nullptr);
+
+ // getView ownership transfer — second call returns nullptr
+ EXPECT_EQ(Consumer.getView<Analysis2View>(), nullptr);
+
+ EXPECT_EQ(View2->Entries.size(), 2u);
+ EXPECT_TRUE(hasEntry(View2->Entries, E2, s2a));
+ EXPECT_TRUE(hasEntry(View2->Entries, E3, s2b));
+
+ EXPECT_TRUE(View2->WasFinalized);
+
+ // Builder lifetime
+ EXPECT_TRUE(Analysis2BuilderWasDestroyed);
+ }
+
+ // Analysis 3
+ {
+ // Unregistered builder — Analysis3 data silently skipped
+ EXPECT_EQ(Consumer.getView<Analysis3View>(), nullptr);
+ }
+
+ // Analysis4
+ {
+ // Registered builder but no data in LUSummary — builder never invoked
+ EXPECT_EQ(Consumer.getView<Analysis4View>(), nullptr);
+
+ // Builder lifetime
+ EXPECT_FALSE(Analysis4BuilderWasDestroyed);
+ }
+}
+
+} // namespace
>From 9ec4a63f5c732bef2e5c342cf03a2dc2abd4bb9e Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Wed, 11 Mar 2026 18:56:10 -0700
Subject: [PATCH 7/8] Rename
---
.../LUSummaryConsumer.h | 46 ++++----
.../SummaryData.h} | 16 +--
.../SummaryDataBuilder.h} | 54 +++++-----
.../SummaryDataBuilderRegistry.h} | 26 ++---
.../SummaryDataTraits.h} | 12 +--
clang/lib/Analysis/Scalable/CMakeLists.txt | 4 +-
.../LUSummaryConsumer.cpp | 16 +--
.../SummaryDataBuilderRegistry.cpp} | 12 +--
.../Analysis/Scalable/CMakeLists.txt | 2 +-
.../SummaryDataTest.cpp} | 100 +++++++++---------
10 files changed, 144 insertions(+), 144 deletions(-)
rename clang/include/clang/Analysis/Scalable/{SummaryView => SummaryData}/LUSummaryConsumer.h (55%)
rename clang/include/clang/Analysis/Scalable/{SummaryView/SummaryView.h => SummaryData/SummaryData.h} (56%)
rename clang/include/clang/Analysis/Scalable/{SummaryView/SummaryViewBuilder.h => SummaryData/SummaryDataBuilder.h} (58%)
rename clang/include/clang/Analysis/Scalable/{SummaryView/SummaryViewBuilderRegistry.h => SummaryData/SummaryDataBuilderRegistry.h} (67%)
rename clang/include/clang/Analysis/Scalable/{SummaryView/SummaryViewTraits.h => SummaryData/SummaryDataTraits.h} (75%)
rename clang/lib/Analysis/Scalable/{SummaryView => SummaryData}/LUSummaryConsumer.cpp (66%)
rename clang/lib/Analysis/Scalable/{SummaryView/SummaryViewBuilderRegistry.cpp => SummaryData/SummaryDataBuilderRegistry.cpp} (66%)
rename clang/unittests/Analysis/Scalable/{SummaryView/SummaryViewTest.cpp => SummaryData/SummaryDataTest.cpp} (73%)
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h b/clang/include/clang/Analysis/Scalable/SummaryData/LUSummaryConsumer.h
similarity index 55%
rename from clang/include/clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h
rename to clang/include/clang/Analysis/Scalable/SummaryData/LUSummaryConsumer.h
index a440f05999e0c..8e93ff025f2f6 100644
--- a/clang/include/clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h
+++ b/clang/include/clang/Analysis/Scalable/SummaryData/LUSummaryConsumer.h
@@ -6,19 +6,19 @@
//
//===----------------------------------------------------------------------===//
//
-// LUSummaryConsumer constructs SummaryView objects by routing LUSummary entity
-// data to the corresponding SummaryViewBuilder objects.
+// LUSummaryConsumer constructs SummaryData objects by routing LUSummary entity
+// data to the corresponding SummaryDataBuilder objects.
//
//===----------------------------------------------------------------------===//
-#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_LUSUMMARYCONSUMER_H
-#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_LUSUMMARYCONSUMER_H
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_LUSUMMARYCONSUMER_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_LUSUMMARYCONSUMER_H
#include "clang/Analysis/Scalable/EntityLinker/LUSummary.h"
#include "clang/Analysis/Scalable/Model/EntityId.h"
#include "clang/Analysis/Scalable/Model/SummaryName.h"
-#include "clang/Analysis/Scalable/SummaryView/SummaryView.h"
-#include "clang/Analysis/Scalable/SummaryView/SummaryViewTraits.h"
+#include "clang/Analysis/Scalable/SummaryData/SummaryData.h"
+#include "clang/Analysis/Scalable/SummaryData/SummaryDataTraits.h"
#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
#include <map>
#include <memory>
@@ -26,44 +26,44 @@
namespace clang::ssaf {
/// Consumes a LUSummary by dispatching its entity data to all registered
-/// SummaryViewBuilders and collecting the resulting views.
+/// SummaryDataBuilders and collecting the resulting data.
class LUSummaryConsumer final {
public:
explicit LUSummaryConsumer(std::unique_ptr<LUSummary> LU)
: LU(std::move(LU)) {}
/// Instantiates a builder for each SummaryName present in the LUSummary,
- /// delivers its entities, finalizes it, and stores the resulting view. Each
+ /// delivers its entities, finalizes it, and stores the resulting data. Each
/// builder is fully processed before the next SummaryName is visited.
/// Builders are discarded on return.
///
/// \pre Must be called exactly once.
void run();
- /// Transfers ownership of the view for \p ViewT to the caller.
+ /// Transfers ownership of the data for \p DataT to the caller.
///
- /// Returns nullptr if no builder for \p ViewT was registered or run() has
- /// not been called. A second call for the same ViewT also returns nullptr.
- template <typename ViewT> [[nodiscard]] std::unique_ptr<ViewT> getView() {
- static_assert(std::is_base_of_v<SummaryView, ViewT>,
- "ViewT must derive from SummaryView");
- static_assert(HasSummaryName<ViewT>::value,
- "ViewT must have a static summaryName() method");
+ /// Returns nullptr if no builder for \p DataT was registered or run() has
+ /// not been called. A second call for the same DataT also returns nullptr.
+ template <typename DataT> [[nodiscard]] std::unique_ptr<DataT> getData() {
+ static_assert(std::is_base_of_v<SummaryData, DataT>,
+ "DataT must derive from SummaryData");
+ static_assert(HasSummaryName<DataT>::value,
+ "DataT must have a static summaryName() method");
- auto It = Views.find(ViewT::summaryName());
- if (It == Views.end()) {
+ auto It = Data.find(DataT::summaryName());
+ if (It == Data.end()) {
return nullptr;
}
- auto *RawPtr = static_cast<ViewT *>(It->second.release());
- Views.erase(It);
- return std::unique_ptr<ViewT>(RawPtr);
+ auto *RawPtr = static_cast<DataT *>(It->second.release());
+ Data.erase(It);
+ return std::unique_ptr<DataT>(RawPtr);
}
private:
using EntityDataMap = std::map<EntityId, std::unique_ptr<EntitySummary>>;
std::unique_ptr<LUSummary> LU;
- std::map<SummaryName, std::unique_ptr<SummaryView>> Views;
+ std::map<SummaryName, std::unique_ptr<SummaryData>> Data;
bool WasRun = false;
void run(const SummaryName &SN, EntityDataMap &Data);
@@ -71,4 +71,4 @@ class LUSummaryConsumer final {
} // namespace clang::ssaf
-#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_LUSUMMARYCONSUMER_H
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_LUSUMMARYCONSUMER_H
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h b/clang/include/clang/Analysis/Scalable/SummaryData/SummaryData.h
similarity index 56%
rename from clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h
rename to clang/include/clang/Analysis/Scalable/SummaryData/SummaryData.h
index dfbdab4814ac7..6f76a5b47cf7c 100644
--- a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h
+++ b/clang/include/clang/Analysis/Scalable/SummaryData/SummaryData.h
@@ -1,4 +1,4 @@
-//===- SummaryView.h ------------------------------------------------------===//
+//===- SummaryData.h ------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -6,23 +6,23 @@
//
//===----------------------------------------------------------------------===//
//
-// Abstract base class for all whole-program analysis views built from
+// Abstract base class for all whole-program analysis data built from
// LUSummary data. Carries no query API — all analysis-specific methods live
// on concrete subclasses.
//
//===----------------------------------------------------------------------===//
-#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEW_H
-#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEW_H
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATA_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATA_H
namespace clang::ssaf {
-/// Abstract base class for whole-program analysis views.
-class SummaryView {
+/// Abstract base class for whole-program analysis data.
+class SummaryData {
public:
- virtual ~SummaryView() = default;
+ virtual ~SummaryData() = default;
};
} // namespace clang::ssaf
-#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEW_H
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATA_H
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h b/clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataBuilder.h
similarity index 58%
rename from clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h
rename to clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataBuilder.h
index 14231eec17753..fd68a9f9ea0d2 100644
--- a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h
+++ b/clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataBuilder.h
@@ -1,4 +1,4 @@
-//===- SummaryViewBuilder.h -----------------------------------------------===//
+//===- SummaryDataBuilder.h -----------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -6,19 +6,19 @@
//
//===----------------------------------------------------------------------===//
//
-// This file defines SummaryViewBuilderBase (abstract base known to the
+// This file defines SummaryDataBuilderBase (abstract base known to the
// registry and LUSummaryConsumer) and the typed intermediate template
-// SummaryViewBuilder<ViewT, SummaryT> that concrete builders inherit from.
+// SummaryDataBuilder<DataT, SummaryT> that concrete builders inherit from.
//
//===----------------------------------------------------------------------===//
-#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDER_H
-#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDER_H
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATABUILDER_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATABUILDER_H
#include "clang/Analysis/Scalable/Model/EntityId.h"
#include "clang/Analysis/Scalable/Model/SummaryName.h"
-#include "clang/Analysis/Scalable/SummaryView/SummaryView.h"
-#include "clang/Analysis/Scalable/SummaryView/SummaryViewTraits.h"
+#include "clang/Analysis/Scalable/SummaryData/SummaryData.h"
+#include "clang/Analysis/Scalable/SummaryData/SummaryDataTraits.h"
#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
#include <memory>
@@ -26,16 +26,16 @@ namespace clang::ssaf {
class LUSummaryConsumer;
-/// Abstract base class for all summary view builders.
+/// Abstract base class for all summary data builders.
///
/// Known to the registry and LUSummaryConsumer. Receives entities one at a
/// time via \c addSummary(), is finalized via \c finalize(), and transfers
-/// ownership of the built view via \c getView().
-class SummaryViewBuilderBase {
+/// ownership of the built data via \c getData().
+class SummaryDataBuilderBase {
friend class LUSummaryConsumer;
public:
- virtual ~SummaryViewBuilderBase() = default;
+ virtual ~SummaryDataBuilderBase() = default;
private:
/// Called once per entity belonging to this builder's analysis.
@@ -46,40 +46,40 @@ class SummaryViewBuilderBase {
/// Called after all entities have been added.
virtual void finalize() {}
- /// Transfers ownership of the built view. Called by LUSummaryConsumer after
+ /// Transfers ownership of the built data. Called by LUSummaryConsumer after
/// finalize(). The rvalue ref-qualifier enforces single use — the builder
/// cannot be accessed after this call.
- virtual std::unique_ptr<SummaryView> getView() && = 0;
+ virtual std::unique_ptr<SummaryData> getData() && = 0;
};
/// Typed intermediate template that concrete builders inherit from.
/// Concrete builders must implement the typed
/// \c addSummary(EntityId, unique_ptr<SummaryT>) overload, and may override
/// \c finalize() for any post-processing needed after all entities are added.
-template <typename ViewT, typename SummaryT>
-class SummaryViewBuilder : public SummaryViewBuilderBase {
- static_assert(std::is_base_of_v<SummaryView, ViewT>,
- "ViewT must derive from SummaryView");
- static_assert(HasSummaryName<ViewT>::value,
- "ViewT must have a static summaryName() method");
+template <typename DataT, typename SummaryT>
+class SummaryDataBuilder : public SummaryDataBuilderBase {
+ static_assert(std::is_base_of_v<SummaryData, DataT>,
+ "DataT must derive from SummaryData");
+ static_assert(HasSummaryName<DataT>::value,
+ "DataT must have a static summaryName() method");
- std::unique_ptr<ViewT> View;
+ std::unique_ptr<DataT> Data;
public:
- SummaryViewBuilder() : View(std::make_unique<ViewT>()) {}
+ SummaryDataBuilder() : Data(std::make_unique<DataT>()) {}
- /// Returns the SummaryName of the view this builder produces.
- /// Used by SummaryViewBuilderRegistry::Add to derive the registry entry name.
- static SummaryName summaryName() { return ViewT::summaryName(); }
+ /// Returns the SummaryName of the data this builder produces.
+ /// Used by SummaryDataBuilderRegistry::Add to derive the registry entry name.
+ static SummaryName summaryName() { return DataT::summaryName(); }
/// Typed customization point — concrete builders override this.
virtual void addSummary(EntityId Id, std::unique_ptr<SummaryT> Summary) = 0;
protected:
- ViewT &getView() & { return *View; }
+ DataT &getData() & { return *Data; }
private:
- std::unique_ptr<SummaryView> getView() && override { return std::move(View); }
+ std::unique_ptr<SummaryData> getData() && override { return std::move(Data); }
/// Seals the base overload, downcasts, and dispatches to the typed overload.
void addSummary(EntityId Id, std::unique_ptr<EntitySummary> Summary) final {
@@ -90,4 +90,4 @@ class SummaryViewBuilder : public SummaryViewBuilderBase {
} // namespace clang::ssaf
-#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDER_H
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATABUILDER_H
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h b/clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataBuilderRegistry.h
similarity index 67%
rename from clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h
rename to clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataBuilderRegistry.h
index 2c84ee1573874..76e136f8bae40 100644
--- a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h
+++ b/clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataBuilderRegistry.h
@@ -1,4 +1,4 @@
-//===- SummaryViewBuilderRegistry.h ---------------------------------------===//
+//===- SummaryDataBuilderRegistry.h ---------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -6,36 +6,36 @@
//
//===----------------------------------------------------------------------===//
//
-// Registry for SummaryViewBuilders.
+// Registry for SummaryDataBuilders.
//
// To register a builder, add a static Add<BuilderT> in the builder's
// translation unit:
//
-// static SummaryViewBuilderRegistry::Add<MyViewBuilder>
-// Registered("View builder for MyAnalysis");
+// static SummaryDataBuilderRegistry::Add<MyDataBuilder>
+// Registered("Data builder for MyAnalysis");
//
// The registry entry name is derived automatically from
-// MyViewBuilder::summaryName(), which returns MyView::summaryName().
+// MyDataBuilder::summaryName(), which returns MyData::summaryName().
//
//===----------------------------------------------------------------------===//
-#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDERREGISTRY_H
-#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDERREGISTRY_H
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATABUILDERREGISTRY_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATABUILDERREGISTRY_H
-#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h"
+#include "clang/Analysis/Scalable/SummaryData/SummaryDataBuilder.h"
#include "llvm/Support/Registry.h"
#include <memory>
#include <string>
namespace clang::ssaf {
-/// Registry for SummaryViewBuilder implementations.
+/// Registry for SummaryDataBuilder implementations.
///
/// Provides an Add helper that derives the registry entry name from
/// BuilderT::summaryName(), eliminating the possibility of registering a
/// builder under the wrong name.
-class SummaryViewBuilderRegistry {
- using RegistryT = llvm::Registry<SummaryViewBuilderBase>;
+class SummaryDataBuilderRegistry {
+ using RegistryT = llvm::Registry<SummaryDataBuilderBase>;
public:
/// Registers \p BuilderT under the name returned by
@@ -54,10 +54,10 @@ class SummaryViewBuilderRegistry {
/// Instantiates the builder registered under \p Name, or returns nullptr
/// if no such builder is registered.
- static std::unique_ptr<SummaryViewBuilderBase>
+ static std::unique_ptr<SummaryDataBuilderBase>
instantiate(llvm::StringRef Name);
};
} // namespace clang::ssaf
-#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDERREGISTRY_H
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATABUILDERREGISTRY_H
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewTraits.h b/clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataTraits.h
similarity index 75%
rename from clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewTraits.h
rename to clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataTraits.h
index 542714b8b21a7..3d791a9bc8706 100644
--- a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewTraits.h
+++ b/clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataTraits.h
@@ -1,4 +1,4 @@
-//===- SummaryViewTraits.h ------------------------------------------------===//
+//===- SummaryDataTraits.h ------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -6,12 +6,12 @@
//
//===----------------------------------------------------------------------===//
//
-// Type traits for SummaryView subclasses.
+// Type traits for SummaryData subclasses.
//
//===----------------------------------------------------------------------===//
-#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWTRAITS_H
-#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWTRAITS_H
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATATRAITS_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATATRAITS_H
#include "clang/Analysis/Scalable/Model/SummaryName.h"
#include <type_traits>
@@ -19,7 +19,7 @@
namespace clang::ssaf {
/// Type trait that checks whether \p T has a static summaryName() method
-/// returning SummaryName. Used to enforce the convention on SummaryView
+/// returning SummaryName. Used to enforce the convention on SummaryData
/// subclasses at instantiation time.
template <typename T, typename = void>
struct HasSummaryName : std::false_type {};
@@ -30,4 +30,4 @@ struct HasSummaryName<T, std::void_t<decltype(T::summaryName())>>
} // namespace clang::ssaf
-#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWTRAITS_H
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATATRAITS_H
diff --git a/clang/lib/Analysis/Scalable/CMakeLists.txt b/clang/lib/Analysis/Scalable/CMakeLists.txt
index 3da854558f760..63e066be6a8d2 100644
--- a/clang/lib/Analysis/Scalable/CMakeLists.txt
+++ b/clang/lib/Analysis/Scalable/CMakeLists.txt
@@ -18,8 +18,8 @@ add_clang_library(clangAnalysisScalable
Serialization/JSONFormat/TUSummary.cpp
Serialization/JSONFormat/TUSummaryEncoding.cpp
Serialization/SerializationFormatRegistry.cpp
- SummaryView/LUSummaryConsumer.cpp
- SummaryView/SummaryViewBuilderRegistry.cpp
+ SummaryData/LUSummaryConsumer.cpp
+ SummaryData/SummaryDataBuilderRegistry.cpp
Support/ErrorBuilder.cpp
TUSummary/ExtractorRegistry.cpp
TUSummary/TUSummaryBuilder.cpp
diff --git a/clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp b/clang/lib/Analysis/Scalable/SummaryData/LUSummaryConsumer.cpp
similarity index 66%
rename from clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp
rename to clang/lib/Analysis/Scalable/SummaryData/LUSummaryConsumer.cpp
index 1be43eee5e923..1efbcaea24dca 100644
--- a/clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp
+++ b/clang/lib/Analysis/Scalable/SummaryData/LUSummaryConsumer.cpp
@@ -6,32 +6,32 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h"
-#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
+#include "clang/Analysis/Scalable/SummaryData/LUSummaryConsumer.h"
+#include "clang/Analysis/Scalable/SummaryData/SummaryDataBuilderRegistry.h"
#include <cassert>
using namespace clang;
using namespace ssaf;
-void LUSummaryConsumer::run(const SummaryName &SN, EntityDataMap &Data) {
- auto Builder = SummaryViewBuilderRegistry::instantiate(SN.str());
+void LUSummaryConsumer::run(const SummaryName &SN, EntityDataMap &EntityData) {
+ auto Builder = SummaryDataBuilderRegistry::instantiate(SN.str());
if (!Builder) {
return;
}
- for (auto &[Id, Summary] : Data) {
+ for (auto &[Id, Summary] : EntityData) {
Builder->addSummary(Id, std::move(Summary));
}
Builder->finalize();
- Views.emplace(SN, std::move(*Builder).getView());
+ Data.emplace(SN, std::move(*Builder).getData());
}
void LUSummaryConsumer::run() {
assert(!WasRun && "run() must be called exactly once");
WasRun = true;
- for (auto &[SN, Data] : LU->Data) {
- run(SN, Data);
+ for (auto &[SN, EntityData] : LU->Data) {
+ run(SN, EntityData);
}
}
diff --git a/clang/lib/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.cpp b/clang/lib/Analysis/Scalable/SummaryData/SummaryDataBuilderRegistry.cpp
similarity index 66%
rename from clang/lib/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.cpp
rename to clang/lib/Analysis/Scalable/SummaryData/SummaryDataBuilderRegistry.cpp
index 7d7ef13a70c01..de8e7ecbb2d84 100644
--- a/clang/lib/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.cpp
+++ b/clang/lib/Analysis/Scalable/SummaryData/SummaryDataBuilderRegistry.cpp
@@ -1,4 +1,4 @@
-//===- SummaryViewBuilderRegistry.cpp -------------------------------------===//
+//===- SummaryDataBuilderRegistry.cpp -------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -6,16 +6,16 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
+#include "clang/Analysis/Scalable/SummaryData/SummaryDataBuilderRegistry.h"
using namespace clang;
using namespace ssaf;
-using RegistryT = llvm::Registry<SummaryViewBuilderBase>;
+using RegistryT = llvm::Registry<SummaryDataBuilderBase>;
LLVM_INSTANTIATE_REGISTRY(RegistryT)
-std::unique_ptr<SummaryViewBuilderBase>
-SummaryViewBuilderRegistry::instantiate(llvm::StringRef Name) {
+std::unique_ptr<SummaryDataBuilderBase>
+SummaryDataBuilderRegistry::instantiate(llvm::StringRef Name) {
for (const auto &Entry : RegistryT::entries()) {
if (Entry.getName() == Name) {
return Entry.instantiate();
@@ -24,7 +24,7 @@ SummaryViewBuilderRegistry::instantiate(llvm::StringRef Name) {
return nullptr;
}
-bool SummaryViewBuilderRegistry::isRegistered(llvm::StringRef Name) {
+bool SummaryDataBuilderRegistry::isRegistered(llvm::StringRef Name) {
for (const auto &Entry : RegistryT::entries()) {
if (Entry.getName() == Name) {
return true;
diff --git a/clang/unittests/Analysis/Scalable/CMakeLists.txt b/clang/unittests/Analysis/Scalable/CMakeLists.txt
index 7af03a9aec1c2..209e46898326d 100644
--- a/clang/unittests/Analysis/Scalable/CMakeLists.txt
+++ b/clang/unittests/Analysis/Scalable/CMakeLists.txt
@@ -19,7 +19,7 @@ add_distinct_clang_unittest(ClangScalableAnalysisTests
Serialization/JSONFormatTest/LUSummaryTest.cpp
Serialization/JSONFormatTest/TUSummaryTest.cpp
SummaryNameTest.cpp
- SummaryView/SummaryViewTest.cpp
+ SummaryData/SummaryDataTest.cpp
TestFixture.cpp
TUSummaryBuilderTest.cpp
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewTest.cpp b/clang/unittests/Analysis/Scalable/SummaryData/SummaryDataTest.cpp
similarity index 73%
rename from clang/unittests/Analysis/Scalable/SummaryView/SummaryViewTest.cpp
rename to clang/unittests/Analysis/Scalable/SummaryData/SummaryDataTest.cpp
index 3b89e2f6d6966..6400f11a7d2be 100644
--- a/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewTest.cpp
+++ b/clang/unittests/Analysis/Scalable/SummaryData/SummaryDataTest.cpp
@@ -1,4 +1,4 @@
-//===- LUSummaryConsumerTest.cpp ------------------------------------------===//
+//===- SummaryDataTest.cpp ------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -14,8 +14,8 @@
#include "clang/Analysis/Scalable/Model/EntityLinkage.h"
#include "clang/Analysis/Scalable/Model/EntityName.h"
#include "clang/Analysis/Scalable/Model/SummaryName.h"
-#include "clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h"
-#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
+#include "clang/Analysis/Scalable/SummaryData/LUSummaryConsumer.h"
+#include "clang/Analysis/Scalable/SummaryData/SummaryDataBuilderRegistry.h"
#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
#include "gtest/gtest.h"
#include <algorithm>
@@ -63,17 +63,17 @@ class Analysis4EntitySummary : public EntitySummary {
};
// ---------------------------------------------------------------------------
-// Views
+// Data
// ---------------------------------------------------------------------------
-class Analysis1View : public SummaryView {
+class Analysis1Data : public SummaryData {
public:
static SummaryName summaryName() { return SummaryName("Analysis1"); }
std::vector<std::pair<EntityId, int>> Entries;
bool WasFinalized = false;
};
-class Analysis2View : public SummaryView {
+class Analysis2Data : public SummaryData {
public:
static SummaryName summaryName() { return SummaryName("Analysis2"); }
std::vector<std::pair<EntityId, int>> Entries;
@@ -82,14 +82,14 @@ class Analysis2View : public SummaryView {
// No builder or registration for Analysis3. Data for Analysis3 is inserted
// into the LUSummary to verify the consumer silently skips it.
-class Analysis3View : public SummaryView {
+class Analysis3Data : public SummaryData {
public:
static SummaryName summaryName() { return SummaryName("Analysis3"); }
};
// Analysis4 has a registered builder but no data is inserted into the
-// LUSummary, so the builder is never invoked and getView returns nullptr.
-class Analysis4View : public SummaryView {
+// LUSummary, so the builder is never invoked and getData returns nullptr.
+class Analysis4Data : public SummaryData {
public:
static SummaryName summaryName() { return SummaryName("Analysis4"); }
std::vector<std::pair<EntityId, int>> Entries;
@@ -109,51 +109,51 @@ static bool Analysis4BuilderWasDestroyed = false;
// ---------------------------------------------------------------------------
class Analysis1Builder
- : public SummaryViewBuilder<Analysis1View, Analysis1EntitySummary> {
+ : public SummaryDataBuilder<Analysis1Data, Analysis1EntitySummary> {
public:
~Analysis1Builder() { Analysis1BuilderWasDestroyed = true; }
void addSummary(EntityId Id,
std::unique_ptr<Analysis1EntitySummary> S) override {
- getView().Entries.push_back({Id, S->InstanceId});
+ getData().Entries.push_back({Id, S->InstanceId});
}
- void finalize() override { getView().WasFinalized = true; }
+ void finalize() override { getData().WasFinalized = true; }
};
-static SummaryViewBuilderRegistry::Add<Analysis1Builder>
+static SummaryDataBuilderRegistry::Add<Analysis1Builder>
RegAnalysis1("Builder for Analysis1");
class Analysis2Builder
- : public SummaryViewBuilder<Analysis2View, Analysis2EntitySummary> {
+ : public SummaryDataBuilder<Analysis2Data, Analysis2EntitySummary> {
public:
~Analysis2Builder() { Analysis2BuilderWasDestroyed = true; }
void addSummary(EntityId Id,
std::unique_ptr<Analysis2EntitySummary> S) override {
- getView().Entries.push_back({Id, S->InstanceId});
+ getData().Entries.push_back({Id, S->InstanceId});
}
- void finalize() override { getView().WasFinalized = true; }
+ void finalize() override { getData().WasFinalized = true; }
};
-static SummaryViewBuilderRegistry::Add<Analysis2Builder>
+static SummaryDataBuilderRegistry::Add<Analysis2Builder>
RegAnalysis2("Builder for Analysis2");
class Analysis4Builder
- : public SummaryViewBuilder<Analysis4View, Analysis4EntitySummary> {
+ : public SummaryDataBuilder<Analysis4Data, Analysis4EntitySummary> {
public:
~Analysis4Builder() { Analysis4BuilderWasDestroyed = true; }
void addSummary(EntityId Id,
std::unique_ptr<Analysis4EntitySummary> S) override {
- getView().Entries.push_back({Id, S->InstanceId});
+ getData().Entries.push_back({Id, S->InstanceId});
}
- void finalize() override { getView().WasFinalized = true; }
+ void finalize() override { getData().WasFinalized = true; }
};
-static SummaryViewBuilderRegistry::Add<Analysis4Builder>
+static SummaryDataBuilderRegistry::Add<Analysis4Builder>
RegAnalysis4("Builder for Analysis4");
// ---------------------------------------------------------------------------
@@ -208,18 +208,18 @@ class LUSummaryConsumerTest : public TestFixture {
// Tests
// ---------------------------------------------------------------------------
-TEST(SummaryViewBuilderRegistryTest, BuilderIsRegistered) {
- EXPECT_FALSE(SummaryViewBuilderRegistry::isRegistered("Analysis10"));
- EXPECT_TRUE(SummaryViewBuilderRegistry::isRegistered("Analysis1"));
- EXPECT_TRUE(SummaryViewBuilderRegistry::isRegistered("Analysis2"));
- EXPECT_TRUE(SummaryViewBuilderRegistry::isRegistered("Analysis4"));
+TEST(SummaryDataBuilderRegistryTest, BuilderIsRegistered) {
+ EXPECT_FALSE(SummaryDataBuilderRegistry::isRegistered("Analysis10"));
+ EXPECT_TRUE(SummaryDataBuilderRegistry::isRegistered("Analysis1"));
+ EXPECT_TRUE(SummaryDataBuilderRegistry::isRegistered("Analysis2"));
+ EXPECT_TRUE(SummaryDataBuilderRegistry::isRegistered("Analysis4"));
}
-TEST(SummaryViewBuilderRegistryTest, BuilderCanBeInstantiated) {
- EXPECT_EQ(SummaryViewBuilderRegistry::instantiate("Analysis10"), nullptr);
- EXPECT_NE(SummaryViewBuilderRegistry::instantiate("Analysis1"), nullptr);
- EXPECT_NE(SummaryViewBuilderRegistry::instantiate("Analysis2"), nullptr);
- EXPECT_NE(SummaryViewBuilderRegistry::instantiate("Analysis4"), nullptr);
+TEST(SummaryDataBuilderRegistryTest, BuilderCanBeInstantiated) {
+ EXPECT_EQ(SummaryDataBuilderRegistry::instantiate("Analysis10"), nullptr);
+ EXPECT_NE(SummaryDataBuilderRegistry::instantiate("Analysis1"), nullptr);
+ EXPECT_NE(SummaryDataBuilderRegistry::instantiate("Analysis2"), nullptr);
+ EXPECT_NE(SummaryDataBuilderRegistry::instantiate("Analysis4"), nullptr);
}
TEST_F(LUSummaryConsumerTest, Run) {
@@ -242,17 +242,17 @@ TEST_F(LUSummaryConsumerTest, Run) {
// Analysis1
{
- auto View1 = Consumer.getView<Analysis1View>();
- ASSERT_NE(View1, nullptr);
+ auto Data1 = Consumer.getData<Analysis1Data>();
+ ASSERT_NE(Data1, nullptr);
- // getView ownership transfer — second call returns nullptr
- EXPECT_EQ(Consumer.getView<Analysis1View>(), nullptr);
+ // getData ownership transfer — second call returns nullptr
+ EXPECT_EQ(Consumer.getData<Analysis1Data>(), nullptr);
- EXPECT_EQ(View1->Entries.size(), 2u);
- EXPECT_TRUE(hasEntry(View1->Entries, E1, s1a));
- EXPECT_TRUE(hasEntry(View1->Entries, E2, s1b));
+ EXPECT_EQ(Data1->Entries.size(), 2u);
+ EXPECT_TRUE(hasEntry(Data1->Entries, E1, s1a));
+ EXPECT_TRUE(hasEntry(Data1->Entries, E2, s1b));
- EXPECT_TRUE(View1->WasFinalized);
+ EXPECT_TRUE(Data1->WasFinalized);
// Builder lifetime
EXPECT_TRUE(Analysis1BuilderWasDestroyed);
@@ -260,32 +260,32 @@ TEST_F(LUSummaryConsumerTest, Run) {
// Analysis2
{
- auto View2 = Consumer.getView<Analysis2View>();
- ASSERT_NE(View2, nullptr);
+ auto Data2 = Consumer.getData<Analysis2Data>();
+ ASSERT_NE(Data2, nullptr);
- // getView ownership transfer — second call returns nullptr
- EXPECT_EQ(Consumer.getView<Analysis2View>(), nullptr);
+ // getData ownership transfer — second call returns nullptr
+ EXPECT_EQ(Consumer.getData<Analysis2Data>(), nullptr);
- EXPECT_EQ(View2->Entries.size(), 2u);
- EXPECT_TRUE(hasEntry(View2->Entries, E2, s2a));
- EXPECT_TRUE(hasEntry(View2->Entries, E3, s2b));
+ EXPECT_EQ(Data2->Entries.size(), 2u);
+ EXPECT_TRUE(hasEntry(Data2->Entries, E2, s2a));
+ EXPECT_TRUE(hasEntry(Data2->Entries, E3, s2b));
- EXPECT_TRUE(View2->WasFinalized);
+ EXPECT_TRUE(Data2->WasFinalized);
// Builder lifetime
EXPECT_TRUE(Analysis2BuilderWasDestroyed);
}
- // Analysis 3
+ // Analysis3
{
// Unregistered builder — Analysis3 data silently skipped
- EXPECT_EQ(Consumer.getView<Analysis3View>(), nullptr);
+ EXPECT_EQ(Consumer.getData<Analysis3Data>(), nullptr);
}
// Analysis4
{
// Registered builder but no data in LUSummary — builder never invoked
- EXPECT_EQ(Consumer.getView<Analysis4View>(), nullptr);
+ EXPECT_EQ(Consumer.getData<Analysis4Data>(), nullptr);
// Builder lifetime
EXPECT_FALSE(Analysis4BuilderWasDestroyed);
>From 69e640fc4e1e682ce50b2c11aa2e11e909956405 Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Wed, 11 Mar 2026 21:56:56 -0700
Subject: [PATCH 8/8] Add store
---
.../Scalable/SummaryData/LUSummaryConsumer.h | 72 +++----
.../Scalable/SummaryData/SummaryDataStore.h | 114 ++++++++++++
.../SummaryData/LUSummaryConsumer.cpp | 58 ++++--
.../Scalable/SummaryData/SummaryDataTest.cpp | 175 ++++++++++++++----
4 files changed, 335 insertions(+), 84 deletions(-)
create mode 100644 clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataStore.h
diff --git a/clang/include/clang/Analysis/Scalable/SummaryData/LUSummaryConsumer.h b/clang/include/clang/Analysis/Scalable/SummaryData/LUSummaryConsumer.h
index 8e93ff025f2f6..bed70c9a989d6 100644
--- a/clang/include/clang/Analysis/Scalable/SummaryData/LUSummaryConsumer.h
+++ b/clang/include/clang/Analysis/Scalable/SummaryData/LUSummaryConsumer.h
@@ -15,58 +15,64 @@
#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_LUSUMMARYCONSUMER_H
#include "clang/Analysis/Scalable/EntityLinker/LUSummary.h"
-#include "clang/Analysis/Scalable/Model/EntityId.h"
#include "clang/Analysis/Scalable/Model/SummaryName.h"
-#include "clang/Analysis/Scalable/SummaryData/SummaryData.h"
-#include "clang/Analysis/Scalable/SummaryData/SummaryDataTraits.h"
-#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
-#include <map>
+#include "clang/Analysis/Scalable/SummaryData/SummaryDataStore.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/Support/Error.h"
#include <memory>
namespace clang::ssaf {
-/// Consumes a LUSummary by dispatching its entity data to all registered
-/// SummaryDataBuilders and collecting the resulting data.
+/// Consumes a LUSummary by dispatching its entity data to registered
+/// SummaryDataBuilders and returning the results in a SummaryDataStore.
+///
+/// Three consumption patterns are supported:
+/// - run() — processes all registered analyses in the LUSummary,
+/// silently skipping any with missing data or builders.
+/// - run(names) — processes a named subset; returns an error if any
+/// name has no data in the LUSummary or no registered
+/// builder.
+/// - run<DataTs...>() — type-safe variant of run(names) with the same error
+/// semantics.
+///
+/// All patterns consume the underlying LUSummary data, so each analysis can
+/// only be retrieved once across all patterns.
class LUSummaryConsumer final {
public:
explicit LUSummaryConsumer(std::unique_ptr<LUSummary> LU)
: LU(std::move(LU)) {}
- /// Instantiates a builder for each SummaryName present in the LUSummary,
- /// delivers its entities, finalizes it, and stores the resulting data. Each
- /// builder is fully processed before the next SummaryName is visited.
- /// Builders are discarded on return.
+ /// Processes all registered analyses in LUSummary and returns the results.
+ /// Silently skips analyses with no data or no registered builder.
///
- /// \pre Must be called exactly once.
- void run();
+ /// Requires an rvalue consumer (call as \c std::move(Consumer).run()) because
+ /// this pattern exhausts all remaining LUSummary data.
+ [[nodiscard]] SummaryDataStore run() &&;
- /// Transfers ownership of the data for \p DataT to the caller.
+ /// Processes the named analyses and returns the results.
///
- /// Returns nullptr if no builder for \p DataT was registered or run() has
- /// not been called. A second call for the same DataT also returns nullptr.
- template <typename DataT> [[nodiscard]] std::unique_ptr<DataT> getData() {
- static_assert(std::is_base_of_v<SummaryData, DataT>,
- "DataT must derive from SummaryData");
- static_assert(HasSummaryName<DataT>::value,
- "DataT must have a static summaryName() method");
+ /// Returns an error if any name has no data in the LUSummary or no
+ /// registered builder.
+ [[nodiscard]] llvm::Expected<SummaryDataStore>
+ run(llvm::ArrayRef<SummaryName> Names);
- auto It = Data.find(DataT::summaryName());
- if (It == Data.end()) {
- return nullptr;
- }
- auto *RawPtr = static_cast<DataT *>(It->second.release());
- Data.erase(It);
- return std::unique_ptr<DataT>(RawPtr);
+ /// Processes analyses for each of the given types and returns the results.
+ ///
+ /// Returns an error if any type has no data in the LUSummary or no
+ /// registered builder.
+ template <typename... DataTs>
+ [[nodiscard]] llvm::Expected<SummaryDataStore> run() {
+ return run({DataTs::summaryName()...});
}
private:
- using EntityDataMap = std::map<EntityId, std::unique_ptr<EntitySummary>>;
-
std::unique_ptr<LUSummary> LU;
- std::map<SummaryName, std::unique_ptr<SummaryData>> Data;
- bool WasRun = false;
- void run(const SummaryName &SN, EntityDataMap &Data);
+ /// Looks up \p SN in the LUSummary, instantiates the registered builder,
+ /// delivers all entities, finalizes, and returns the built data.
+ /// Returns an error if no data for \p SN exists or no builder is registered.
+ /// Consumes the LUSummary entry for \p SN on success.
+ llvm::Expected<std::unique_ptr<SummaryData>> build(const SummaryName &SN);
};
} // namespace clang::ssaf
diff --git a/clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataStore.h b/clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataStore.h
new file mode 100644
index 0000000000000..1ac26e5789fa2
--- /dev/null
+++ b/clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataStore.h
@@ -0,0 +1,114 @@
+//===- SummaryDataStore.h -------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Owns a collection of SummaryData objects keyed by SummaryName.
+// Produced by LUSummaryConsumer::run() variants.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATASTORE_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATASTORE_H
+
+#include "clang/Analysis/Scalable/Model/SummaryName.h"
+#include "clang/Analysis/Scalable/SummaryData/SummaryData.h"
+#include "clang/Analysis/Scalable/SummaryData/SummaryDataTraits.h"
+#include "clang/Analysis/Scalable/Support/ErrorBuilder.h"
+#include <map>
+#include <memory>
+
+namespace clang::ssaf {
+
+class LUSummaryConsumer;
+
+/// Owns a collection of SummaryData objects keyed by SummaryName.
+/// Produced by LUSummaryConsumer::run() variants.
+class SummaryDataStore {
+ friend class LUSummaryConsumer;
+
+ std::map<SummaryName, std::unique_ptr<SummaryData>> Data;
+
+public:
+ /// Returns true if data for \p Name is stored.
+ [[nodiscard]] bool contains(const SummaryName &Name) const {
+ return Data.find(Name) != Data.end();
+ }
+
+ /// Returns true if data for \p DataT is stored.
+ template <typename DataT> [[nodiscard]] bool contains() const {
+ static_assert(std::is_base_of_v<SummaryData, DataT>,
+ "DataT must derive from SummaryData");
+ static_assert(HasSummaryName<DataT>::value,
+ "DataT must have a static summaryName() method");
+
+ return contains(DataT::summaryName());
+ }
+
+ /// Returns a reference to the data for \p DataT, or an error if
+ /// no data for \p DataT is stored.
+ template <typename DataT> [[nodiscard]] llvm::Expected<DataT &> get() {
+ static_assert(std::is_base_of_v<SummaryData, DataT>,
+ "DataT must derive from SummaryData");
+ static_assert(HasSummaryName<DataT>::value,
+ "DataT must have a static summaryName() method");
+
+ auto Result = get(DataT::summaryName());
+ if (!Result) {
+ return Result.takeError();
+ }
+ return static_cast<DataT &>(*Result);
+ }
+
+ /// Returns a reference to the data for \p Name, or an error if
+ /// no data for \p Name is stored.
+ [[nodiscard]] llvm::Expected<SummaryData &> get(const SummaryName &Name) {
+ auto It = Data.find(Name);
+ if (It == Data.end()) {
+ return ErrorBuilder::create(std::errc::invalid_argument,
+ "no data for analysis '{0}' in store",
+ Name.str())
+ .build();
+ }
+ return *It->second;
+ }
+
+ /// Transfers ownership of the data for \p DataT to the caller, or returns
+ /// an error if no data for \p DataT is stored.
+ template <typename DataT>
+ [[nodiscard]] llvm::Expected<std::unique_ptr<DataT>> take() {
+ static_assert(std::is_base_of_v<SummaryData, DataT>,
+ "DataT must derive from SummaryData");
+ static_assert(HasSummaryName<DataT>::value,
+ "DataT must have a static summaryName() method");
+
+ auto Result = take(DataT::summaryName());
+ if (!Result) {
+ return Result.takeError();
+ }
+ return std::unique_ptr<DataT>(static_cast<DataT *>(Result->release()));
+ }
+
+ /// Transfers ownership of the data for \p Name to the caller, or returns
+ /// an error if no data for \p Name is stored.
+ [[nodiscard]] llvm::Expected<std::unique_ptr<SummaryData>>
+ take(const SummaryName &Name) {
+ auto It = Data.find(Name);
+ if (It == Data.end()) {
+ return ErrorBuilder::create(std::errc::invalid_argument,
+ "no data for analysis '{0}' in store",
+ Name.str())
+ .build();
+ }
+ auto Ptr = std::move(It->second);
+ Data.erase(It);
+ return Ptr;
+ }
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATASTORE_H
diff --git a/clang/lib/Analysis/Scalable/SummaryData/LUSummaryConsumer.cpp b/clang/lib/Analysis/Scalable/SummaryData/LUSummaryConsumer.cpp
index 1efbcaea24dca..12c8af08aec36 100644
--- a/clang/lib/Analysis/Scalable/SummaryData/LUSummaryConsumer.cpp
+++ b/clang/lib/Analysis/Scalable/SummaryData/LUSummaryConsumer.cpp
@@ -8,30 +8,66 @@
#include "clang/Analysis/Scalable/SummaryData/LUSummaryConsumer.h"
#include "clang/Analysis/Scalable/SummaryData/SummaryDataBuilderRegistry.h"
-#include <cassert>
+#include "clang/Analysis/Scalable/Support/ErrorBuilder.h"
+#include <vector>
using namespace clang;
using namespace ssaf;
-void LUSummaryConsumer::run(const SummaryName &SN, EntityDataMap &EntityData) {
+llvm::Expected<std::unique_ptr<SummaryData>>
+LUSummaryConsumer::build(const SummaryName &SN) {
+ auto LUIt = LU->Data.find(SN);
+ if (LUIt == LU->Data.end()) {
+ return ErrorBuilder::create(std::errc::invalid_argument,
+ "no data for analysis '{0}' in LUSummary",
+ SN.str())
+ .build();
+ }
+
auto Builder = SummaryDataBuilderRegistry::instantiate(SN.str());
if (!Builder) {
- return;
+ return ErrorBuilder::create(std::errc::invalid_argument,
+ "no builder registered for analysis '{0}'",
+ SN.str())
+ .build();
}
- for (auto &[Id, Summary] : EntityData) {
+ for (auto &[Id, Summary] : LUIt->second) {
Builder->addSummary(Id, std::move(Summary));
}
-
Builder->finalize();
+ LU->Data.erase(LUIt);
- Data.emplace(SN, std::move(*Builder).getData());
+ return std::move(*Builder).getData();
}
-void LUSummaryConsumer::run() {
- assert(!WasRun && "run() must be called exactly once");
- WasRun = true;
- for (auto &[SN, EntityData] : LU->Data) {
- run(SN, EntityData);
+llvm::Expected<SummaryDataStore>
+LUSummaryConsumer::run(llvm::ArrayRef<SummaryName> Names) {
+ SummaryDataStore Store;
+ for (const auto &SN : Names) {
+ auto Result = build(SN);
+ if (!Result)
+ return Result.takeError();
+ Store.Data.emplace(SN, std::move(*Result));
+ }
+ return Store;
+}
+
+SummaryDataStore LUSummaryConsumer::run() && {
+ SummaryDataStore Store;
+ // Snapshot names first: build() erases entries from LU->Data, so iterating
+ // directly over LU->Data while calling build() would invalidate iterators.
+ std::vector<SummaryName> Names;
+ for (const auto &[SN, _] : LU->Data) {
+ Names.push_back(SN);
+ }
+ for (const auto &SN : Names) {
+ auto Result = build(SN);
+ if (!Result) {
+ llvm::consumeError(Result.takeError());
+ continue;
+ }
+ Store.Data.emplace(SN, std::move(*Result));
}
+ return Store;
}
diff --git a/clang/unittests/Analysis/Scalable/SummaryData/SummaryDataTest.cpp b/clang/unittests/Analysis/Scalable/SummaryData/SummaryDataTest.cpp
index 6400f11a7d2be..3170186f09cfb 100644
--- a/clang/unittests/Analysis/Scalable/SummaryData/SummaryDataTest.cpp
+++ b/clang/unittests/Analysis/Scalable/SummaryData/SummaryDataTest.cpp
@@ -17,6 +17,7 @@
#include "clang/Analysis/Scalable/SummaryData/LUSummaryConsumer.h"
#include "clang/Analysis/Scalable/SummaryData/SummaryDataBuilderRegistry.h"
#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
+#include "llvm/Testing/Support/Error.h"
#include "gtest/gtest.h"
#include <algorithm>
#include <memory>
@@ -222,7 +223,8 @@ TEST(SummaryDataBuilderRegistryTest, BuilderCanBeInstantiated) {
EXPECT_NE(SummaryDataBuilderRegistry::instantiate("Analysis4"), nullptr);
}
-TEST_F(LUSummaryConsumerTest, Run) {
+// run() — processes all registered analyses present in the LUSummary.
+TEST_F(LUSummaryConsumerTest, RunAll) {
auto LU = makeLUSummary();
const auto E1 = addEntity(*LU, "Entity1");
const auto E2 = addEntity(*LU, "Entity2");
@@ -233,63 +235,156 @@ TEST_F(LUSummaryConsumerTest, Run) {
int s2a = insertSummary<Analysis2EntitySummary>(*LU, "Analysis2", E2);
int s2b = insertSummary<Analysis2EntitySummary>(*LU, "Analysis2", E3);
- // no registered builder
+ // No registered builder — Analysis3 data silently skipped.
[[maybe_unused]] int s3a =
insertSummary<Analysis1EntitySummary>(*LU, "Analysis3", E1);
LUSummaryConsumer Consumer(std::move(LU));
- Consumer.run();
+ SummaryDataStore Store = std::move(Consumer).run();
- // Analysis1
{
- auto Data1 = Consumer.getData<Analysis1Data>();
- ASSERT_NE(Data1, nullptr);
-
- // getData ownership transfer — second call returns nullptr
- EXPECT_EQ(Consumer.getData<Analysis1Data>(), nullptr);
+ auto Data1OrErr = Store.get<Analysis1Data>();
+ ASSERT_THAT_EXPECTED(Data1OrErr, llvm::Succeeded());
+ auto &Data1 = *Data1OrErr;
+
+ EXPECT_EQ(Data1.Entries.size(), 2u);
+ EXPECT_TRUE(hasEntry(Data1.Entries, E1, s1a));
+ EXPECT_TRUE(hasEntry(Data1.Entries, E2, s1b));
+ EXPECT_TRUE(Data1.WasFinalized);
+ EXPECT_TRUE(Analysis1BuilderWasDestroyed);
- EXPECT_EQ(Data1->Entries.size(), 2u);
- EXPECT_TRUE(hasEntry(Data1->Entries, E1, s1a));
- EXPECT_TRUE(hasEntry(Data1->Entries, E2, s1b));
+ // take() transfers ownership — subsequent get() returns an error.
+ auto Take1OrErr = Store.take<Analysis1Data>();
+ ASSERT_THAT_EXPECTED(Take1OrErr, llvm::Succeeded());
+ EXPECT_NE(*Take1OrErr, nullptr);
+ EXPECT_THAT_EXPECTED(Store.get<Analysis1Data>(), llvm::Failed());
+ }
- EXPECT_TRUE(Data1->WasFinalized);
+ {
+ auto Data2OrErr = Store.get<Analysis2Data>();
+ ASSERT_THAT_EXPECTED(Data2OrErr, llvm::Succeeded());
+ auto &Data2 = *Data2OrErr;
+ EXPECT_EQ(Data2.Entries.size(), 2u);
+ EXPECT_TRUE(hasEntry(Data2.Entries, E2, s2a));
+ EXPECT_TRUE(hasEntry(Data2.Entries, E3, s2b));
+ EXPECT_TRUE(Data2.WasFinalized);
+ EXPECT_TRUE(Analysis2BuilderWasDestroyed);
- // Builder lifetime
- EXPECT_TRUE(Analysis1BuilderWasDestroyed);
+ auto Take2OrErr = Store.take<Analysis2Data>();
+ ASSERT_THAT_EXPECTED(Take2OrErr, llvm::Succeeded());
+ EXPECT_NE(*Take2OrErr, nullptr);
+ EXPECT_THAT_EXPECTED(Store.get<Analysis2Data>(), llvm::Failed());
}
- // Analysis2
- {
- auto Data2 = Consumer.getData<Analysis2Data>();
- ASSERT_NE(Data2, nullptr);
+ // Unregistered — not present in store.
+ EXPECT_THAT_EXPECTED(Store.get<Analysis3Data>(), llvm::Failed());
- // getData ownership transfer — second call returns nullptr
- EXPECT_EQ(Consumer.getData<Analysis2Data>(), nullptr);
+ // Registered builder but no data in LUSummary — not present in store.
+ EXPECT_THAT_EXPECTED(Store.get<Analysis4Data>(), llvm::Failed());
+ EXPECT_FALSE(Analysis4BuilderWasDestroyed);
+}
- EXPECT_EQ(Data2->Entries.size(), 2u);
- EXPECT_TRUE(hasEntry(Data2->Entries, E2, s2a));
- EXPECT_TRUE(hasEntry(Data2->Entries, E3, s2b));
+// run(names) — processes only the analyses for the given names.
+TEST_F(LUSummaryConsumerTest, RunByName) {
+ auto LU = makeLUSummary();
+ const auto E1 = addEntity(*LU, "Entity1");
+ const auto E2 = addEntity(*LU, "Entity2");
- EXPECT_TRUE(Data2->WasFinalized);
+ int s1a = insertSummary<Analysis1EntitySummary>(*LU, "Analysis1", E1);
+ insertSummary<Analysis2EntitySummary>(*LU, "Analysis2", E2);
- // Builder lifetime
- EXPECT_TRUE(Analysis2BuilderWasDestroyed);
- }
+ LUSummaryConsumer Consumer(std::move(LU));
+ auto StoreOrErr = Consumer.run({SummaryName("Analysis1")});
+ ASSERT_THAT_EXPECTED(StoreOrErr, llvm::Succeeded());
+
+ // Analysis1 was requested and has data — present.
+ auto Data1OrErr = StoreOrErr->get<Analysis1Data>();
+ ASSERT_THAT_EXPECTED(Data1OrErr, llvm::Succeeded());
+ auto &Data1 = *Data1OrErr;
+ EXPECT_EQ(Data1.Entries.size(), 1u);
+ EXPECT_TRUE(hasEntry(Data1.Entries, E1, s1a));
+ EXPECT_TRUE(Data1.WasFinalized);
+
+ // Analysis2 was not requested — not present even though data exists.
+ EXPECT_THAT_EXPECTED(StoreOrErr->get<Analysis2Data>(), llvm::Failed());
+}
- // Analysis3
- {
- // Unregistered builder — Analysis3 data silently skipped
- EXPECT_EQ(Consumer.getData<Analysis3Data>(), nullptr);
- }
+// run(names) — error when a requested name has no data in LUSummary.
+TEST_F(LUSummaryConsumerTest, RunByNameErrorMissingData) {
+ auto LU = makeLUSummary();
+ LUSummaryConsumer Consumer(std::move(LU));
- // Analysis4
- {
- // Registered builder but no data in LUSummary — builder never invoked
- EXPECT_EQ(Consumer.getData<Analysis4Data>(), nullptr);
+ EXPECT_THAT_EXPECTED(Consumer.run({SummaryName("Analysis1")}),
+ llvm::Failed());
+}
- // Builder lifetime
- EXPECT_FALSE(Analysis4BuilderWasDestroyed);
- }
+// run(names) — error when a requested name has no registered builder.
+TEST_F(LUSummaryConsumerTest, RunByNameErrorMissingBuilder) {
+ auto LU = makeLUSummary();
+ const auto E1 = addEntity(*LU, "Entity1");
+ insertSummary<Analysis1EntitySummary>(*LU, "Analysis3", E1);
+
+ LUSummaryConsumer Consumer(std::move(LU));
+
+ // Analysis3 has data but no registered builder.
+ EXPECT_THAT_EXPECTED(Consumer.run({SummaryName("Analysis3")}),
+ llvm::Failed());
+}
+
+// run<DataTs...>() — type-safe subset.
+TEST_F(LUSummaryConsumerTest, RunByType) {
+ auto LU = makeLUSummary();
+ const auto E1 = addEntity(*LU, "Entity1");
+ const auto E2 = addEntity(*LU, "Entity2");
+
+ int s1a = insertSummary<Analysis1EntitySummary>(*LU, "Analysis1", E1);
+ insertSummary<Analysis2EntitySummary>(*LU, "Analysis2", E2);
+
+ LUSummaryConsumer Consumer(std::move(LU));
+ auto StoreOrErr = Consumer.run<Analysis1Data>();
+ ASSERT_THAT_EXPECTED(StoreOrErr, llvm::Succeeded());
+
+ // Analysis1 was requested — present.
+ auto Data1OrErr = StoreOrErr->get<Analysis1Data>();
+ ASSERT_THAT_EXPECTED(Data1OrErr, llvm::Succeeded());
+ auto &Data1 = *Data1OrErr;
+ EXPECT_EQ(Data1.Entries.size(), 1u);
+ EXPECT_TRUE(hasEntry(Data1.Entries, E1, s1a));
+ EXPECT_TRUE(Data1.WasFinalized);
+
+ // Analysis2 was not requested — not present even though data exists.
+ EXPECT_THAT_EXPECTED(StoreOrErr->get<Analysis2Data>(), llvm::Failed());
+}
+
+// run<DataTs...>() — error when a requested type has no data in LUSummary.
+TEST_F(LUSummaryConsumerTest, RunByTypeErrorMissingData) {
+ auto LU = makeLUSummary();
+ LUSummaryConsumer Consumer(std::move(LU));
+
+ EXPECT_THAT_EXPECTED(Consumer.run<Analysis1Data>(), llvm::Failed());
+}
+
+// contains() — present entries return true; absent entries return false.
+TEST_F(LUSummaryConsumerTest, Contains) {
+ auto LU = makeLUSummary();
+ const auto E1 = addEntity(*LU, "Entity1");
+ insertSummary<Analysis1EntitySummary>(*LU, "Analysis1", E1);
+
+ LUSummaryConsumer Consumer(std::move(LU));
+ SummaryDataStore Store = std::move(Consumer).run();
+
+ // Type-safe variant.
+ EXPECT_TRUE(Store.contains<Analysis1Data>());
+ EXPECT_FALSE(Store.contains<Analysis2Data>());
+
+ // Name-based variant.
+ EXPECT_TRUE(Store.contains(SummaryName("Analysis1")));
+ EXPECT_FALSE(Store.contains(SummaryName("Analysis2")));
+
+ // After take(), contains() returns false.
+ llvm::cantFail(Store.take<Analysis1Data>());
+ EXPECT_FALSE(Store.contains<Analysis1Data>());
+ EXPECT_FALSE(Store.contains(SummaryName("Analysis1")));
}
} // namespace
More information about the cfe-commits
mailing list