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