[clang] [clang][ssaf] Add LUSummary consumer APIs (PR #185803)
Aviral Goel via cfe-commits
cfe-commits at lists.llvm.org
Wed Mar 11 15:57:50 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/4] Initial implementation
---
.../Scalable/EntityLinker/LUSummary.h | 1 +
.../Scalable/SummaryView/LUSummaryConsumer.h | 81 +++++++
.../Scalable/SummaryView/SummaryView.h | 32 +++
.../Scalable/SummaryView/SummaryViewBuilder.h | 104 +++++++++
.../SummaryView/SummaryViewBuilderRegistry.h | 44 ++++
clang/lib/Analysis/Scalable/CMakeLists.txt | 2 +
.../SummaryView/LUSummaryConsumer.cpp | 51 +++++
.../SummaryViewBuilderRegistry.cpp | 23 ++
.../Analysis/Scalable/CMakeLists.txt | 4 +
.../SummaryView/LUSummaryConsumerTest.cpp | 215 ++++++++++++++++++
.../SummaryView/MockSummaryViewBuilder1.cpp | 45 ++++
.../SummaryView/MockSummaryViewBuilder2.cpp | 41 ++++
.../SummaryView/MockSummaryViewBuilders.h | 68 ++++++
.../SummaryViewBuilderRegistryTest.cpp | 82 +++++++
14 files changed, 793 insertions(+)
create mode 100644 clang/include/clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h
create mode 100644 clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h
create mode 100644 clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h
create mode 100644 clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h
create mode 100644 clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp
create mode 100644 clang/lib/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.cpp
create mode 100644 clang/unittests/Analysis/Scalable/SummaryView/LUSummaryConsumerTest.cpp
create mode 100644 clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder1.cpp
create mode 100644 clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder2.cpp
create mode 100644 clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilders.h
create mode 100644 clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp
diff --git a/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummary.h b/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummary.h
index e24f7f581185c..7d0ec92f5c9e7 100644
--- a/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummary.h
+++ b/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummary.h
@@ -31,6 +31,7 @@ namespace clang::ssaf {
/// together. It contains deduplicated entities with their linkage information
/// and the merged entity summaries.
class LUSummary {
+ friend class LUSummaryConsumer;
friend class SerializationFormat;
friend class TestFixture;
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h b/clang/include/clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h
new file mode 100644
index 0000000000000..eb71b2add386d
--- /dev/null
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h
@@ -0,0 +1,81 @@
+//===- LUSummaryConsumer.h ------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// LUSummaryConsumer constructs SummaryView objects by routing LUSummary entity
+// data to the corresponding SummaryViewBuilder objects.
+//
+// Typical usage:
+//
+// auto LU = Format->readLUSummary(Path);
+//
+// LUSummaryConsumer Consumer(std::move(LU));
+// Consumer.run();
+//
+// auto View = Consumer.getView<MyView>();
+// // View is std::unique_ptr<MyView>; Consumer no longer holds it.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_LUSUMMARYCONSUMER_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_LUSUMMARYCONSUMER_H
+
+#include "clang/Analysis/Scalable/EntityLinker/LUSummary.h"
+#include "clang/Analysis/Scalable/Model/EntityId.h"
+#include "clang/Analysis/Scalable/Model/SummaryName.h"
+#include "clang/Analysis/Scalable/SummaryView/SummaryView.h"
+#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
+#include <map>
+#include <memory>
+
+namespace clang::ssaf {
+
+/// Consumes a LUSummary by dispatching its entity data to all registered
+/// SummaryViewBuilders and collecting the resulting views.
+class LUSummaryConsumer final {
+ std::unique_ptr<LUSummary> LU;
+ std::map<SummaryName, std::unique_ptr<SummaryView>> Views;
+ bool WasRun = false;
+
+public:
+ explicit LUSummaryConsumer(std::unique_ptr<LUSummary> LU)
+ : LU(std::move(LU)) {}
+
+ /// Instantiates a builder for each SummaryName present in the LUSummary,
+ /// delivers its entities, finalizes it, and stores the resulting view.
+ /// Each builder is fully processed (addSummary → finalize → getView) before
+ /// the next SummaryName is visited. Builders are discarded on return.
+ ///
+ /// \pre Must be called exactly once.
+ void run();
+
+ /// Transfers ownership of the view for \p ViewT to the caller.
+ ///
+ /// Returns nullptr if no builder for \p ViewT was registered or run() has
+ /// not been called. A second call for the same ViewT also returns nullptr.
+ template <typename ViewT> [[nodiscard]] std::unique_ptr<ViewT> getView() {
+ auto It = Views.find(ViewT::summaryName());
+ if (It == Views.end()) {
+ return nullptr;
+ }
+ auto *RawPtr = static_cast<ViewT *>(It->second.release());
+ Views.erase(It);
+ return std::unique_ptr<ViewT>(RawPtr);
+ }
+
+private:
+ using EntityDataMap = std::map<EntityId, std::unique_ptr<EntitySummary>>;
+
+ /// Processes all entities for a single SummaryName: instantiates the
+ /// registered builder, delivers entities, finalizes, and stores the view.
+ /// Does nothing if no builder is registered for \p SN.
+ void run(const SummaryName &SN, EntityDataMap &Data);
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_LUSUMMARYCONSUMER_H
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h
new file mode 100644
index 0000000000000..34accf5be3c20
--- /dev/null
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h
@@ -0,0 +1,32 @@
+//===- SummaryView.h
+//-------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Abstract base class for all whole-program analysis views built from
+// LUSummary data. Carries no query API — all analysis-specific methods live
+// on concrete subclasses.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEW_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEW_H
+
+namespace clang::ssaf {
+
+/// Abstract base class for whole-program analysis views.
+///
+/// Concrete view classes inherit from this and add a static
+/// \c summaryName() method along with their analysis-specific query API.
+class SummaryView {
+public:
+ virtual ~SummaryView() = default;
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEW_H
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h
new file mode 100644
index 0000000000000..d3d05587b30d9
--- /dev/null
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h
@@ -0,0 +1,104 @@
+//===- SummaryViewBuilder.h
+//------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines SummaryViewBuilderBase (abstract base known to the
+// registry and LUSummaryConsumer) and the typed intermediate template
+// SummaryViewBuilder<ViewT, SummaryT> that concrete builders inherit from.
+//
+// To implement a view builder, inherit from SummaryViewBuilder:
+//
+// class MyViewBuilder
+// : public SummaryViewBuilder<MyView, MyEntitySummary> {
+// public:
+// void addSummary(EntityId Id,
+// std::unique_ptr<MyEntitySummary> Summary) override {
+// // accumulate into getView()
+// }
+// // summaryName() and getView() && provided by the intermediate.
+// // override finalize() if post-processing is needed.
+// };
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDER_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDER_H
+
+#include "clang/Analysis/Scalable/Model/EntityId.h"
+#include "clang/Analysis/Scalable/Model/SummaryName.h"
+#include "clang/Analysis/Scalable/SummaryView/SummaryView.h"
+#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
+#include <memory>
+
+namespace clang::ssaf {
+
+/// Abstract base class for all summary view builders.
+///
+/// Known to the registry and LUSummaryConsumer. Receives entities one at a
+/// time via \c addSummary(), is finalized via \c finalize(), and transfers
+/// ownership of the built view via \c getView() &&.
+class SummaryViewBuilderBase {
+public:
+ virtual ~SummaryViewBuilderBase() = default;
+
+ /// Returns the SummaryName this builder handles.
+ virtual SummaryName summaryName() const = 0;
+
+ /// Called once per entity belonging to this builder's analysis.
+ /// Takes ownership of the summary data.
+ virtual void addSummary(EntityId Id,
+ std::unique_ptr<EntitySummary> Summary) = 0;
+
+ /// Called after all entities have been added.
+ virtual void finalize() {}
+
+ /// Transfers ownership of the built view (type-erased).
+ /// Called by LUSummaryConsumer after finalize(). The rvalue ref-qualifier
+ /// enforces single use — the builder cannot be accessed after this call.
+ virtual std::unique_ptr<SummaryView> getView() && = 0;
+};
+
+/// Typed intermediate template that concrete builders inherit from.
+///
+/// Provides:
+/// - \c summaryName() derived from \c ViewT::summaryName().
+/// - \c getView() && which moves out the built view.
+/// - A protected \c getView() accessor for use during construction.
+/// - NVI dispatch: seals the base \c addSummary(EntityId, EntitySummary) as
+/// \c final, downcasts, and dispatches to the typed overload.
+///
+/// Concrete builders only need to implement the typed
+/// \c addSummary(EntityId, unique_ptr<SummaryT>) overload.
+template <typename ViewT, typename SummaryT>
+class SummaryViewBuilder : public SummaryViewBuilderBase {
+ std::unique_ptr<ViewT> View;
+
+protected:
+ ViewT &getView() & { return *View; }
+
+public:
+ SummaryViewBuilder() : View(std::make_unique<ViewT>()) {}
+
+ SummaryName summaryName() const override { return ViewT::summaryName(); }
+
+ std::unique_ptr<SummaryView> getView() && override { return std::move(View); }
+
+ /// Typed customization point — concrete builders override this.
+ virtual void addSummary(EntityId Id, std::unique_ptr<SummaryT> Summary) = 0;
+
+private:
+ /// Seals the base overload, downcasts, and dispatches to the typed overload.
+ void addSummary(EntityId Id, std::unique_ptr<EntitySummary> Summary) final {
+ addSummary(Id, std::unique_ptr<SummaryT>(
+ static_cast<SummaryT *>(Summary.release())));
+ }
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDER_H
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h
new file mode 100644
index 0000000000000..c02843ad940d3
--- /dev/null
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h
@@ -0,0 +1,44 @@
+//===- SummaryViewBuilderRegistry.h
+//----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Registry for SummaryViewBuilders and helper functions.
+//
+// To register a builder, add this to the builder's translation unit:
+//
+// static SummaryViewBuilderRegistry::Add<MyViewBuilder>
+// Register("MyAnalysis", "View builder for MyAnalysis");
+//
+// The registry entry name must match MyView::summaryName().
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDERREGISTRY_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDERREGISTRY_H
+
+#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h"
+#include "clang/Support/Compiler.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Registry.h"
+
+namespace clang::ssaf {
+
+/// Check if a SummaryViewBuilder was registered with a given name.
+bool isSummaryViewBuilderRegistered(llvm::StringRef Name);
+
+/// Registry for adding new SummaryViewBuilder implementations.
+using SummaryViewBuilderRegistry = llvm::Registry<SummaryViewBuilderBase>;
+
+} // namespace clang::ssaf
+
+namespace llvm {
+extern template class CLANG_TEMPLATE_ABI
+ Registry<clang::ssaf::SummaryViewBuilderBase>;
+} // namespace llvm
+
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDERREGISTRY_H
diff --git a/clang/lib/Analysis/Scalable/CMakeLists.txt b/clang/lib/Analysis/Scalable/CMakeLists.txt
index 4593fbcd515b5..3da854558f760 100644
--- a/clang/lib/Analysis/Scalable/CMakeLists.txt
+++ b/clang/lib/Analysis/Scalable/CMakeLists.txt
@@ -18,6 +18,8 @@ add_clang_library(clangAnalysisScalable
Serialization/JSONFormat/TUSummary.cpp
Serialization/JSONFormat/TUSummaryEncoding.cpp
Serialization/SerializationFormatRegistry.cpp
+ SummaryView/LUSummaryConsumer.cpp
+ SummaryView/SummaryViewBuilderRegistry.cpp
Support/ErrorBuilder.cpp
TUSummary/ExtractorRegistry.cpp
TUSummary/TUSummaryBuilder.cpp
diff --git a/clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp b/clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp
new file mode 100644
index 0000000000000..a3db29be840d6
--- /dev/null
+++ b/clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp
@@ -0,0 +1,51 @@
+//===- LUSummaryConsumer.cpp ----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h"
+#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
+#include <cassert>
+#include <memory>
+
+using namespace clang;
+using namespace ssaf;
+
+static std::unique_ptr<SummaryViewBuilderBase>
+instantiateBuilder(const SummaryName &SN) {
+ for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
+ if (Entry.getName() == SN.str()) {
+ return Entry.instantiate();
+ }
+ }
+ return nullptr;
+}
+
+void LUSummaryConsumer::run(const SummaryName &SN, EntityDataMap &Data) {
+ auto Builder = instantiateBuilder(SN);
+ if (!Builder) {
+ return;
+ }
+
+ assert(Builder->summaryName() == SN &&
+ "registry entry name must match SummaryViewBuilder::summaryName()");
+
+ for (auto &[Id, Summary] : Data) {
+ Builder->addSummary(Id, std::move(Summary));
+ }
+
+ Builder->finalize();
+
+ Views.emplace(SN, std::move(*Builder).getView());
+}
+
+void LUSummaryConsumer::run() {
+ assert(!WasRun && "run() must be called exactly once");
+ WasRun = true;
+ for (auto &[SN, Data] : LU->Data) {
+ run(SN, Data);
+ }
+}
diff --git a/clang/lib/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.cpp b/clang/lib/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.cpp
new file mode 100644
index 0000000000000..eb64394ba4401
--- /dev/null
+++ b/clang/lib/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.cpp
@@ -0,0 +1,23 @@
+//===- SummaryViewBuilderRegistry.cpp -------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
+
+using namespace clang;
+using namespace ssaf;
+
+LLVM_INSTANTIATE_REGISTRY(SummaryViewBuilderRegistry)
+
+bool ssaf::isSummaryViewBuilderRegistered(llvm::StringRef Name) {
+ for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
+ if (Entry.getName() == Name) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/clang/unittests/Analysis/Scalable/CMakeLists.txt b/clang/unittests/Analysis/Scalable/CMakeLists.txt
index 54f9de71884dd..fbeefea15b421 100644
--- a/clang/unittests/Analysis/Scalable/CMakeLists.txt
+++ b/clang/unittests/Analysis/Scalable/CMakeLists.txt
@@ -19,6 +19,10 @@ add_distinct_clang_unittest(ClangScalableAnalysisTests
Serialization/JSONFormatTest/LUSummaryTest.cpp
Serialization/JSONFormatTest/TUSummaryTest.cpp
SummaryNameTest.cpp
+ SummaryView/LUSummaryConsumerTest.cpp
+ SummaryView/MockSummaryViewBuilder1.cpp
+ SummaryView/MockSummaryViewBuilder2.cpp
+ SummaryView/SummaryViewBuilderRegistryTest.cpp
TestFixture.cpp
TUSummaryBuilderTest.cpp
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/LUSummaryConsumerTest.cpp b/clang/unittests/Analysis/Scalable/SummaryView/LUSummaryConsumerTest.cpp
new file mode 100644
index 0000000000000..bab8730ec3343
--- /dev/null
+++ b/clang/unittests/Analysis/Scalable/SummaryView/LUSummaryConsumerTest.cpp
@@ -0,0 +1,215 @@
+//===- LUSummaryConsumerTest.cpp ------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h"
+#include "../TestFixture.h"
+#include "MockSummaryViewBuilders.h"
+#include "clang/Analysis/Scalable/EntityLinker/LUSummary.h"
+#include "clang/Analysis/Scalable/Model/BuildNamespace.h"
+#include "clang/Analysis/Scalable/Model/EntityId.h"
+#include "clang/Analysis/Scalable/Model/EntityIdTable.h"
+#include "clang/Analysis/Scalable/Model/EntityName.h"
+#include "clang/Analysis/Scalable/Model/SummaryName.h"
+#include "gtest/gtest.h"
+#include <memory>
+
+using namespace clang;
+using namespace ssaf;
+
+namespace {
+
+class LUSummaryConsumerTest : public TestFixture {
+protected:
+ void SetUp() override { clearMockBuilderLog(); }
+
+ std::unique_ptr<LUSummary> makeLUSummary() {
+ NestedBuildNamespace NS(
+ {BuildNamespace(BuildNamespaceKind::LinkUnit, "TestLU")});
+ return std::make_unique<LUSummary>(std::move(NS));
+ }
+
+ /// Add an entity to the LUSummary's id table and return its EntityId.
+ EntityId addEntity(LUSummary &LU, llvm::StringRef USR) {
+ NestedBuildNamespace NS(
+ {BuildNamespace(BuildNamespaceKind::LinkUnit, "TestLU")});
+ EntityName Name(USR.str(), "", NS);
+ return getIdTable(LU).getId(Name);
+ }
+
+ /// Insert a pre-built EntitySummary into LUSummary::Data.
+ void insertSummary(LUSummary &LU, llvm::StringRef SummaryNameStr, EntityId Id,
+ std::unique_ptr<EntitySummary> Summary) {
+ getData(LU)[SummaryName(SummaryNameStr.str())][Id] = std::move(Summary);
+ }
+};
+
+// ---------------------------------------------------------------------------
+// No matching data in LUSummary
+// ---------------------------------------------------------------------------
+
+TEST_F(LUSummaryConsumerTest, NoMatchingData_AddSummaryNeverCalled) {
+ auto LU = makeLUSummary();
+ // Insert data for a summary name that has no registered builder.
+ auto Id = addEntity(*LU, "A");
+ insertSummary(*LU, "UnregisteredAnalysis", Id,
+ std::make_unique<MockEntitySummary1>());
+
+ LUSummaryConsumer Consumer(std::move(LU));
+ Consumer.run();
+
+ EXPECT_EQ(MockBuilderLog.find("addSummary"), std::string::npos);
+}
+
+TEST_F(LUSummaryConsumerTest, NoMatchingData_FinalizeNotCalled) {
+ auto LU = makeLUSummary(); // empty — no Mock1 data
+
+ LUSummaryConsumer Consumer(std::move(LU));
+ Consumer.run();
+
+ EXPECT_EQ(MockBuilderLog.find("finalize"), std::string::npos);
+}
+
+TEST_F(LUSummaryConsumerTest, NoMatchingData_GetViewReturnsNull) {
+ auto LU = makeLUSummary();
+
+ LUSummaryConsumer Consumer(std::move(LU));
+ Consumer.run();
+
+ EXPECT_EQ(Consumer.getView<MockView1>(), nullptr);
+}
+
+// ---------------------------------------------------------------------------
+// Matching data delivered correctly
+// ---------------------------------------------------------------------------
+
+TEST_F(LUSummaryConsumerTest, MatchingData_AddSummaryCalledPerEntity) {
+ auto LU = makeLUSummary();
+ auto Id1 = addEntity(*LU, "A");
+ auto Id2 = addEntity(*LU, "B");
+ insertSummary(*LU, "Mock1", Id1, std::make_unique<MockEntitySummary1>());
+ insertSummary(*LU, "Mock1", Id2, std::make_unique<MockEntitySummary1>());
+
+ LUSummaryConsumer Consumer(std::move(LU));
+ Consumer.run();
+
+ auto View = Consumer.getView<MockView1>();
+ ASSERT_NE(View, nullptr);
+ EXPECT_EQ(View->Ids.size(), 2u);
+ EXPECT_NE(std::find(View->Ids.begin(), View->Ids.end(), Id1),
+ View->Ids.end());
+ EXPECT_NE(std::find(View->Ids.begin(), View->Ids.end(), Id2),
+ View->Ids.end());
+}
+
+TEST_F(LUSummaryConsumerTest, MatchingData_FinalizeCalledAfterAllAddSummary) {
+ auto LU = makeLUSummary();
+ auto Id = addEntity(*LU, "A");
+ insertSummary(*LU, "Mock1", Id, std::make_unique<MockEntitySummary1>());
+
+ LUSummaryConsumer Consumer(std::move(LU));
+ Consumer.run();
+
+ // Verify ordering in the log: all addSummary entries must precede all
+ // finalize entries.
+ auto AddSummaryPos = MockBuilderLog.find("addSummary");
+ auto FinalizePos = MockBuilderLog.find("finalize");
+ ASSERT_NE(AddSummaryPos, std::string::npos);
+ ASSERT_NE(FinalizePos, std::string::npos);
+ EXPECT_LT(AddSummaryPos, FinalizePos);
+}
+
+// ---------------------------------------------------------------------------
+// Multiple builders receive only their own entities
+// ---------------------------------------------------------------------------
+
+TEST_F(LUSummaryConsumerTest, MultipleBuilders_IndependentEntities) {
+ auto LU = makeLUSummary();
+ auto Id1 = addEntity(*LU, "A");
+ auto Id2 = addEntity(*LU, "B");
+ insertSummary(*LU, "Mock1", Id1, std::make_unique<MockEntitySummary1>());
+ insertSummary(*LU, "Mock2", Id2, std::make_unique<MockEntitySummary2>());
+
+ LUSummaryConsumer Consumer(std::move(LU));
+ Consumer.run();
+
+ auto View1 = Consumer.getView<MockView1>();
+ ASSERT_NE(View1, nullptr);
+ ASSERT_EQ(View1->Ids.size(), 1u);
+ EXPECT_EQ(View1->Ids[0], Id1);
+
+ auto View2 = Consumer.getView<MockView2>();
+ ASSERT_NE(View2, nullptr);
+ ASSERT_EQ(View2->Ids.size(), 1u);
+ EXPECT_EQ(View2->Ids[0], Id2);
+}
+
+// ---------------------------------------------------------------------------
+// getView ownership transfer
+// ---------------------------------------------------------------------------
+
+TEST_F(LUSummaryConsumerTest, GetView_FirstCallReturnsNonNull) {
+ auto LU = makeLUSummary();
+ auto Id = addEntity(*LU, "A");
+ insertSummary(*LU, "Mock1", Id, std::make_unique<MockEntitySummary1>());
+
+ LUSummaryConsumer Consumer(std::move(LU));
+ Consumer.run();
+
+ EXPECT_NE(Consumer.getView<MockView1>(), nullptr);
+}
+
+TEST_F(LUSummaryConsumerTest, GetView_SecondCallReturnsNull) {
+ auto LU = makeLUSummary();
+ auto Id = addEntity(*LU, "A");
+ insertSummary(*LU, "Mock1", Id, std::make_unique<MockEntitySummary1>());
+
+ LUSummaryConsumer Consumer(std::move(LU));
+ Consumer.run();
+
+ auto First = Consumer.getView<MockView1>();
+ EXPECT_NE(First, nullptr);
+ EXPECT_EQ(Consumer.getView<MockView1>(), nullptr);
+}
+
+TEST_F(LUSummaryConsumerTest, GetView_UnregisteredViewReturnsNull) {
+ // MockView1 is registered; a hypothetical OtherView is not.
+ struct OtherView : public SummaryView {
+ static SummaryName summaryName() { return SummaryName("Other"); }
+ };
+
+ auto LU = makeLUSummary();
+ LUSummaryConsumer Consumer(std::move(LU));
+ Consumer.run();
+
+ EXPECT_EQ(Consumer.getView<OtherView>(), nullptr);
+}
+
+// ---------------------------------------------------------------------------
+// Builder lifetime
+// ---------------------------------------------------------------------------
+
+TEST_F(LUSummaryConsumerTest, BuildersDestroyedAfterRun) {
+ auto LU = makeLUSummary();
+ auto Id1 = addEntity(*LU, "A");
+ auto Id2 = addEntity(*LU, "B");
+ insertSummary(*LU, "Mock1", Id1, std::make_unique<MockEntitySummary1>());
+ insertSummary(*LU, "Mock2", Id2, std::make_unique<MockEntitySummary2>());
+
+ LUSummaryConsumer Consumer(std::move(LU));
+ Consumer.run();
+
+ // Both destructors must have been called by the time run() returns.
+ EXPECT_NE(
+ MockBuilderLog.find("MockSummaryViewBuilder1 destructor was invoked"),
+ std::string::npos);
+ EXPECT_NE(
+ MockBuilderLog.find("MockSummaryViewBuilder2 destructor was invoked"),
+ std::string::npos);
+}
+
+} // namespace
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder1.cpp b/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder1.cpp
new file mode 100644
index 0000000000000..2bfc526969393
--- /dev/null
+++ b/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder1.cpp
@@ -0,0 +1,45 @@
+//===- MockSummaryViewBuilder1.cpp ----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MockSummaryViewBuilders.h"
+#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
+
+using namespace clang;
+using namespace ssaf;
+
+std::string clang::ssaf::MockBuilderLog;
+
+void clang::ssaf::clearMockBuilderLog() { MockBuilderLog.clear(); }
+
+namespace {
+
+class MockSummaryViewBuilder1
+ : public SummaryViewBuilder<MockView1, MockEntitySummary1> {
+public:
+ MockSummaryViewBuilder1() {
+ MockBuilderLog += "MockSummaryViewBuilder1 constructor was invoked\n";
+ }
+
+ ~MockSummaryViewBuilder1() {
+ MockBuilderLog += "MockSummaryViewBuilder1 destructor was invoked\n";
+ }
+
+ void addSummary(EntityId Id, std::unique_ptr<MockEntitySummary1>) override {
+ MockBuilderLog += "MockSummaryViewBuilder1 addSummary was invoked\n";
+ getView().Ids.push_back(Id);
+ }
+
+ void finalize() override {
+ MockBuilderLog += "MockSummaryViewBuilder1 finalize was invoked\n";
+ }
+};
+
+static SummaryViewBuilderRegistry::Add<MockSummaryViewBuilder1>
+ Register("Mock1", "Mock view builder 1");
+
+} // namespace
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder2.cpp b/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder2.cpp
new file mode 100644
index 0000000000000..b43e7f69bbb8e
--- /dev/null
+++ b/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder2.cpp
@@ -0,0 +1,41 @@
+//===- MockSummaryViewBuilder2.cpp ----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MockSummaryViewBuilders.h"
+#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
+
+using namespace clang;
+using namespace ssaf;
+
+namespace {
+
+class MockSummaryViewBuilder2
+ : public SummaryViewBuilder<MockView2, MockEntitySummary2> {
+public:
+ MockSummaryViewBuilder2() {
+ MockBuilderLog += "MockSummaryViewBuilder2 constructor was invoked\n";
+ }
+
+ ~MockSummaryViewBuilder2() {
+ MockBuilderLog += "MockSummaryViewBuilder2 destructor was invoked\n";
+ }
+
+ void addSummary(EntityId Id, std::unique_ptr<MockEntitySummary2>) override {
+ MockBuilderLog += "MockSummaryViewBuilder2 addSummary was invoked\n";
+ getView().Ids.push_back(Id);
+ }
+
+ void finalize() override {
+ MockBuilderLog += "MockSummaryViewBuilder2 finalize was invoked\n";
+ }
+};
+
+static SummaryViewBuilderRegistry::Add<MockSummaryViewBuilder2>
+ Register("Mock2", "Mock view builder 2");
+
+} // namespace
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilders.h b/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilders.h
new file mode 100644
index 0000000000000..f6a46a65d2256
--- /dev/null
+++ b/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilders.h
@@ -0,0 +1,68 @@
+//===- MockSummaryViewBuilders.h
+//-------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Shared mock types for SummaryView tests. Two mock analyses ("Mock1" and
+// "Mock2") are defined here, with their builders registered in
+// MockSummaryViewBuilder1.cpp and MockSummaryViewBuilder2.cpp.
+//
+// Tests observe builder behaviour via MockBuilderLog, which records lifecycle
+// events (constructor, addSummary, finalize, destructor) as newline-terminated
+// strings. Call clearMockBuilderLog() in SetUp() to isolate tests.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef CLANG_UNITTESTS_ANALYSIS_SCALABLE_SUMMARYVIEW_MOCKSUMMARYVIEWBUILDERS_H
+#define CLANG_UNITTESTS_ANALYSIS_SCALABLE_SUMMARYVIEW_MOCKSUMMARYVIEWBUILDERS_H
+
+#include "clang/Analysis/Scalable/Model/EntityId.h"
+#include "clang/Analysis/Scalable/Model/SummaryName.h"
+#include "clang/Analysis/Scalable/SummaryView/SummaryView.h"
+#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h"
+#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
+#include <string>
+#include <vector>
+
+namespace clang::ssaf {
+
+// ---- Mock entity summaries -----------------------------------------------
+
+class MockEntitySummary1 : public EntitySummary {
+public:
+ SummaryName getSummaryName() const override { return SummaryName("Mock1"); }
+};
+
+class MockEntitySummary2 : public EntitySummary {
+public:
+ SummaryName getSummaryName() const override { return SummaryName("Mock2"); }
+};
+
+// ---- Mock views ------------------------------------------------------------
+
+class MockView1 : public SummaryView {
+public:
+ static SummaryName summaryName() { return SummaryName("Mock1"); }
+ std::vector<EntityId> Ids;
+};
+
+class MockView2 : public SummaryView {
+public:
+ static SummaryName summaryName() { return SummaryName("Mock2"); }
+ std::vector<EntityId> Ids;
+};
+
+// ---- Shared log ------------------------------------------------------------
+// Defined in MockSummaryViewBuilder1.cpp; written by both mock builders.
+
+extern std::string MockBuilderLog;
+
+void clearMockBuilderLog();
+
+} // namespace clang::ssaf
+
+#endif // CLANG_UNITTESTS_ANALYSIS_SCALABLE_SUMMARYVIEW_MOCKSUMMARYVIEWBUILDERS_H
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp b/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp
new file mode 100644
index 0000000000000..99803ef6ffff7
--- /dev/null
+++ b/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp
@@ -0,0 +1,82 @@
+//===- SummaryViewBuilderRegistryTest.cpp ---------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
+#include "MockSummaryViewBuilders.h"
+#include "llvm/ADT/StringRef.h"
+#include "gtest/gtest.h"
+#include <set>
+
+using namespace clang;
+using namespace ssaf;
+
+namespace {
+
+class SummaryViewBuilderRegistryTest : public ::testing::Test {
+protected:
+ void SetUp() override { clearMockBuilderLog(); }
+};
+
+TEST_F(SummaryViewBuilderRegistryTest, isSummaryViewBuilderRegistered) {
+ EXPECT_FALSE(isSummaryViewBuilderRegistered("Non-existent-builder"));
+ EXPECT_TRUE(isSummaryViewBuilderRegistered("Mock1"));
+ EXPECT_TRUE(isSummaryViewBuilderRegistered("Mock2"));
+}
+
+TEST_F(SummaryViewBuilderRegistryTest, EnumeratingRegistryEntries) {
+ std::set<llvm::StringRef> ActualNames;
+ for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
+ bool Inserted = ActualNames.insert(Entry.getName()).second;
+ EXPECT_TRUE(Inserted);
+ }
+
+ EXPECT_EQ(ActualNames, (std::set<llvm::StringRef>{"Mock1", "Mock2"}));
+}
+
+TEST_F(SummaryViewBuilderRegistryTest, InstantiatingBuilder1) {
+ {
+ // Find Mock1 entry explicitly.
+ std::unique_ptr<SummaryViewBuilderBase> B1;
+ for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
+ if (Entry.getName() == "Mock1") {
+ B1 = Entry.instantiate();
+ }
+ }
+
+ ASSERT_TRUE(B1);
+ EXPECT_EQ(B1->summaryName(), SummaryName("Mock1"));
+ EXPECT_TRUE(MockBuilderLog.find(
+ "MockSummaryViewBuilder1 constructor was invoked") !=
+ std::string::npos);
+ }
+ EXPECT_TRUE(
+ MockBuilderLog.find("MockSummaryViewBuilder1 destructor was invoked") !=
+ std::string::npos);
+}
+
+TEST_F(SummaryViewBuilderRegistryTest, InstantiatingBuilder2) {
+ {
+ std::unique_ptr<SummaryViewBuilderBase> B2;
+ for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
+ if (Entry.getName() == "Mock2") {
+ B2 = Entry.instantiate();
+ }
+ }
+
+ ASSERT_TRUE(B2);
+ EXPECT_EQ(B2->summaryName(), SummaryName("Mock2"));
+ EXPECT_TRUE(MockBuilderLog.find(
+ "MockSummaryViewBuilder2 constructor was invoked") !=
+ std::string::npos);
+ }
+ EXPECT_TRUE(
+ MockBuilderLog.find("MockSummaryViewBuilder2 destructor was invoked") !=
+ std::string::npos);
+}
+
+} // namespace
>From 3e97ae7a0ad8ab63d0ea17c55f3cd2b2438532c7 Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Tue, 10 Mar 2026 22:33:38 -0700
Subject: [PATCH 2/4] Fix
---
.../Scalable/SummaryView/SummaryView.h | 3 +--
.../Scalable/SummaryView/SummaryViewBuilder.h | 3 +--
.../SummaryView/SummaryViewBuilderRegistry.h | 3 +--
.../SummaryView/MockSummaryViewBuilders.h | 3 +--
.../SummaryViewBuilderRegistryTest.cpp | 20 +++++++++----------
5 files changed, 14 insertions(+), 18 deletions(-)
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h
index 34accf5be3c20..46778e94976f3 100644
--- a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h
@@ -1,5 +1,4 @@
-//===- SummaryView.h
-//-------------------------------------------------------===//
+//===- SummaryView.h ------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h
index d3d05587b30d9..c71652662f4d9 100644
--- a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h
@@ -1,5 +1,4 @@
-//===- SummaryViewBuilder.h
-//------------------------------------------------===//
+//===- SummaryViewBuilder.h -----------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h
index c02843ad940d3..d3fc08a71a446 100644
--- a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h
@@ -1,5 +1,4 @@
-//===- SummaryViewBuilderRegistry.h
-//----------------------------------------===//
+//===- SummaryViewBuilderRegistry.h ---------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilders.h b/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilders.h
index f6a46a65d2256..8bdcb27ca93f6 100644
--- a/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilders.h
+++ b/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilders.h
@@ -1,5 +1,4 @@
-//===- MockSummaryViewBuilders.h
-//-------------------------------------------===//
+//===- MockSummaryViewBuilders.h ------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp b/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp
index 99803ef6ffff7..b48682500cd39 100644
--- a/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp
+++ b/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp
@@ -50,12 +50,12 @@ TEST_F(SummaryViewBuilderRegistryTest, InstantiatingBuilder1) {
ASSERT_TRUE(B1);
EXPECT_EQ(B1->summaryName(), SummaryName("Mock1"));
- EXPECT_TRUE(MockBuilderLog.find(
- "MockSummaryViewBuilder1 constructor was invoked") !=
- std::string::npos);
+ EXPECT_NE(
+ MockBuilderLog.find("MockSummaryViewBuilder1 constructor was invoked"),
+ std::string::npos);
}
- EXPECT_TRUE(
- MockBuilderLog.find("MockSummaryViewBuilder1 destructor was invoked") !=
+ EXPECT_NE(
+ MockBuilderLog.find("MockSummaryViewBuilder1 destructor was invoked"),
std::string::npos);
}
@@ -70,12 +70,12 @@ TEST_F(SummaryViewBuilderRegistryTest, InstantiatingBuilder2) {
ASSERT_TRUE(B2);
EXPECT_EQ(B2->summaryName(), SummaryName("Mock2"));
- EXPECT_TRUE(MockBuilderLog.find(
- "MockSummaryViewBuilder2 constructor was invoked") !=
- std::string::npos);
+ EXPECT_NE(
+ MockBuilderLog.find("MockSummaryViewBuilder2 constructor was invoked"),
+ std::string::npos);
}
- EXPECT_TRUE(
- MockBuilderLog.find("MockSummaryViewBuilder2 destructor was invoked") !=
+ EXPECT_NE(
+ MockBuilderLog.find("MockSummaryViewBuilder2 destructor was invoked"),
std::string::npos);
}
>From edf933a1d8355485a56f605f6606be57d622d4e8 Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Wed, 11 Mar 2026 13:40:46 -0700
Subject: [PATCH 3/4] Testing
---
.../Analysis/Scalable/CMakeLists.txt | 2 -
.../SummaryView/LUSummaryConsumerTest.cpp | 336 +++++++++++-------
.../SummaryView/MockSummaryViewBuilder1.cpp | 45 ---
.../SummaryView/MockSummaryViewBuilder2.cpp | 41 ---
.../SummaryView/MockSummaryViewBuilders.h | 67 ----
.../SummaryViewBuilderRegistryTest.cpp | 66 ++--
6 files changed, 223 insertions(+), 334 deletions(-)
delete mode 100644 clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder1.cpp
delete mode 100644 clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder2.cpp
delete mode 100644 clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilders.h
diff --git a/clang/unittests/Analysis/Scalable/CMakeLists.txt b/clang/unittests/Analysis/Scalable/CMakeLists.txt
index fbeefea15b421..41e99dcca5fd2 100644
--- a/clang/unittests/Analysis/Scalable/CMakeLists.txt
+++ b/clang/unittests/Analysis/Scalable/CMakeLists.txt
@@ -20,8 +20,6 @@ add_distinct_clang_unittest(ClangScalableAnalysisTests
Serialization/JSONFormatTest/TUSummaryTest.cpp
SummaryNameTest.cpp
SummaryView/LUSummaryConsumerTest.cpp
- SummaryView/MockSummaryViewBuilder1.cpp
- SummaryView/MockSummaryViewBuilder2.cpp
SummaryView/SummaryViewBuilderRegistryTest.cpp
TestFixture.cpp
TUSummaryBuilderTest.cpp
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/LUSummaryConsumerTest.cpp b/clang/unittests/Analysis/Scalable/SummaryView/LUSummaryConsumerTest.cpp
index bab8730ec3343..3499d5bda1847 100644
--- a/clang/unittests/Analysis/Scalable/SummaryView/LUSummaryConsumerTest.cpp
+++ b/clang/unittests/Analysis/Scalable/SummaryView/LUSummaryConsumerTest.cpp
@@ -8,208 +8,272 @@
#include "clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h"
#include "../TestFixture.h"
-#include "MockSummaryViewBuilders.h"
#include "clang/Analysis/Scalable/EntityLinker/LUSummary.h"
#include "clang/Analysis/Scalable/Model/BuildNamespace.h"
#include "clang/Analysis/Scalable/Model/EntityId.h"
#include "clang/Analysis/Scalable/Model/EntityIdTable.h"
+#include "clang/Analysis/Scalable/Model/EntityLinkage.h"
#include "clang/Analysis/Scalable/Model/EntityName.h"
#include "clang/Analysis/Scalable/Model/SummaryName.h"
+#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
+#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
#include "gtest/gtest.h"
+#include <algorithm>
#include <memory>
+#include <utility>
+#include <vector>
using namespace clang;
using namespace ssaf;
namespace {
-class LUSummaryConsumerTest : public TestFixture {
-protected:
- void SetUp() override { clearMockBuilderLog(); }
+// ---------------------------------------------------------------------------
+// Instance counter
+// ---------------------------------------------------------------------------
- std::unique_ptr<LUSummary> makeLUSummary() {
- NestedBuildNamespace NS(
- {BuildNamespace(BuildNamespaceKind::LinkUnit, "TestLU")});
- return std::make_unique<LUSummary>(std::move(NS));
+static int NextSummaryInstanceId = 0;
+
+// ---------------------------------------------------------------------------
+// Entity summaries
+// ---------------------------------------------------------------------------
+
+class Analysis1EntitySummary : public EntitySummary {
+public:
+ int InstanceId = NextSummaryInstanceId++;
+ SummaryName getSummaryName() const override {
+ return SummaryName("Analysis1");
}
+};
- /// Add an entity to the LUSummary's id table and return its EntityId.
- EntityId addEntity(LUSummary &LU, llvm::StringRef USR) {
- NestedBuildNamespace NS(
- {BuildNamespace(BuildNamespaceKind::LinkUnit, "TestLU")});
- EntityName Name(USR.str(), "", NS);
- return getIdTable(LU).getId(Name);
+class Analysis2EntitySummary : public EntitySummary {
+public:
+ int InstanceId = NextSummaryInstanceId++;
+ SummaryName getSummaryName() const override {
+ return SummaryName("Analysis2");
}
+};
- /// Insert a pre-built EntitySummary into LUSummary::Data.
- void insertSummary(LUSummary &LU, llvm::StringRef SummaryNameStr, EntityId Id,
- std::unique_ptr<EntitySummary> Summary) {
- getData(LU)[SummaryName(SummaryNameStr.str())][Id] = std::move(Summary);
+class Analysis4EntitySummary : public EntitySummary {
+public:
+ int InstanceId = NextSummaryInstanceId++;
+ SummaryName getSummaryName() const override {
+ return SummaryName("Analysis4");
}
};
// ---------------------------------------------------------------------------
-// No matching data in LUSummary
+// Views
// ---------------------------------------------------------------------------
-TEST_F(LUSummaryConsumerTest, NoMatchingData_AddSummaryNeverCalled) {
- auto LU = makeLUSummary();
- // Insert data for a summary name that has no registered builder.
- auto Id = addEntity(*LU, "A");
- insertSummary(*LU, "UnregisteredAnalysis", Id,
- std::make_unique<MockEntitySummary1>());
+class Analysis1View : public SummaryView {
+public:
+ static SummaryName summaryName() { return SummaryName("Analysis1"); }
+ std::vector<std::pair<EntityId, int>> Entries;
+ bool WasFinalized = false;
+};
- LUSummaryConsumer Consumer(std::move(LU));
- Consumer.run();
+class Analysis2View : public SummaryView {
+public:
+ static SummaryName summaryName() { return SummaryName("Analysis2"); }
+ std::vector<std::pair<EntityId, int>> Entries;
+ bool WasFinalized = false;
+};
- EXPECT_EQ(MockBuilderLog.find("addSummary"), std::string::npos);
-}
+// No builder or registration for Analysis3. Data for Analysis3 is inserted
+// into the LUSummary to verify the consumer silently skips it.
+class Analysis3View : public SummaryView {
+public:
+ static SummaryName summaryName() { return SummaryName("Analysis3"); }
+};
-TEST_F(LUSummaryConsumerTest, NoMatchingData_FinalizeNotCalled) {
- auto LU = makeLUSummary(); // empty — no Mock1 data
+// Analysis4 has a registered builder but no data is inserted into the
+// LUSummary, so the builder is never invoked and getView returns nullptr.
+class Analysis4View : public SummaryView {
+public:
+ static SummaryName summaryName() { return SummaryName("Analysis4"); }
+};
- LUSummaryConsumer Consumer(std::move(LU));
- Consumer.run();
+// ---------------------------------------------------------------------------
+// Builder destruction flags (reset in SetUp)
+// ---------------------------------------------------------------------------
- EXPECT_EQ(MockBuilderLog.find("finalize"), std::string::npos);
-}
+static bool Analysis1BuilderWasDestroyed = false;
+static bool Analysis2BuilderWasDestroyed = false;
+static bool Analysis4BuilderWasDestroyed = false;
-TEST_F(LUSummaryConsumerTest, NoMatchingData_GetViewReturnsNull) {
- auto LU = makeLUSummary();
+// ---------------------------------------------------------------------------
+// Builders
+// ---------------------------------------------------------------------------
- LUSummaryConsumer Consumer(std::move(LU));
- Consumer.run();
+class Analysis1Builder
+ : public SummaryViewBuilder<Analysis1View, Analysis1EntitySummary> {
+public:
+ ~Analysis1Builder() { Analysis1BuilderWasDestroyed = true; }
- EXPECT_EQ(Consumer.getView<MockView1>(), nullptr);
-}
+ void addSummary(EntityId Id,
+ std::unique_ptr<Analysis1EntitySummary> S) override {
+ getView().Entries.push_back({Id, S->InstanceId});
+ }
-// ---------------------------------------------------------------------------
-// Matching data delivered correctly
-// ---------------------------------------------------------------------------
+ void finalize() override { getView().WasFinalized = true; }
+};
-TEST_F(LUSummaryConsumerTest, MatchingData_AddSummaryCalledPerEntity) {
- auto LU = makeLUSummary();
- auto Id1 = addEntity(*LU, "A");
- auto Id2 = addEntity(*LU, "B");
- insertSummary(*LU, "Mock1", Id1, std::make_unique<MockEntitySummary1>());
- insertSummary(*LU, "Mock1", Id2, std::make_unique<MockEntitySummary1>());
+static SummaryViewBuilderRegistry::Add<Analysis1Builder>
+ RegAnalysis1("Analysis1", "Builder for Analysis1");
- LUSummaryConsumer Consumer(std::move(LU));
- Consumer.run();
+class Analysis2Builder
+ : public SummaryViewBuilder<Analysis2View, Analysis2EntitySummary> {
+public:
+ ~Analysis2Builder() { Analysis2BuilderWasDestroyed = true; }
- auto View = Consumer.getView<MockView1>();
- ASSERT_NE(View, nullptr);
- EXPECT_EQ(View->Ids.size(), 2u);
- EXPECT_NE(std::find(View->Ids.begin(), View->Ids.end(), Id1),
- View->Ids.end());
- EXPECT_NE(std::find(View->Ids.begin(), View->Ids.end(), Id2),
- View->Ids.end());
-}
+ void addSummary(EntityId Id,
+ std::unique_ptr<Analysis2EntitySummary> S) override {
+ getView().Entries.push_back({Id, S->InstanceId});
+ }
-TEST_F(LUSummaryConsumerTest, MatchingData_FinalizeCalledAfterAllAddSummary) {
- auto LU = makeLUSummary();
- auto Id = addEntity(*LU, "A");
- insertSummary(*LU, "Mock1", Id, std::make_unique<MockEntitySummary1>());
+ void finalize() override { getView().WasFinalized = true; }
+};
- LUSummaryConsumer Consumer(std::move(LU));
- Consumer.run();
+static SummaryViewBuilderRegistry::Add<Analysis2Builder>
+ RegAnalysis2("Analysis2", "Builder for Analysis2");
- // Verify ordering in the log: all addSummary entries must precede all
- // finalize entries.
- auto AddSummaryPos = MockBuilderLog.find("addSummary");
- auto FinalizePos = MockBuilderLog.find("finalize");
- ASSERT_NE(AddSummaryPos, std::string::npos);
- ASSERT_NE(FinalizePos, std::string::npos);
- EXPECT_LT(AddSummaryPos, FinalizePos);
-}
+class Analysis4Builder
+ : public SummaryViewBuilder<Analysis4View, Analysis4EntitySummary> {
+public:
+ ~Analysis4Builder() { Analysis4BuilderWasDestroyed = true; }
+
+ void addSummary(EntityId Id,
+ std::unique_ptr<Analysis4EntitySummary> S) override {
+ getView().Entries.push_back({Id, S->InstanceId});
+ }
+
+ void finalize() override { getView().WasFinalized = true; }
+};
+
+static SummaryViewBuilderRegistry::Add<Analysis4Builder>
+ RegAnalysis4("Analysis4", "Builder for Analysis4");
// ---------------------------------------------------------------------------
-// Multiple builders receive only their own entities
+// Fixture
// ---------------------------------------------------------------------------
-TEST_F(LUSummaryConsumerTest, MultipleBuilders_IndependentEntities) {
- auto LU = makeLUSummary();
- auto Id1 = addEntity(*LU, "A");
- auto Id2 = addEntity(*LU, "B");
- insertSummary(*LU, "Mock1", Id1, std::make_unique<MockEntitySummary1>());
- insertSummary(*LU, "Mock2", Id2, std::make_unique<MockEntitySummary2>());
+class LUSummaryConsumerTest : public TestFixture {
+protected:
+ static constexpr EntityLinkage ExternalLinkage =
+ EntityLinkage(EntityLinkageType::External);
+
+ void SetUp() override {
+ NextSummaryInstanceId = 0;
+ Analysis1BuilderWasDestroyed = false;
+ Analysis2BuilderWasDestroyed = false;
+ Analysis4BuilderWasDestroyed = false;
+ }
- LUSummaryConsumer Consumer(std::move(LU));
- Consumer.run();
+ std::unique_ptr<LUSummary> makeLUSummary() {
+ NestedBuildNamespace NS(
+ {BuildNamespace(BuildNamespaceKind::LinkUnit, "TestLU")});
+ return std::make_unique<LUSummary>(std::move(NS));
+ }
- auto View1 = Consumer.getView<MockView1>();
- ASSERT_NE(View1, nullptr);
- ASSERT_EQ(View1->Ids.size(), 1u);
- EXPECT_EQ(View1->Ids[0], Id1);
+ EntityId addEntity(LUSummary &LU, llvm::StringRef USR) {
+ NestedBuildNamespace NS(
+ {BuildNamespace(BuildNamespaceKind::LinkUnit, "TestLU")});
+ EntityName Name(USR.str(), "", NS);
+ EntityId Id = getIdTable(LU).getId(Name);
+ getLinkageTable(LU).insert({Id, ExternalLinkage});
+ return Id;
+ }
- auto View2 = Consumer.getView<MockView2>();
- ASSERT_NE(View2, nullptr);
- ASSERT_EQ(View2->Ids.size(), 1u);
- EXPECT_EQ(View2->Ids[0], Id2);
-}
+ static bool hasEntry(const std::vector<std::pair<EntityId, int>> &Entries,
+ EntityId Id, int InstanceId) {
+ return std::find(Entries.begin(), Entries.end(),
+ std::make_pair(Id, InstanceId)) != Entries.end();
+ }
+
+ /// Inserts a freshly constructed SummaryT for the given entity and returns
+ /// the summary's InstanceId so the test can verify delivery later.
+ template <typename SummaryT>
+ int insertSummary(LUSummary &LU, llvm::StringRef SN, EntityId Id) {
+ auto S = std::make_unique<SummaryT>();
+ int InstanceId = S->InstanceId;
+ getData(LU)[SummaryName(SN.str())][Id] = std::move(S);
+ return InstanceId;
+ }
+};
// ---------------------------------------------------------------------------
-// getView ownership transfer
+// Tests
// ---------------------------------------------------------------------------
-TEST_F(LUSummaryConsumerTest, GetView_FirstCallReturnsNonNull) {
+TEST_F(LUSummaryConsumerTest, Run) {
auto LU = makeLUSummary();
- auto Id = addEntity(*LU, "A");
- insertSummary(*LU, "Mock1", Id, std::make_unique<MockEntitySummary1>());
+ const auto E1 = addEntity(*LU, "Entity1");
+ const auto E2 = addEntity(*LU, "Entity2");
+ const auto E3 = addEntity(*LU, "Entity3");
+
+ int s1a = insertSummary<Analysis1EntitySummary>(*LU, "Analysis1", E1);
+ int s1b = insertSummary<Analysis1EntitySummary>(*LU, "Analysis1", E2);
+ int s2a = insertSummary<Analysis2EntitySummary>(*LU, "Analysis2", E2);
+ int s2b = insertSummary<Analysis2EntitySummary>(*LU, "Analysis2", E3);
+
+ // no registered builder
+ [[maybe_unused]] int s3a =
+ insertSummary<Analysis1EntitySummary>(*LU, "Analysis3", E1);
LUSummaryConsumer Consumer(std::move(LU));
Consumer.run();
- EXPECT_NE(Consumer.getView<MockView1>(), nullptr);
-}
+ // Analysis1
+ {
+ auto View1 = Consumer.getView<Analysis1View>();
+ ASSERT_NE(View1, nullptr);
-TEST_F(LUSummaryConsumerTest, GetView_SecondCallReturnsNull) {
- auto LU = makeLUSummary();
- auto Id = addEntity(*LU, "A");
- insertSummary(*LU, "Mock1", Id, std::make_unique<MockEntitySummary1>());
+ // getView ownership transfer — second call returns nullptr
+ EXPECT_EQ(Consumer.getView<Analysis1View>(), nullptr);
- LUSummaryConsumer Consumer(std::move(LU));
- Consumer.run();
+ EXPECT_EQ(View1->Entries.size(), 2u);
+ EXPECT_TRUE(hasEntry(View1->Entries, E1, s1a));
+ EXPECT_TRUE(hasEntry(View1->Entries, E2, s1b));
- auto First = Consumer.getView<MockView1>();
- EXPECT_NE(First, nullptr);
- EXPECT_EQ(Consumer.getView<MockView1>(), nullptr);
-}
+ EXPECT_TRUE(View1->WasFinalized);
-TEST_F(LUSummaryConsumerTest, GetView_UnregisteredViewReturnsNull) {
- // MockView1 is registered; a hypothetical OtherView is not.
- struct OtherView : public SummaryView {
- static SummaryName summaryName() { return SummaryName("Other"); }
- };
+ // Builder lifetime
+ EXPECT_TRUE(Analysis1BuilderWasDestroyed);
+ }
- auto LU = makeLUSummary();
- LUSummaryConsumer Consumer(std::move(LU));
- Consumer.run();
+ // Analysis2
+ {
+ auto View2 = Consumer.getView<Analysis2View>();
+ ASSERT_NE(View2, nullptr);
- EXPECT_EQ(Consumer.getView<OtherView>(), nullptr);
-}
+ // getView ownership transfer — second call returns nullptr
+ EXPECT_EQ(Consumer.getView<Analysis2View>(), nullptr);
-// ---------------------------------------------------------------------------
-// Builder lifetime
-// ---------------------------------------------------------------------------
+ EXPECT_EQ(View2->Entries.size(), 2u);
+ EXPECT_TRUE(hasEntry(View2->Entries, E2, s2a));
+ EXPECT_TRUE(hasEntry(View2->Entries, E3, s2b));
-TEST_F(LUSummaryConsumerTest, BuildersDestroyedAfterRun) {
- auto LU = makeLUSummary();
- auto Id1 = addEntity(*LU, "A");
- auto Id2 = addEntity(*LU, "B");
- insertSummary(*LU, "Mock1", Id1, std::make_unique<MockEntitySummary1>());
- insertSummary(*LU, "Mock2", Id2, std::make_unique<MockEntitySummary2>());
+ EXPECT_TRUE(View2->WasFinalized);
- LUSummaryConsumer Consumer(std::move(LU));
- Consumer.run();
+ // Builder lifetime
+ EXPECT_TRUE(Analysis2BuilderWasDestroyed);
+ }
+
+ // Analysis 3
+ {
+ // Unregistered builder — Analysis3 data silently skipped
+ EXPECT_EQ(Consumer.getView<Analysis3View>(), nullptr);
+ }
+
+ // Analysis4
+ {
+ // Registered builder but no data in LUSummary — builder never invoked
+ EXPECT_EQ(Consumer.getView<Analysis4View>(), nullptr);
- // Both destructors must have been called by the time run() returns.
- EXPECT_NE(
- MockBuilderLog.find("MockSummaryViewBuilder1 destructor was invoked"),
- std::string::npos);
- EXPECT_NE(
- MockBuilderLog.find("MockSummaryViewBuilder2 destructor was invoked"),
- std::string::npos);
+ // Builder lifetime
+ EXPECT_FALSE(Analysis4BuilderWasDestroyed);
+ }
}
} // namespace
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder1.cpp b/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder1.cpp
deleted file mode 100644
index 2bfc526969393..0000000000000
--- a/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder1.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-//===- MockSummaryViewBuilder1.cpp ----------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "MockSummaryViewBuilders.h"
-#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
-
-using namespace clang;
-using namespace ssaf;
-
-std::string clang::ssaf::MockBuilderLog;
-
-void clang::ssaf::clearMockBuilderLog() { MockBuilderLog.clear(); }
-
-namespace {
-
-class MockSummaryViewBuilder1
- : public SummaryViewBuilder<MockView1, MockEntitySummary1> {
-public:
- MockSummaryViewBuilder1() {
- MockBuilderLog += "MockSummaryViewBuilder1 constructor was invoked\n";
- }
-
- ~MockSummaryViewBuilder1() {
- MockBuilderLog += "MockSummaryViewBuilder1 destructor was invoked\n";
- }
-
- void addSummary(EntityId Id, std::unique_ptr<MockEntitySummary1>) override {
- MockBuilderLog += "MockSummaryViewBuilder1 addSummary was invoked\n";
- getView().Ids.push_back(Id);
- }
-
- void finalize() override {
- MockBuilderLog += "MockSummaryViewBuilder1 finalize was invoked\n";
- }
-};
-
-static SummaryViewBuilderRegistry::Add<MockSummaryViewBuilder1>
- Register("Mock1", "Mock view builder 1");
-
-} // namespace
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder2.cpp b/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder2.cpp
deleted file mode 100644
index b43e7f69bbb8e..0000000000000
--- a/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilder2.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-//===- MockSummaryViewBuilder2.cpp ----------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "MockSummaryViewBuilders.h"
-#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
-
-using namespace clang;
-using namespace ssaf;
-
-namespace {
-
-class MockSummaryViewBuilder2
- : public SummaryViewBuilder<MockView2, MockEntitySummary2> {
-public:
- MockSummaryViewBuilder2() {
- MockBuilderLog += "MockSummaryViewBuilder2 constructor was invoked\n";
- }
-
- ~MockSummaryViewBuilder2() {
- MockBuilderLog += "MockSummaryViewBuilder2 destructor was invoked\n";
- }
-
- void addSummary(EntityId Id, std::unique_ptr<MockEntitySummary2>) override {
- MockBuilderLog += "MockSummaryViewBuilder2 addSummary was invoked\n";
- getView().Ids.push_back(Id);
- }
-
- void finalize() override {
- MockBuilderLog += "MockSummaryViewBuilder2 finalize was invoked\n";
- }
-};
-
-static SummaryViewBuilderRegistry::Add<MockSummaryViewBuilder2>
- Register("Mock2", "Mock view builder 2");
-
-} // namespace
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilders.h b/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilders.h
deleted file mode 100644
index 8bdcb27ca93f6..0000000000000
--- a/clang/unittests/Analysis/Scalable/SummaryView/MockSummaryViewBuilders.h
+++ /dev/null
@@ -1,67 +0,0 @@
-//===- MockSummaryViewBuilders.h ------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-//
-// Shared mock types for SummaryView tests. Two mock analyses ("Mock1" and
-// "Mock2") are defined here, with their builders registered in
-// MockSummaryViewBuilder1.cpp and MockSummaryViewBuilder2.cpp.
-//
-// Tests observe builder behaviour via MockBuilderLog, which records lifecycle
-// events (constructor, addSummary, finalize, destructor) as newline-terminated
-// strings. Call clearMockBuilderLog() in SetUp() to isolate tests.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef CLANG_UNITTESTS_ANALYSIS_SCALABLE_SUMMARYVIEW_MOCKSUMMARYVIEWBUILDERS_H
-#define CLANG_UNITTESTS_ANALYSIS_SCALABLE_SUMMARYVIEW_MOCKSUMMARYVIEWBUILDERS_H
-
-#include "clang/Analysis/Scalable/Model/EntityId.h"
-#include "clang/Analysis/Scalable/Model/SummaryName.h"
-#include "clang/Analysis/Scalable/SummaryView/SummaryView.h"
-#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h"
-#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
-#include <string>
-#include <vector>
-
-namespace clang::ssaf {
-
-// ---- Mock entity summaries -----------------------------------------------
-
-class MockEntitySummary1 : public EntitySummary {
-public:
- SummaryName getSummaryName() const override { return SummaryName("Mock1"); }
-};
-
-class MockEntitySummary2 : public EntitySummary {
-public:
- SummaryName getSummaryName() const override { return SummaryName("Mock2"); }
-};
-
-// ---- Mock views ------------------------------------------------------------
-
-class MockView1 : public SummaryView {
-public:
- static SummaryName summaryName() { return SummaryName("Mock1"); }
- std::vector<EntityId> Ids;
-};
-
-class MockView2 : public SummaryView {
-public:
- static SummaryName summaryName() { return SummaryName("Mock2"); }
- std::vector<EntityId> Ids;
-};
-
-// ---- Shared log ------------------------------------------------------------
-// Defined in MockSummaryViewBuilder1.cpp; written by both mock builders.
-
-extern std::string MockBuilderLog;
-
-void clearMockBuilderLog();
-
-} // namespace clang::ssaf
-
-#endif // CLANG_UNITTESTS_ANALYSIS_SCALABLE_SUMMARYVIEW_MOCKSUMMARYVIEWBUILDERS_H
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp b/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp
index b48682500cd39..30d3606247b2e 100644
--- a/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp
+++ b/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp
@@ -7,9 +7,9 @@
//===----------------------------------------------------------------------===//
#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
-#include "MockSummaryViewBuilders.h"
#include "llvm/ADT/StringRef.h"
#include "gtest/gtest.h"
+#include <memory>
#include <set>
using namespace clang;
@@ -17,15 +17,13 @@ using namespace ssaf;
namespace {
-class SummaryViewBuilderRegistryTest : public ::testing::Test {
-protected:
- void SetUp() override { clearMockBuilderLog(); }
-};
+class SummaryViewBuilderRegistryTest : public ::testing::Test {};
TEST_F(SummaryViewBuilderRegistryTest, isSummaryViewBuilderRegistered) {
EXPECT_FALSE(isSummaryViewBuilderRegistered("Non-existent-builder"));
- EXPECT_TRUE(isSummaryViewBuilderRegistered("Mock1"));
- EXPECT_TRUE(isSummaryViewBuilderRegistered("Mock2"));
+ EXPECT_TRUE(isSummaryViewBuilderRegistered("Analysis1"));
+ EXPECT_TRUE(isSummaryViewBuilderRegistered("Analysis2"));
+ EXPECT_TRUE(isSummaryViewBuilderRegistered("Analysis4"));
}
TEST_F(SummaryViewBuilderRegistryTest, EnumeratingRegistryEntries) {
@@ -35,48 +33,30 @@ TEST_F(SummaryViewBuilderRegistryTest, EnumeratingRegistryEntries) {
EXPECT_TRUE(Inserted);
}
- EXPECT_EQ(ActualNames, (std::set<llvm::StringRef>{"Mock1", "Mock2"}));
+ EXPECT_EQ(ActualNames,
+ (std::set<llvm::StringRef>{"Analysis1", "Analysis2", "Analysis4"}));
}
-TEST_F(SummaryViewBuilderRegistryTest, InstantiatingBuilder1) {
- {
- // Find Mock1 entry explicitly.
- std::unique_ptr<SummaryViewBuilderBase> B1;
- for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
- if (Entry.getName() == "Mock1") {
- B1 = Entry.instantiate();
- }
- }
-
- ASSERT_TRUE(B1);
- EXPECT_EQ(B1->summaryName(), SummaryName("Mock1"));
- EXPECT_NE(
- MockBuilderLog.find("MockSummaryViewBuilder1 constructor was invoked"),
- std::string::npos);
+TEST_F(SummaryViewBuilderRegistryTest, InstantiatingBuilder_Analysis1) {
+ std::unique_ptr<SummaryViewBuilderBase> B;
+ for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
+ if (Entry.getName() == "Analysis1")
+ B = Entry.instantiate();
}
- EXPECT_NE(
- MockBuilderLog.find("MockSummaryViewBuilder1 destructor was invoked"),
- std::string::npos);
-}
-TEST_F(SummaryViewBuilderRegistryTest, InstantiatingBuilder2) {
- {
- std::unique_ptr<SummaryViewBuilderBase> B2;
- for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
- if (Entry.getName() == "Mock2") {
- B2 = Entry.instantiate();
- }
- }
+ ASSERT_NE(B, nullptr);
+ EXPECT_EQ(B->summaryName(), SummaryName("Analysis1"));
+}
- ASSERT_TRUE(B2);
- EXPECT_EQ(B2->summaryName(), SummaryName("Mock2"));
- EXPECT_NE(
- MockBuilderLog.find("MockSummaryViewBuilder2 constructor was invoked"),
- std::string::npos);
+TEST_F(SummaryViewBuilderRegistryTest, InstantiatingBuilder_Analysis2) {
+ std::unique_ptr<SummaryViewBuilderBase> B;
+ for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
+ if (Entry.getName() == "Analysis2")
+ B = Entry.instantiate();
}
- EXPECT_NE(
- MockBuilderLog.find("MockSummaryViewBuilder2 destructor was invoked"),
- std::string::npos);
+
+ ASSERT_NE(B, nullptr);
+ EXPECT_EQ(B->summaryName(), SummaryName("Analysis2"));
}
} // namespace
>From c6f2af8315d60bdb5adbcdabe0f9f088fadba648 Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Wed, 11 Mar 2026 15:57:27 -0700
Subject: [PATCH 4/4] Fix
---
.../Scalable/SummaryView/LUSummaryConsumer.h | 33 +--
.../Scalable/SummaryView/SummaryView.h | 3 -
.../Scalable/SummaryView/SummaryViewBuilder.h | 58 ++--
.../SummaryView/SummaryViewBuilderRegistry.h | 50 ++--
.../Scalable/SummaryView/SummaryViewTraits.h | 33 +++
.../SummaryView/LUSummaryConsumer.cpp | 15 +-
.../SummaryViewBuilderRegistry.cpp | 17 +-
.../Analysis/Scalable/CMakeLists.txt | 3 +-
.../SummaryView/LUSummaryConsumerTest.cpp | 279 ------------------
.../SummaryViewBuilderRegistryTest.cpp | 62 ----
10 files changed, 115 insertions(+), 438 deletions(-)
create mode 100644 clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewTraits.h
delete mode 100644 clang/unittests/Analysis/Scalable/SummaryView/LUSummaryConsumerTest.cpp
delete mode 100644 clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h b/clang/include/clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h
index eb71b2add386d..a440f05999e0c 100644
--- a/clang/include/clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h
@@ -9,16 +9,6 @@
// LUSummaryConsumer constructs SummaryView objects by routing LUSummary entity
// data to the corresponding SummaryViewBuilder objects.
//
-// Typical usage:
-//
-// auto LU = Format->readLUSummary(Path);
-//
-// LUSummaryConsumer Consumer(std::move(LU));
-// Consumer.run();
-//
-// auto View = Consumer.getView<MyView>();
-// // View is std::unique_ptr<MyView>; Consumer no longer holds it.
-//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_LUSUMMARYCONSUMER_H
@@ -28,6 +18,7 @@
#include "clang/Analysis/Scalable/Model/EntityId.h"
#include "clang/Analysis/Scalable/Model/SummaryName.h"
#include "clang/Analysis/Scalable/SummaryView/SummaryView.h"
+#include "clang/Analysis/Scalable/SummaryView/SummaryViewTraits.h"
#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
#include <map>
#include <memory>
@@ -37,18 +28,14 @@ namespace clang::ssaf {
/// Consumes a LUSummary by dispatching its entity data to all registered
/// SummaryViewBuilders and collecting the resulting views.
class LUSummaryConsumer final {
- std::unique_ptr<LUSummary> LU;
- std::map<SummaryName, std::unique_ptr<SummaryView>> Views;
- bool WasRun = false;
-
public:
explicit LUSummaryConsumer(std::unique_ptr<LUSummary> LU)
: LU(std::move(LU)) {}
/// Instantiates a builder for each SummaryName present in the LUSummary,
- /// delivers its entities, finalizes it, and stores the resulting view.
- /// Each builder is fully processed (addSummary → finalize → getView) before
- /// the next SummaryName is visited. Builders are discarded on return.
+ /// delivers its entities, finalizes it, and stores the resulting view. Each
+ /// builder is fully processed before the next SummaryName is visited.
+ /// Builders are discarded on return.
///
/// \pre Must be called exactly once.
void run();
@@ -58,6 +45,11 @@ class LUSummaryConsumer final {
/// Returns nullptr if no builder for \p ViewT was registered or run() has
/// not been called. A second call for the same ViewT also returns nullptr.
template <typename ViewT> [[nodiscard]] std::unique_ptr<ViewT> getView() {
+ static_assert(std::is_base_of_v<SummaryView, ViewT>,
+ "ViewT must derive from SummaryView");
+ static_assert(HasSummaryName<ViewT>::value,
+ "ViewT must have a static summaryName() method");
+
auto It = Views.find(ViewT::summaryName());
if (It == Views.end()) {
return nullptr;
@@ -70,9 +62,10 @@ class LUSummaryConsumer final {
private:
using EntityDataMap = std::map<EntityId, std::unique_ptr<EntitySummary>>;
- /// Processes all entities for a single SummaryName: instantiates the
- /// registered builder, delivers entities, finalizes, and stores the view.
- /// Does nothing if no builder is registered for \p SN.
+ std::unique_ptr<LUSummary> LU;
+ std::map<SummaryName, std::unique_ptr<SummaryView>> Views;
+ bool WasRun = false;
+
void run(const SummaryName &SN, EntityDataMap &Data);
};
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h
index 46778e94976f3..dfbdab4814ac7 100644
--- a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryView.h
@@ -18,9 +18,6 @@
namespace clang::ssaf {
/// Abstract base class for whole-program analysis views.
-///
-/// Concrete view classes inherit from this and add a static
-/// \c summaryName() method along with their analysis-specific query API.
class SummaryView {
public:
virtual ~SummaryView() = default;
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h
index c71652662f4d9..15c40236c0b99 100644
--- a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h
@@ -10,44 +10,33 @@
// registry and LUSummaryConsumer) and the typed intermediate template
// SummaryViewBuilder<ViewT, SummaryT> that concrete builders inherit from.
//
-// To implement a view builder, inherit from SummaryViewBuilder:
-//
-// class MyViewBuilder
-// : public SummaryViewBuilder<MyView, MyEntitySummary> {
-// public:
-// void addSummary(EntityId Id,
-// std::unique_ptr<MyEntitySummary> Summary) override {
-// // accumulate into getView()
-// }
-// // summaryName() and getView() && provided by the intermediate.
-// // override finalize() if post-processing is needed.
-// };
-//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDER_H
#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDER_H
#include "clang/Analysis/Scalable/Model/EntityId.h"
-#include "clang/Analysis/Scalable/Model/SummaryName.h"
#include "clang/Analysis/Scalable/SummaryView/SummaryView.h"
+#include "clang/Analysis/Scalable/SummaryView/SummaryViewTraits.h"
#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
#include <memory>
namespace clang::ssaf {
+class LUSummaryConsumer;
+
/// Abstract base class for all summary view builders.
///
/// Known to the registry and LUSummaryConsumer. Receives entities one at a
/// time via \c addSummary(), is finalized via \c finalize(), and transfers
-/// ownership of the built view via \c getView() &&.
+/// ownership of the built view via \c getView().
class SummaryViewBuilderBase {
+ friend class LUSummaryConsumer;
+
public:
virtual ~SummaryViewBuilderBase() = default;
- /// Returns the SummaryName this builder handles.
- virtual SummaryName summaryName() const = 0;
-
+private:
/// Called once per entity belonging to this builder's analysis.
/// Takes ownership of the summary data.
virtual void addSummary(EntityId Id,
@@ -56,41 +45,40 @@ class SummaryViewBuilderBase {
/// Called after all entities have been added.
virtual void finalize() {}
- /// Transfers ownership of the built view (type-erased).
- /// Called by LUSummaryConsumer after finalize(). The rvalue ref-qualifier
- /// enforces single use — the builder cannot be accessed after this call.
+ /// Transfers ownership of the built view. Called by LUSummaryConsumer after
+ /// finalize(). The rvalue ref-qualifier enforces single use — the builder
+ /// cannot be accessed after this call.
virtual std::unique_ptr<SummaryView> getView() && = 0;
};
/// Typed intermediate template that concrete builders inherit from.
-///
-/// Provides:
-/// - \c summaryName() derived from \c ViewT::summaryName().
-/// - \c getView() && which moves out the built view.
-/// - A protected \c getView() accessor for use during construction.
-/// - NVI dispatch: seals the base \c addSummary(EntityId, EntitySummary) as
-/// \c final, downcasts, and dispatches to the typed overload.
-///
/// Concrete builders only need to implement the typed
/// \c addSummary(EntityId, unique_ptr<SummaryT>) overload.
template <typename ViewT, typename SummaryT>
class SummaryViewBuilder : public SummaryViewBuilderBase {
- std::unique_ptr<ViewT> View;
+ static_assert(std::is_base_of_v<SummaryView, ViewT>,
+ "ViewT must derive from SummaryView");
+ static_assert(HasSummaryName<ViewT>::value,
+ "ViewT must have a static summaryName() method");
-protected:
- ViewT &getView() & { return *View; }
+ std::unique_ptr<ViewT> View;
public:
SummaryViewBuilder() : View(std::make_unique<ViewT>()) {}
- SummaryName summaryName() const override { return ViewT::summaryName(); }
-
- std::unique_ptr<SummaryView> getView() && override { return std::move(View); }
+ /// Returns the SummaryName of the view this builder produces.
+ /// Used by SummaryViewBuilderRegistry::Add to derive the registry entry name.
+ static SummaryName summaryName() { return ViewT::summaryName(); }
/// Typed customization point — concrete builders override this.
virtual void addSummary(EntityId Id, std::unique_ptr<SummaryT> Summary) = 0;
+protected:
+ ViewT &getView() & { return *View; }
+
private:
+ std::unique_ptr<SummaryView> getView() && override { return std::move(View); }
+
/// Seals the base overload, downcasts, and dispatches to the typed overload.
void addSummary(EntityId Id, std::unique_ptr<EntitySummary> Summary) final {
addSummary(Id, std::unique_ptr<SummaryT>(
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h
index d3fc08a71a446..1cfe6fa730eec 100644
--- a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h
@@ -6,14 +6,7 @@
//
//===----------------------------------------------------------------------===//
//
-// Registry for SummaryViewBuilders and helper functions.
-//
-// To register a builder, add this to the builder's translation unit:
-//
-// static SummaryViewBuilderRegistry::Add<MyViewBuilder>
-// Register("MyAnalysis", "View builder for MyAnalysis");
-//
-// The registry entry name must match MyView::summaryName().
+// Registry for SummaryViewBuilders.
//
//===----------------------------------------------------------------------===//
@@ -21,23 +14,40 @@
#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDERREGISTRY_H
#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilder.h"
-#include "clang/Support/Compiler.h"
-#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Registry.h"
+#include <memory>
namespace clang::ssaf {
-/// Check if a SummaryViewBuilder was registered with a given name.
-bool isSummaryViewBuilderRegistered(llvm::StringRef Name);
-
-/// Registry for adding new SummaryViewBuilder implementations.
-using SummaryViewBuilderRegistry = llvm::Registry<SummaryViewBuilderBase>;
+/// Registry for SummaryViewBuilder implementations.
+///
+/// Provides an Add helper that derives the registry entry name from
+/// BuilderT::summaryName(), eliminating the possibility of registering a
+/// builder under the wrong name.
+class SummaryViewBuilderRegistry {
+ using RegistryT = llvm::Registry<SummaryViewBuilderBase>;
+
+public:
+ /// Registers \p BuilderT under the name returned by
+ /// \c BuilderT::summaryName(). Only a description is required.
+ template <typename BuilderT> struct Add {
+ explicit Add(llvm::StringRef Desc)
+ : Name(BuilderT::summaryName().str().str()), Node(Name, Desc) {}
+
+ private:
+ std::string Name;
+ RegistryT::Add<BuilderT> Node;
+ };
+
+ /// Returns true if a builder is registered under \p Name.
+ static bool isRegistered(llvm::StringRef Name);
+
+ /// Instantiates the builder registered under \p Name, or returns nullptr
+ /// if no such builder is registered.
+ static std::unique_ptr<SummaryViewBuilderBase>
+ instantiate(llvm::StringRef Name);
+};
} // namespace clang::ssaf
-namespace llvm {
-extern template class CLANG_TEMPLATE_ABI
- Registry<clang::ssaf::SummaryViewBuilderBase>;
-} // namespace llvm
-
#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWBUILDERREGISTRY_H
diff --git a/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewTraits.h b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewTraits.h
new file mode 100644
index 0000000000000..542714b8b21a7
--- /dev/null
+++ b/clang/include/clang/Analysis/Scalable/SummaryView/SummaryViewTraits.h
@@ -0,0 +1,33 @@
+//===- SummaryViewTraits.h ------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Type traits for SummaryView subclasses.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWTRAITS_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWTRAITS_H
+
+#include "clang/Analysis/Scalable/Model/SummaryName.h"
+#include <type_traits>
+
+namespace clang::ssaf {
+
+/// Type trait that checks whether \p T has a static summaryName() method
+/// returning SummaryName. Used to enforce the convention on SummaryView
+/// subclasses at instantiation time.
+template <typename T, typename = void>
+struct HasSummaryName : std::false_type {};
+
+template <typename T>
+struct HasSummaryName<T, std::void_t<decltype(T::summaryName())>>
+ : std::is_same<decltype(T::summaryName()), SummaryName> {};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYVIEW_SUMMARYVIEWTRAITS_H
diff --git a/clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp b/clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp
index a3db29be840d6..c742879c9eb93 100644
--- a/clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp
+++ b/clang/lib/Analysis/Scalable/SummaryView/LUSummaryConsumer.cpp
@@ -14,25 +14,12 @@
using namespace clang;
using namespace ssaf;
-static std::unique_ptr<SummaryViewBuilderBase>
-instantiateBuilder(const SummaryName &SN) {
- for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
- if (Entry.getName() == SN.str()) {
- return Entry.instantiate();
- }
- }
- return nullptr;
-}
-
void LUSummaryConsumer::run(const SummaryName &SN, EntityDataMap &Data) {
- auto Builder = instantiateBuilder(SN);
+ auto Builder = SummaryViewBuilderRegistry::instantiate(SN.str());
if (!Builder) {
return;
}
- assert(Builder->summaryName() == SN &&
- "registry entry name must match SummaryViewBuilder::summaryName()");
-
for (auto &[Id, Summary] : Data) {
Builder->addSummary(Id, std::move(Summary));
}
diff --git a/clang/lib/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.cpp b/clang/lib/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.cpp
index eb64394ba4401..7d7ef13a70c01 100644
--- a/clang/lib/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.cpp
+++ b/clang/lib/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.cpp
@@ -11,10 +11,21 @@
using namespace clang;
using namespace ssaf;
-LLVM_INSTANTIATE_REGISTRY(SummaryViewBuilderRegistry)
+using RegistryT = llvm::Registry<SummaryViewBuilderBase>;
+LLVM_INSTANTIATE_REGISTRY(RegistryT)
-bool ssaf::isSummaryViewBuilderRegistered(llvm::StringRef Name) {
- for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
+std::unique_ptr<SummaryViewBuilderBase>
+SummaryViewBuilderRegistry::instantiate(llvm::StringRef Name) {
+ for (const auto &Entry : RegistryT::entries()) {
+ if (Entry.getName() == Name) {
+ return Entry.instantiate();
+ }
+ }
+ return nullptr;
+}
+
+bool SummaryViewBuilderRegistry::isRegistered(llvm::StringRef Name) {
+ for (const auto &Entry : RegistryT::entries()) {
if (Entry.getName() == Name) {
return true;
}
diff --git a/clang/unittests/Analysis/Scalable/CMakeLists.txt b/clang/unittests/Analysis/Scalable/CMakeLists.txt
index 41e99dcca5fd2..7af03a9aec1c2 100644
--- a/clang/unittests/Analysis/Scalable/CMakeLists.txt
+++ b/clang/unittests/Analysis/Scalable/CMakeLists.txt
@@ -19,8 +19,7 @@ add_distinct_clang_unittest(ClangScalableAnalysisTests
Serialization/JSONFormatTest/LUSummaryTest.cpp
Serialization/JSONFormatTest/TUSummaryTest.cpp
SummaryNameTest.cpp
- SummaryView/LUSummaryConsumerTest.cpp
- SummaryView/SummaryViewBuilderRegistryTest.cpp
+ SummaryView/SummaryViewTest.cpp
TestFixture.cpp
TUSummaryBuilderTest.cpp
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/LUSummaryConsumerTest.cpp b/clang/unittests/Analysis/Scalable/SummaryView/LUSummaryConsumerTest.cpp
deleted file mode 100644
index 3499d5bda1847..0000000000000
--- a/clang/unittests/Analysis/Scalable/SummaryView/LUSummaryConsumerTest.cpp
+++ /dev/null
@@ -1,279 +0,0 @@
-//===- LUSummaryConsumerTest.cpp ------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "clang/Analysis/Scalable/SummaryView/LUSummaryConsumer.h"
-#include "../TestFixture.h"
-#include "clang/Analysis/Scalable/EntityLinker/LUSummary.h"
-#include "clang/Analysis/Scalable/Model/BuildNamespace.h"
-#include "clang/Analysis/Scalable/Model/EntityId.h"
-#include "clang/Analysis/Scalable/Model/EntityIdTable.h"
-#include "clang/Analysis/Scalable/Model/EntityLinkage.h"
-#include "clang/Analysis/Scalable/Model/EntityName.h"
-#include "clang/Analysis/Scalable/Model/SummaryName.h"
-#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
-#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
-#include "gtest/gtest.h"
-#include <algorithm>
-#include <memory>
-#include <utility>
-#include <vector>
-
-using namespace clang;
-using namespace ssaf;
-
-namespace {
-
-// ---------------------------------------------------------------------------
-// Instance counter
-// ---------------------------------------------------------------------------
-
-static int NextSummaryInstanceId = 0;
-
-// ---------------------------------------------------------------------------
-// Entity summaries
-// ---------------------------------------------------------------------------
-
-class Analysis1EntitySummary : public EntitySummary {
-public:
- int InstanceId = NextSummaryInstanceId++;
- SummaryName getSummaryName() const override {
- return SummaryName("Analysis1");
- }
-};
-
-class Analysis2EntitySummary : public EntitySummary {
-public:
- int InstanceId = NextSummaryInstanceId++;
- SummaryName getSummaryName() const override {
- return SummaryName("Analysis2");
- }
-};
-
-class Analysis4EntitySummary : public EntitySummary {
-public:
- int InstanceId = NextSummaryInstanceId++;
- SummaryName getSummaryName() const override {
- return SummaryName("Analysis4");
- }
-};
-
-// ---------------------------------------------------------------------------
-// Views
-// ---------------------------------------------------------------------------
-
-class Analysis1View : public SummaryView {
-public:
- static SummaryName summaryName() { return SummaryName("Analysis1"); }
- std::vector<std::pair<EntityId, int>> Entries;
- bool WasFinalized = false;
-};
-
-class Analysis2View : public SummaryView {
-public:
- static SummaryName summaryName() { return SummaryName("Analysis2"); }
- std::vector<std::pair<EntityId, int>> Entries;
- bool WasFinalized = false;
-};
-
-// No builder or registration for Analysis3. Data for Analysis3 is inserted
-// into the LUSummary to verify the consumer silently skips it.
-class Analysis3View : public SummaryView {
-public:
- static SummaryName summaryName() { return SummaryName("Analysis3"); }
-};
-
-// Analysis4 has a registered builder but no data is inserted into the
-// LUSummary, so the builder is never invoked and getView returns nullptr.
-class Analysis4View : public SummaryView {
-public:
- static SummaryName summaryName() { return SummaryName("Analysis4"); }
-};
-
-// ---------------------------------------------------------------------------
-// Builder destruction flags (reset in SetUp)
-// ---------------------------------------------------------------------------
-
-static bool Analysis1BuilderWasDestroyed = false;
-static bool Analysis2BuilderWasDestroyed = false;
-static bool Analysis4BuilderWasDestroyed = false;
-
-// ---------------------------------------------------------------------------
-// Builders
-// ---------------------------------------------------------------------------
-
-class Analysis1Builder
- : public SummaryViewBuilder<Analysis1View, Analysis1EntitySummary> {
-public:
- ~Analysis1Builder() { Analysis1BuilderWasDestroyed = true; }
-
- void addSummary(EntityId Id,
- std::unique_ptr<Analysis1EntitySummary> S) override {
- getView().Entries.push_back({Id, S->InstanceId});
- }
-
- void finalize() override { getView().WasFinalized = true; }
-};
-
-static SummaryViewBuilderRegistry::Add<Analysis1Builder>
- RegAnalysis1("Analysis1", "Builder for Analysis1");
-
-class Analysis2Builder
- : public SummaryViewBuilder<Analysis2View, Analysis2EntitySummary> {
-public:
- ~Analysis2Builder() { Analysis2BuilderWasDestroyed = true; }
-
- void addSummary(EntityId Id,
- std::unique_ptr<Analysis2EntitySummary> S) override {
- getView().Entries.push_back({Id, S->InstanceId});
- }
-
- void finalize() override { getView().WasFinalized = true; }
-};
-
-static SummaryViewBuilderRegistry::Add<Analysis2Builder>
- RegAnalysis2("Analysis2", "Builder for Analysis2");
-
-class Analysis4Builder
- : public SummaryViewBuilder<Analysis4View, Analysis4EntitySummary> {
-public:
- ~Analysis4Builder() { Analysis4BuilderWasDestroyed = true; }
-
- void addSummary(EntityId Id,
- std::unique_ptr<Analysis4EntitySummary> S) override {
- getView().Entries.push_back({Id, S->InstanceId});
- }
-
- void finalize() override { getView().WasFinalized = true; }
-};
-
-static SummaryViewBuilderRegistry::Add<Analysis4Builder>
- RegAnalysis4("Analysis4", "Builder for Analysis4");
-
-// ---------------------------------------------------------------------------
-// Fixture
-// ---------------------------------------------------------------------------
-
-class LUSummaryConsumerTest : public TestFixture {
-protected:
- static constexpr EntityLinkage ExternalLinkage =
- EntityLinkage(EntityLinkageType::External);
-
- void SetUp() override {
- NextSummaryInstanceId = 0;
- Analysis1BuilderWasDestroyed = false;
- Analysis2BuilderWasDestroyed = false;
- Analysis4BuilderWasDestroyed = false;
- }
-
- std::unique_ptr<LUSummary> makeLUSummary() {
- NestedBuildNamespace NS(
- {BuildNamespace(BuildNamespaceKind::LinkUnit, "TestLU")});
- return std::make_unique<LUSummary>(std::move(NS));
- }
-
- EntityId addEntity(LUSummary &LU, llvm::StringRef USR) {
- NestedBuildNamespace NS(
- {BuildNamespace(BuildNamespaceKind::LinkUnit, "TestLU")});
- EntityName Name(USR.str(), "", NS);
- EntityId Id = getIdTable(LU).getId(Name);
- getLinkageTable(LU).insert({Id, ExternalLinkage});
- return Id;
- }
-
- static bool hasEntry(const std::vector<std::pair<EntityId, int>> &Entries,
- EntityId Id, int InstanceId) {
- return std::find(Entries.begin(), Entries.end(),
- std::make_pair(Id, InstanceId)) != Entries.end();
- }
-
- /// Inserts a freshly constructed SummaryT for the given entity and returns
- /// the summary's InstanceId so the test can verify delivery later.
- template <typename SummaryT>
- int insertSummary(LUSummary &LU, llvm::StringRef SN, EntityId Id) {
- auto S = std::make_unique<SummaryT>();
- int InstanceId = S->InstanceId;
- getData(LU)[SummaryName(SN.str())][Id] = std::move(S);
- return InstanceId;
- }
-};
-
-// ---------------------------------------------------------------------------
-// Tests
-// ---------------------------------------------------------------------------
-
-TEST_F(LUSummaryConsumerTest, Run) {
- auto LU = makeLUSummary();
- const auto E1 = addEntity(*LU, "Entity1");
- const auto E2 = addEntity(*LU, "Entity2");
- const auto E3 = addEntity(*LU, "Entity3");
-
- int s1a = insertSummary<Analysis1EntitySummary>(*LU, "Analysis1", E1);
- int s1b = insertSummary<Analysis1EntitySummary>(*LU, "Analysis1", E2);
- int s2a = insertSummary<Analysis2EntitySummary>(*LU, "Analysis2", E2);
- int s2b = insertSummary<Analysis2EntitySummary>(*LU, "Analysis2", E3);
-
- // no registered builder
- [[maybe_unused]] int s3a =
- insertSummary<Analysis1EntitySummary>(*LU, "Analysis3", E1);
-
- LUSummaryConsumer Consumer(std::move(LU));
- Consumer.run();
-
- // Analysis1
- {
- auto View1 = Consumer.getView<Analysis1View>();
- ASSERT_NE(View1, nullptr);
-
- // getView ownership transfer — second call returns nullptr
- EXPECT_EQ(Consumer.getView<Analysis1View>(), nullptr);
-
- EXPECT_EQ(View1->Entries.size(), 2u);
- EXPECT_TRUE(hasEntry(View1->Entries, E1, s1a));
- EXPECT_TRUE(hasEntry(View1->Entries, E2, s1b));
-
- EXPECT_TRUE(View1->WasFinalized);
-
- // Builder lifetime
- EXPECT_TRUE(Analysis1BuilderWasDestroyed);
- }
-
- // Analysis2
- {
- auto View2 = Consumer.getView<Analysis2View>();
- ASSERT_NE(View2, nullptr);
-
- // getView ownership transfer — second call returns nullptr
- EXPECT_EQ(Consumer.getView<Analysis2View>(), nullptr);
-
- EXPECT_EQ(View2->Entries.size(), 2u);
- EXPECT_TRUE(hasEntry(View2->Entries, E2, s2a));
- EXPECT_TRUE(hasEntry(View2->Entries, E3, s2b));
-
- EXPECT_TRUE(View2->WasFinalized);
-
- // Builder lifetime
- EXPECT_TRUE(Analysis2BuilderWasDestroyed);
- }
-
- // Analysis 3
- {
- // Unregistered builder — Analysis3 data silently skipped
- EXPECT_EQ(Consumer.getView<Analysis3View>(), nullptr);
- }
-
- // Analysis4
- {
- // Registered builder but no data in LUSummary — builder never invoked
- EXPECT_EQ(Consumer.getView<Analysis4View>(), nullptr);
-
- // Builder lifetime
- EXPECT_FALSE(Analysis4BuilderWasDestroyed);
- }
-}
-
-} // namespace
diff --git a/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp b/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp
deleted file mode 100644
index 30d3606247b2e..0000000000000
--- a/clang/unittests/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistryTest.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-//===- SummaryViewBuilderRegistryTest.cpp ---------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "clang/Analysis/Scalable/SummaryView/SummaryViewBuilderRegistry.h"
-#include "llvm/ADT/StringRef.h"
-#include "gtest/gtest.h"
-#include <memory>
-#include <set>
-
-using namespace clang;
-using namespace ssaf;
-
-namespace {
-
-class SummaryViewBuilderRegistryTest : public ::testing::Test {};
-
-TEST_F(SummaryViewBuilderRegistryTest, isSummaryViewBuilderRegistered) {
- EXPECT_FALSE(isSummaryViewBuilderRegistered("Non-existent-builder"));
- EXPECT_TRUE(isSummaryViewBuilderRegistered("Analysis1"));
- EXPECT_TRUE(isSummaryViewBuilderRegistered("Analysis2"));
- EXPECT_TRUE(isSummaryViewBuilderRegistered("Analysis4"));
-}
-
-TEST_F(SummaryViewBuilderRegistryTest, EnumeratingRegistryEntries) {
- std::set<llvm::StringRef> ActualNames;
- for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
- bool Inserted = ActualNames.insert(Entry.getName()).second;
- EXPECT_TRUE(Inserted);
- }
-
- EXPECT_EQ(ActualNames,
- (std::set<llvm::StringRef>{"Analysis1", "Analysis2", "Analysis4"}));
-}
-
-TEST_F(SummaryViewBuilderRegistryTest, InstantiatingBuilder_Analysis1) {
- std::unique_ptr<SummaryViewBuilderBase> B;
- for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
- if (Entry.getName() == "Analysis1")
- B = Entry.instantiate();
- }
-
- ASSERT_NE(B, nullptr);
- EXPECT_EQ(B->summaryName(), SummaryName("Analysis1"));
-}
-
-TEST_F(SummaryViewBuilderRegistryTest, InstantiatingBuilder_Analysis2) {
- std::unique_ptr<SummaryViewBuilderBase> B;
- for (const auto &Entry : SummaryViewBuilderRegistry::entries()) {
- if (Entry.getName() == "Analysis2")
- B = Entry.instantiate();
- }
-
- ASSERT_NE(B, nullptr);
- EXPECT_EQ(B->summaryName(), SummaryName("Analysis2"));
-}
-
-} // namespace
More information about the cfe-commits
mailing list