Author: Aviral Goel Date: 2026-03-12T13:52:00-07:00 New Revision: 4334fed5c954f1d12cf3003f64d3ed61605b3b39
URL: https://github.com/llvm/llvm-project/commit/4334fed5c954f1d12cf3003f64d3ed61605b3b39 DIFF: https://github.com/llvm/llvm-project/commit/4334fed5c954f1d12cf3003f64d3ed61605b3b39.diff LOG: [clang][ssaf] Add LUSummary consumer APIs This patch adds the consumer-side infrastructure for the Scalable Static Analysis Framework (SSAF). After the EntityLinker produces a LUSummary, these new components build typed analysis data from it. - `SummaryData` — A base class for analysis views to expose analysis-specific query API. - `SummaryDataBuilder` — An abstract base class that populates a concrete `SummaryData`. - `SummaryDataBuilderRegistry` — A registry wrapper for `SummaryDataBuilder` class. - `SummaryDataStore` - A wrapper containing a map from `SummaryName` to `SummaryData`, returned as a result of running the `LUSummaryConsumer`. - `LUSummaryConsumer` — Drives the process of populating `SummaryData` instances from `LUSummary` via corresponding `SummaryDataBuilder` instances. Added: clang/include/clang/Analysis/Scalable/SummaryData/LUSummaryConsumer.h clang/include/clang/Analysis/Scalable/SummaryData/SummaryData.h clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataBuilder.h clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataBuilderRegistry.h clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataStore.h clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataTraits.h clang/lib/Analysis/Scalable/SummaryData/LUSummaryConsumer.cpp clang/lib/Analysis/Scalable/SummaryData/SummaryDataBuilderRegistry.cpp clang/unittests/Analysis/Scalable/SummaryData/SummaryDataTest.cpp Modified: clang/include/clang/Analysis/Scalable/EntityLinker/LUSummary.h clang/lib/Analysis/Scalable/CMakeLists.txt clang/unittests/Analysis/Scalable/CMakeLists.txt Removed: ################################################################################ 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/SummaryData/LUSummaryConsumer.h b/clang/include/clang/Analysis/Scalable/SummaryData/LUSummaryConsumer.h new file mode 100644 index 0000000000000..6ac2e564e1c48 --- /dev/null +++ b/clang/include/clang/Analysis/Scalable/SummaryData/LUSummaryConsumer.h @@ -0,0 +1,93 @@ +//===- 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 SummaryData objects by routing LUSummary entity +// data to the corresponding SummaryDataBuilder objects. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_LUSUMMARYCONSUMER_H +#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_LUSUMMARYCONSUMER_H + +#include "clang/Analysis/Scalable/EntityLinker/LUSummary.h" +#include "clang/Analysis/Scalable/Model/SummaryName.h" +#include "clang/Analysis/Scalable/SummaryData/SummaryDataStore.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/Error.h" +#include <memory> +#include <utility> + +namespace clang::ssaf { + +/// Consumes a LUSummary by dispatching its entity data to registered +/// SummaryDataBuilders and returning the results in a SummaryDataStore. +/// +/// Three consumption patterns are supported: +/// - \c run() && — processes every analysis present in the +/// LUSummary, silently skipping any whose data is +/// absent or whose builder is not registered. +/// Cannot fail, so it returns \c SummaryDataStore +/// directly. Requires an rvalue consumer because +/// this pattern exhausts all remaining data. +/// - \c run(names) — processes a named subset; returns +/// \c llvm::Expected<SummaryDataStore> and fails if +/// any requested name has no data in the LUSummary +/// or no registered builder. +/// - \c run<DataTs...>() — type-safe variant of \c run(names) with the +/// same error semantics and return type. +/// +/// All patterns consume the underlying LUSummary data, so each analysis can +/// only be retrieved once across all run() calls. +class LUSummaryConsumer final { +public: + explicit LUSummaryConsumer(std::unique_ptr<LUSummary> LU) + : LU(std::move(LU)) {} + + /// Processes all registered analyses in LUSummary and returns the results. + /// Silently skips analyses with no data or no registered builder. + /// + /// Requires an rvalue consumer (call as \c std::move(Consumer).run()) because + /// this pattern exhausts all remaining LUSummary data. + [[nodiscard]] SummaryDataStore run() &&; + + /// Processes the named analyses and returns the results. + /// + /// Returns an error if any name has no data in the LUSummary or no + /// registered builder. + [[nodiscard]] llvm::Expected<SummaryDataStore> + run(llvm::ArrayRef<SummaryName> Names); + + /// Processes analyses for each of the given types and returns the results. + /// + /// Returns an error if any type has no data in the LUSummary or no + /// registered builder. + template <typename... DataTs> + [[nodiscard]] llvm::Expected<SummaryDataStore> run() { + return run({DataTs::summaryName()...}); + } + +private: + std::unique_ptr<LUSummary> LU; + + /// Iterator into LUSummary::Data — the map from SummaryName to entity data. + using LUDataIterator = decltype(std::declval<LUSummary &>().Data)::iterator; + + /// Core build implementation. Instantiates the registered builder for the + /// analysis at \p It, delivers all entities, finalizes, and returns the + /// built data. Returns an error if no builder is registered. Erases the + /// LUSummary entry on success. + llvm::Expected<std::unique_ptr<SummaryData>> build(LUDataIterator It); + + /// Looks up \p SN in the LUSummary and delegates to the iterator overload. + /// Returns an error if no data for \p SN exists or no builder is registered. + llvm::Expected<std::unique_ptr<SummaryData>> build(const SummaryName &SN); +}; + +} // namespace clang::ssaf + +#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_LUSUMMARYCONSUMER_H diff --git a/clang/include/clang/Analysis/Scalable/SummaryData/SummaryData.h b/clang/include/clang/Analysis/Scalable/SummaryData/SummaryData.h new file mode 100644 index 0000000000000..35403ecda5d6d --- /dev/null +++ b/clang/include/clang/Analysis/Scalable/SummaryData/SummaryData.h @@ -0,0 +1,28 @@ +//===- SummaryData.h ------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Base class for all whole-program analysis data built from +// LUSummary data. Carries no query API — all analysis-specific methods live +// on concrete subclasses. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATA_H +#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATA_H + +namespace clang::ssaf { + +/// Base class for whole-program analysis data. +class SummaryData { +public: + virtual ~SummaryData() = default; +}; + +} // namespace clang::ssaf + +#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATA_H diff --git a/clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataBuilder.h b/clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataBuilder.h new file mode 100644 index 0000000000000..f7784b861130f --- /dev/null +++ b/clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataBuilder.h @@ -0,0 +1,95 @@ +//===- SummaryDataBuilder.h -----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines SummaryDataBuilderBase (abstract base known to the +// registry and LUSummaryConsumer) and the typed intermediate template +// SummaryDataBuilder<DataT, SummaryT> that concrete builders inherit from. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATABUILDER_H +#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATABUILDER_H + +#include "clang/Analysis/Scalable/Model/EntityId.h" +#include "clang/Analysis/Scalable/Model/SummaryName.h" +#include "clang/Analysis/Scalable/SummaryData/SummaryData.h" +#include "clang/Analysis/Scalable/SummaryData/SummaryDataTraits.h" +#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h" +#include <memory> + +namespace clang::ssaf { + +class LUSummaryConsumer; + +/// Abstract base class for all summary data builders. +/// +/// Known to the registry and LUSummaryConsumer. Receives entities one at a +/// time via \c addSummary(), is finalized via \c finalize(), and transfers +/// ownership of the built data via \c getData(). +class SummaryDataBuilderBase { + friend class LUSummaryConsumer; + +public: + virtual ~SummaryDataBuilderBase() = default; + +private: + /// 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 data. Called by LUSummaryConsumer after + /// finalize(). The rvalue ref-qualifier enforces single use — the builder + /// cannot be accessed after this call. + virtual std::unique_ptr<SummaryData> getData() && = 0; +}; + +/// Typed intermediate template that concrete builders inherit from. +/// Concrete builders must implement the typed +/// \c addSummary(EntityId, unique_ptr<SummaryT>) overload, and may override +/// \c finalize() for any post-processing needed after all entities are added. +template <typename DataT, typename SummaryT> +class SummaryDataBuilder : public SummaryDataBuilderBase { + static_assert(std::is_base_of_v<SummaryData, DataT>, + "DataT must derive from SummaryData"); + static_assert(HasSummaryName<DataT>::value, + "DataT must have a static summaryName() method"); + static_assert(std::is_base_of_v<EntitySummary, SummaryT>, + "SummaryT must derive from EntitySummary"); + + std::unique_ptr<DataT> Data; + +public: + SummaryDataBuilder() : Data(std::make_unique<DataT>()) {} + + /// Returns the SummaryName of the data this builder produces. + /// Used by SummaryDataBuilderRegistry::Add to derive the registry entry name. + static SummaryName summaryName() { return DataT::summaryName(); } + +protected: + /// Typed customization point — concrete builders override this. + virtual void addSummary(EntityId Id, std::unique_ptr<SummaryT> Summary) = 0; + + DataT &getData() & { return *Data; } + +private: + std::unique_ptr<SummaryData> getData() && override { return std::move(Data); } + + /// Seals the base overload, downcasts, and dispatches to the typed overload. + void addSummary(EntityId Id, std::unique_ptr<EntitySummary> Summary) final { + addSummary(Id, std::unique_ptr<SummaryT>( + static_cast<SummaryT *>(Summary.release()))); + } +}; + +} // namespace clang::ssaf + +#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATABUILDER_H diff --git a/clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataBuilderRegistry.h b/clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataBuilderRegistry.h new file mode 100644 index 0000000000000..7ee6a214486bf --- /dev/null +++ b/clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataBuilderRegistry.h @@ -0,0 +1,72 @@ +//===- SummaryDataBuilderRegistry.h ---------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Registry for SummaryDataBuilders. +// +// To register a builder, add a static Add<BuilderT> in the builder's +// translation unit: +// +// static SummaryDataBuilderRegistry::Add<MyDataBuilder> +// Registered("Data builder for MyAnalysis"); +// +// The registry entry name is derived automatically from +// MyDataBuilder::summaryName(), which returns MyData::summaryName(). +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATABUILDERREGISTRY_H +#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATABUILDERREGISTRY_H + +#include "clang/Analysis/Scalable/SummaryData/SummaryDataBuilder.h" +#include "llvm/Support/Registry.h" +#include <memory> +#include <string> + +namespace clang::ssaf { + +/// Registry for SummaryDataBuilder implementations. +/// +/// Provides an Add helper that derives the registry entry name from +/// BuilderT::summaryName(), eliminating the possibility of registering a +/// builder under the wrong name. +class SummaryDataBuilderRegistry { + using RegistryT = llvm::Registry<SummaryDataBuilderBase>; + + SummaryDataBuilderRegistry() = delete; + +public: + /// Registers \p BuilderT under the name returned by + /// \c BuilderT::summaryName(). Only a description is required. + /// + /// \c Add objects must be declared \c static at namespace scope — they + /// register an entry in a global linked list on construction and are + /// not copyable or movable. + template <typename BuilderT> struct Add { + explicit Add(llvm::StringRef Desc) + : Name(BuilderT::summaryName().str().str()), Node(Name, Desc) {} + + Add(const Add &) = delete; + Add &operator=(const Add &) = delete; + + private: + std::string Name; + RegistryT::Add<BuilderT> Node; + }; + + /// Returns true if a builder is registered under \p Name. + static bool contains(llvm::StringRef Name); + + /// Instantiates the builder registered under \p Name, or returns nullptr + /// if no such builder is registered. + static std::unique_ptr<SummaryDataBuilderBase> + instantiate(llvm::StringRef Name); +}; + +} // namespace clang::ssaf + +#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATABUILDERREGISTRY_H diff --git a/clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataStore.h b/clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataStore.h new file mode 100644 index 0000000000000..9a091ed43880c --- /dev/null +++ b/clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataStore.h @@ -0,0 +1,115 @@ +//===- SummaryDataStore.h -------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Owns a collection of SummaryData objects keyed by SummaryName. +// Produced by LUSummaryConsumer::run() variants. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATASTORE_H +#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATASTORE_H + +#include "clang/Analysis/Scalable/Model/SummaryName.h" +#include "clang/Analysis/Scalable/SummaryData/SummaryData.h" +#include "clang/Analysis/Scalable/SummaryData/SummaryDataTraits.h" +#include "clang/Analysis/Scalable/Support/ErrorBuilder.h" +#include "llvm/Support/Error.h" +#include <map> +#include <memory> + +namespace clang::ssaf { + +class LUSummaryConsumer; + +/// Owns a collection of SummaryData objects keyed by SummaryName. +/// Produced by LUSummaryConsumer::run() variants. +class SummaryDataStore { + friend class LUSummaryConsumer; + + std::map<SummaryName, std::unique_ptr<SummaryData>> Data; + +public: + /// Returns true if data for \p Name is stored. + [[nodiscard]] bool contains(const SummaryName &Name) const { + return Data.find(Name) != Data.end(); + } + + /// Returns true if data for \p DataT is stored. + template <typename DataT> [[nodiscard]] bool contains() const { + static_assert(std::is_base_of_v<SummaryData, DataT>, + "DataT must derive from SummaryData"); + static_assert(HasSummaryName<DataT>::value, + "DataT must have a static summaryName() method"); + + return contains(DataT::summaryName()); + } + + /// Returns a reference to the data for \p DataT, or an error if + /// no data for \p DataT is stored. + template <typename DataT> [[nodiscard]] llvm::Expected<DataT &> get() { + static_assert(std::is_base_of_v<SummaryData, DataT>, + "DataT must derive from SummaryData"); + static_assert(HasSummaryName<DataT>::value, + "DataT must have a static summaryName() method"); + + auto Result = get(DataT::summaryName()); + if (!Result) { + return Result.takeError(); + } + return static_cast<DataT &>(*Result); + } + + /// Returns a reference to the data for \p Name, or an error if + /// no data for \p Name is stored. + [[nodiscard]] llvm::Expected<SummaryData &> get(const SummaryName &Name) { + auto It = Data.find(Name); + if (It == Data.end()) { + return ErrorBuilder::create(std::errc::invalid_argument, + "no data for analysis '{0}' in store", + Name.str()) + .build(); + } + return *It->second; + } + + /// Transfers ownership of the data for \p DataT to the caller, or returns + /// an error if no data for \p DataT is stored. + template <typename DataT> + [[nodiscard]] llvm::Expected<std::unique_ptr<DataT>> take() { + static_assert(std::is_base_of_v<SummaryData, DataT>, + "DataT must derive from SummaryData"); + static_assert(HasSummaryName<DataT>::value, + "DataT must have a static summaryName() method"); + + auto Result = take(DataT::summaryName()); + if (!Result) { + return Result.takeError(); + } + return std::unique_ptr<DataT>(static_cast<DataT *>(Result->release())); + } + + /// Transfers ownership of the data for \p Name to the caller, or returns + /// an error if no data for \p Name is stored. + [[nodiscard]] llvm::Expected<std::unique_ptr<SummaryData>> + take(const SummaryName &Name) { + auto It = Data.find(Name); + if (It == Data.end()) { + return ErrorBuilder::create(std::errc::invalid_argument, + "no data for analysis '{0}' in store", + Name.str()) + .build(); + } + auto Ptr = std::move(It->second); + Data.erase(It); + return Ptr; + } +}; + +} // namespace clang::ssaf + +#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATASTORE_H diff --git a/clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataTraits.h b/clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataTraits.h new file mode 100644 index 0000000000000..4c41da9266c5a --- /dev/null +++ b/clang/include/clang/Analysis/Scalable/SummaryData/SummaryDataTraits.h @@ -0,0 +1,38 @@ +//===- SummaryDataTraits.h ------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Type traits for SummaryData subclasses. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATATRAITS_H +#define LLVM_CLANG_ANALYSIS_SCALABLE_SUMMARYDATA_SUMMARYDATATRAITS_H + +#include "clang/Analysis/Scalable/Model/SummaryName.h" +#include <type_traits> + +namespace clang::ssaf { + +/// Type trait that checks whether \p T has a static \c summaryName() method +/// returning \c SummaryName. Used to enforce the convention on SummaryData +/// subclasses at instantiation time. +/// +/// The expression \c T::summaryName() is only well-formed for static methods — +/// calling a non-static member without an object is ill-formed and causes the +/// partial specialization to be discarded via SFINAE, so non-static overloads +/// are correctly rejected. +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_SUMMARYDATA_SUMMARYDATATRAITS_H diff --git a/clang/lib/Analysis/Scalable/CMakeLists.txt b/clang/lib/Analysis/Scalable/CMakeLists.txt index 4593fbcd515b5..63e066be6a8d2 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 + SummaryData/LUSummaryConsumer.cpp + SummaryData/SummaryDataBuilderRegistry.cpp Support/ErrorBuilder.cpp TUSummary/ExtractorRegistry.cpp TUSummary/TUSummaryBuilder.cpp diff --git a/clang/lib/Analysis/Scalable/SummaryData/LUSummaryConsumer.cpp b/clang/lib/Analysis/Scalable/SummaryData/LUSummaryConsumer.cpp new file mode 100644 index 0000000000000..db68c009cbf1b --- /dev/null +++ b/clang/lib/Analysis/Scalable/SummaryData/LUSummaryConsumer.cpp @@ -0,0 +1,78 @@ +//===- 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/SummaryData/LUSummaryConsumer.h" +#include "clang/Analysis/Scalable/SummaryData/SummaryDataBuilderRegistry.h" +#include "clang/Analysis/Scalable/Support/ErrorBuilder.h" + +using namespace clang; +using namespace ssaf; + +llvm::Expected<std::unique_ptr<SummaryData>> +LUSummaryConsumer::build(LUDataIterator LUIt) { + const SummaryName SN = LUIt->first; + auto Builder = SummaryDataBuilderRegistry::instantiate(SN.str()); + if (!Builder) { + return ErrorBuilder::create(std::errc::invalid_argument, + "no builder registered for analysis '{0}'", + SN.str()) + .build(); + } + + for (auto &[Id, Summary] : LUIt->second) { + Builder->addSummary(Id, std::move(Summary)); + } + Builder->finalize(); + LU->Data.erase(LUIt); + + return std::move(*Builder).getData(); +} + +llvm::Expected<std::unique_ptr<SummaryData>> +LUSummaryConsumer::build(const SummaryName &SN) { + auto LUIt = LU->Data.find(SN); + if (LUIt == LU->Data.end()) { + return ErrorBuilder::create(std::errc::invalid_argument, + "no data for analysis '{0}' in LUSummary", + SN.str()) + .build(); + } + return build(LUIt); +} + +llvm::Expected<SummaryDataStore> +LUSummaryConsumer::run(llvm::ArrayRef<SummaryName> Names) { + SummaryDataStore Store; + for (const auto &SN : Names) { + auto Result = build(SN); + if (!Result) { + return Result.takeError(); + } + Store.Data.emplace(SN, std::move(*Result)); + } + return Store; +} + +SummaryDataStore LUSummaryConsumer::run() && { + SummaryDataStore Store; + // Advance the iterator before calling build(): build() erases the current + // element on success, but std::map only invalidates iterators to the erased + // element, so the pre-advanced iterator remains valid in all cases. + auto It = LU->Data.begin(); + while (It != LU->Data.end()) { + auto Current = It++; // Read the comment above! + SummaryName SN = Current->first; // copy before build() potentially erases + auto Result = build(Current); + if (!Result) { + llvm::consumeError(Result.takeError()); + continue; + } + Store.Data.emplace(std::move(SN), std::move(*Result)); + } + return Store; +} diff --git a/clang/lib/Analysis/Scalable/SummaryData/SummaryDataBuilderRegistry.cpp b/clang/lib/Analysis/Scalable/SummaryData/SummaryDataBuilderRegistry.cpp new file mode 100644 index 0000000000000..6b25ed03eeb81 --- /dev/null +++ b/clang/lib/Analysis/Scalable/SummaryData/SummaryDataBuilderRegistry.cpp @@ -0,0 +1,36 @@ +//===- SummaryDataBuilderRegistry.cpp -------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/Scalable/SummaryData/SummaryDataBuilderRegistry.h" + +using namespace clang; +using namespace ssaf; + +using RegistryT = llvm::Registry<SummaryDataBuilderBase>; +LLVM_INSTANTIATE_REGISTRY(RegistryT) + +namespace { +const RegistryT::entry *findEntry(llvm::StringRef Name) { + for (const auto &Entry : RegistryT::entries()) { + if (Entry.getName() == Name) { + return &Entry; + } + } + return nullptr; +} +} // namespace + +bool SummaryDataBuilderRegistry::contains(llvm::StringRef Name) { + return findEntry(Name) != nullptr; +} + +std::unique_ptr<SummaryDataBuilderBase> +SummaryDataBuilderRegistry::instantiate(llvm::StringRef Name) { + const auto *Entry = findEntry(Name); + return Entry ? Entry->instantiate() : nullptr; +} diff --git a/clang/unittests/Analysis/Scalable/CMakeLists.txt b/clang/unittests/Analysis/Scalable/CMakeLists.txt index 54f9de71884dd..770e900d8683e 100644 --- a/clang/unittests/Analysis/Scalable/CMakeLists.txt +++ b/clang/unittests/Analysis/Scalable/CMakeLists.txt @@ -18,6 +18,7 @@ add_distinct_clang_unittest(ClangScalableAnalysisTests Serialization/JSONFormatTest/JSONFormatTest.cpp Serialization/JSONFormatTest/LUSummaryTest.cpp Serialization/JSONFormatTest/TUSummaryTest.cpp + SummaryData/SummaryDataTest.cpp SummaryNameTest.cpp TestFixture.cpp TUSummaryBuilderTest.cpp diff --git a/clang/unittests/Analysis/Scalable/SummaryData/SummaryDataTest.cpp b/clang/unittests/Analysis/Scalable/SummaryData/SummaryDataTest.cpp new file mode 100644 index 0000000000000..067683b1ca164 --- /dev/null +++ b/clang/unittests/Analysis/Scalable/SummaryData/SummaryDataTest.cpp @@ -0,0 +1,397 @@ +//===- SummaryDataTest.cpp ------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "../TestFixture.h" +#include "clang/Analysis/Scalable/EntityLinker/LUSummary.h" +#include "clang/Analysis/Scalable/Model/BuildNamespace.h" +#include "clang/Analysis/Scalable/Model/EntityId.h" +#include "clang/Analysis/Scalable/Model/EntityIdTable.h" +#include "clang/Analysis/Scalable/Model/EntityLinkage.h" +#include "clang/Analysis/Scalable/Model/EntityName.h" +#include "clang/Analysis/Scalable/Model/SummaryName.h" +#include "clang/Analysis/Scalable/SummaryData/LUSummaryConsumer.h" +#include "clang/Analysis/Scalable/SummaryData/SummaryDataBuilderRegistry.h" +#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Testing/Support/Error.h" +#include "gtest/gtest.h" +#include <memory> +#include <utility> +#include <vector> + +using namespace clang; +using namespace ssaf; + +namespace { + +// --------------------------------------------------------------------------- +// Instance counter +// --------------------------------------------------------------------------- + +static int NextSummaryInstanceId = 0; + +// --------------------------------------------------------------------------- +// Entity summaries +// --------------------------------------------------------------------------- + +class Analysis1EntitySummary final : public EntitySummary { +public: + int InstanceId = NextSummaryInstanceId++; + SummaryName getSummaryName() const override { + return SummaryName("Analysis1"); + } +}; + +class Analysis2EntitySummary final : public EntitySummary { +public: + int InstanceId = NextSummaryInstanceId++; + SummaryName getSummaryName() const override { + return SummaryName("Analysis2"); + } +}; + +class Analysis3EntitySummary final : public EntitySummary { +public: + int InstanceId = NextSummaryInstanceId++; + SummaryName getSummaryName() const override { + return SummaryName("Analysis3"); + } +}; + +class Analysis4EntitySummary final : public EntitySummary { +public: + int InstanceId = NextSummaryInstanceId++; + SummaryName getSummaryName() const override { + return SummaryName("Analysis4"); + } +}; + +// --------------------------------------------------------------------------- +// Data +// --------------------------------------------------------------------------- + +class Analysis1Data final : public SummaryData { +public: + static SummaryName summaryName() { return SummaryName("Analysis1"); } + std::vector<std::pair<EntityId, int>> Entries; + bool WasFinalized = false; +}; + +class Analysis2Data final : public SummaryData { +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 Analysis3Data final : public SummaryData { +public: + static SummaryName summaryName() { return SummaryName("Analysis3"); } +}; + +// Analysis4 has a registered builder but no data is inserted into the +// LUSummary, so the builder is never invoked and getData returns nullptr. +class Analysis4Data final : public SummaryData { +public: + static SummaryName summaryName() { return SummaryName("Analysis4"); } + std::vector<std::pair<EntityId, int>> Entries; + bool WasFinalized = false; +}; + +// --------------------------------------------------------------------------- +// Builder destruction flags (reset in SetUp) +// --------------------------------------------------------------------------- + +static bool Analysis1BuilderWasDestroyed = false; +static bool Analysis2BuilderWasDestroyed = false; +static bool Analysis4BuilderWasDestroyed = false; + +// --------------------------------------------------------------------------- +// Builders +// --------------------------------------------------------------------------- + +class Analysis1Builder final + : public SummaryDataBuilder<Analysis1Data, Analysis1EntitySummary> { +public: + ~Analysis1Builder() { Analysis1BuilderWasDestroyed = true; } + + void addSummary(EntityId Id, + std::unique_ptr<Analysis1EntitySummary> S) override { + getData().Entries.push_back({Id, S->InstanceId}); + } + + void finalize() override { getData().WasFinalized = true; } +}; + +static SummaryDataBuilderRegistry::Add<Analysis1Builder> + RegAnalysis1("Builder for Analysis1"); + +class Analysis2Builder final + : public SummaryDataBuilder<Analysis2Data, Analysis2EntitySummary> { +public: + ~Analysis2Builder() { Analysis2BuilderWasDestroyed = true; } + + void addSummary(EntityId Id, + std::unique_ptr<Analysis2EntitySummary> S) override { + getData().Entries.push_back({Id, S->InstanceId}); + } + + void finalize() override { getData().WasFinalized = true; } +}; + +static SummaryDataBuilderRegistry::Add<Analysis2Builder> + RegAnalysis2("Builder for Analysis2"); + +class Analysis4Builder final + : public SummaryDataBuilder<Analysis4Data, Analysis4EntitySummary> { +public: + ~Analysis4Builder() { Analysis4BuilderWasDestroyed = true; } + + void addSummary(EntityId Id, + std::unique_ptr<Analysis4EntitySummary> S) override { + getData().Entries.push_back({Id, S->InstanceId}); + } + + void finalize() override { getData().WasFinalized = true; } +}; + +static SummaryDataBuilderRegistry::Add<Analysis4Builder> + RegAnalysis4("Builder for Analysis4"); + +// --------------------------------------------------------------------------- +// Fixture +// --------------------------------------------------------------------------- + +class LUSummaryConsumerTest : public TestFixture { +protected: + static constexpr EntityLinkage ExternalLinkage = + EntityLinkage(EntityLinkageType::External); + + void SetUp() override { + NextSummaryInstanceId = 0; + Analysis1BuilderWasDestroyed = false; + Analysis2BuilderWasDestroyed = false; + Analysis4BuilderWasDestroyed = false; + } + + std::unique_ptr<LUSummary> makeLUSummary() { + NestedBuildNamespace NS( + {BuildNamespace(BuildNamespaceKind::LinkUnit, "TestLU")}); + return std::make_unique<LUSummary>(std::move(NS)); + } + + EntityId addEntity(LUSummary &LU, llvm::StringRef USR) { + NestedBuildNamespace NS( + {BuildNamespace(BuildNamespaceKind::LinkUnit, "TestLU")}); + EntityName Name(USR.str(), "", NS); + EntityId Id = getIdTable(LU).getId(Name); + getLinkageTable(LU).insert({Id, ExternalLinkage}); + return Id; + } + + static bool hasEntry(const std::vector<std::pair<EntityId, int>> &Entries, + EntityId Id, int InstanceId) { + return llvm::is_contained(Entries, std::make_pair(Id, InstanceId)); + } + + /// 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(SummaryDataBuilderRegistryTest, BuilderIsRegistered) { + EXPECT_FALSE(SummaryDataBuilderRegistry::contains("AnalysisNonExisting")); + EXPECT_TRUE(SummaryDataBuilderRegistry::contains("Analysis1")); + EXPECT_TRUE(SummaryDataBuilderRegistry::contains("Analysis2")); + EXPECT_TRUE(SummaryDataBuilderRegistry::contains("Analysis4")); +} + +TEST(SummaryDataBuilderRegistryTest, BuilderCanBeInstantiated) { + EXPECT_EQ(SummaryDataBuilderRegistry::instantiate("AnalysisNonExisting"), + nullptr); + EXPECT_NE(SummaryDataBuilderRegistry::instantiate("Analysis1"), nullptr); + EXPECT_NE(SummaryDataBuilderRegistry::instantiate("Analysis2"), nullptr); + EXPECT_NE(SummaryDataBuilderRegistry::instantiate("Analysis4"), nullptr); +} + +// run() — processes all registered analyses present in the LUSummary. +TEST_F(LUSummaryConsumerTest, RunAll) { + auto LU = makeLUSummary(); + const auto E1 = addEntity(*LU, "Entity1"); + const auto E2 = addEntity(*LU, "Entity2"); + 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 — Analysis3 data silently skipped. + (void)insertSummary<Analysis3EntitySummary>(*LU, "Analysis3", E1); + + LUSummaryConsumer Consumer(std::move(LU)); + SummaryDataStore Store = std::move(Consumer).run(); + + { + auto Data1OrErr = Store.get<Analysis1Data>(); + ASSERT_THAT_EXPECTED(Data1OrErr, llvm::Succeeded()); + auto &Data1 = *Data1OrErr; + + EXPECT_EQ(Data1.Entries.size(), 2u); + EXPECT_TRUE(hasEntry(Data1.Entries, E1, s1a)); + EXPECT_TRUE(hasEntry(Data1.Entries, E2, s1b)); + EXPECT_TRUE(Data1.WasFinalized); + EXPECT_TRUE(Analysis1BuilderWasDestroyed); + + // take() transfers ownership — subsequent get() returns an error. + auto Take1OrErr = Store.take<Analysis1Data>(); + ASSERT_THAT_EXPECTED(Take1OrErr, llvm::Succeeded()); + EXPECT_NE(*Take1OrErr, nullptr); + EXPECT_THAT_EXPECTED(Store.get<Analysis1Data>(), llvm::Failed()); + } + + { + auto Data2OrErr = Store.get<Analysis2Data>(); + ASSERT_THAT_EXPECTED(Data2OrErr, llvm::Succeeded()); + auto &Data2 = *Data2OrErr; + EXPECT_EQ(Data2.Entries.size(), 2u); + EXPECT_TRUE(hasEntry(Data2.Entries, E2, s2a)); + EXPECT_TRUE(hasEntry(Data2.Entries, E3, s2b)); + EXPECT_TRUE(Data2.WasFinalized); + EXPECT_TRUE(Analysis2BuilderWasDestroyed); + + auto Take2OrErr = Store.take<Analysis2Data>(); + ASSERT_THAT_EXPECTED(Take2OrErr, llvm::Succeeded()); + EXPECT_NE(*Take2OrErr, nullptr); + EXPECT_THAT_EXPECTED(Store.get<Analysis2Data>(), llvm::Failed()); + } + + // Unregistered — not present in store. + EXPECT_THAT_EXPECTED(Store.get<Analysis3Data>(), llvm::Failed()); + + // Registered builder but no data in LUSummary — not present in store. + EXPECT_THAT_EXPECTED(Store.get<Analysis4Data>(), llvm::Failed()); + EXPECT_FALSE(Analysis4BuilderWasDestroyed); +} + +// run(names) — processes only the analyses for the given names. +TEST_F(LUSummaryConsumerTest, RunByName) { + auto LU = makeLUSummary(); + const auto E1 = addEntity(*LU, "Entity1"); + const auto E2 = addEntity(*LU, "Entity2"); + + int s1a = insertSummary<Analysis1EntitySummary>(*LU, "Analysis1", E1); + insertSummary<Analysis2EntitySummary>(*LU, "Analysis2", E2); + + LUSummaryConsumer Consumer(std::move(LU)); + auto StoreOrErr = Consumer.run({SummaryName("Analysis1")}); + ASSERT_THAT_EXPECTED(StoreOrErr, llvm::Succeeded()); + + // Analysis1 was requested and has data — present. + auto Data1OrErr = StoreOrErr->get<Analysis1Data>(); + ASSERT_THAT_EXPECTED(Data1OrErr, llvm::Succeeded()); + auto &Data1 = *Data1OrErr; + EXPECT_EQ(Data1.Entries.size(), 1u); + EXPECT_TRUE(hasEntry(Data1.Entries, E1, s1a)); + EXPECT_TRUE(Data1.WasFinalized); + + // Analysis2 was not requested — not present even though data exists. + EXPECT_THAT_EXPECTED(StoreOrErr->get<Analysis2Data>(), llvm::Failed()); +} + +// run(names) — error when a requested name has no data in LUSummary. +TEST_F(LUSummaryConsumerTest, RunByNameErrorMissingData) { + auto LU = makeLUSummary(); + LUSummaryConsumer Consumer(std::move(LU)); + + EXPECT_THAT_EXPECTED(Consumer.run({SummaryName("Analysis1")}), + llvm::Failed()); +} + +// run(names) — error when a requested name has no registered builder. +TEST_F(LUSummaryConsumerTest, RunByNameErrorMissingBuilder) { + auto LU = makeLUSummary(); + const auto E1 = addEntity(*LU, "Entity1"); + insertSummary<Analysis3EntitySummary>(*LU, "Analysis3", E1); + + LUSummaryConsumer Consumer(std::move(LU)); + + // Analysis3 has data but no registered builder. + EXPECT_THAT_EXPECTED(Consumer.run({SummaryName("Analysis3")}), + llvm::Failed()); +} + +// run<DataTs...>() — type-safe subset. +TEST_F(LUSummaryConsumerTest, RunByType) { + auto LU = makeLUSummary(); + const auto E1 = addEntity(*LU, "Entity1"); + const auto E2 = addEntity(*LU, "Entity2"); + + int s1a = insertSummary<Analysis1EntitySummary>(*LU, "Analysis1", E1); + insertSummary<Analysis2EntitySummary>(*LU, "Analysis2", E2); + + LUSummaryConsumer Consumer(std::move(LU)); + auto StoreOrErr = Consumer.run<Analysis1Data>(); + ASSERT_THAT_EXPECTED(StoreOrErr, llvm::Succeeded()); + + // Analysis1 was requested — present. + auto Data1OrErr = StoreOrErr->get<Analysis1Data>(); + ASSERT_THAT_EXPECTED(Data1OrErr, llvm::Succeeded()); + auto &Data1 = *Data1OrErr; + EXPECT_EQ(Data1.Entries.size(), 1u); + EXPECT_TRUE(hasEntry(Data1.Entries, E1, s1a)); + EXPECT_TRUE(Data1.WasFinalized); + + // Analysis2 was not requested — not present even though data exists. + EXPECT_THAT_EXPECTED(StoreOrErr->get<Analysis2Data>(), llvm::Failed()); +} + +// run<DataTs...>() — error when a requested type has no data in LUSummary. +TEST_F(LUSummaryConsumerTest, RunByTypeErrorMissingData) { + auto LU = makeLUSummary(); + LUSummaryConsumer Consumer(std::move(LU)); + + EXPECT_THAT_EXPECTED(Consumer.run<Analysis1Data>(), llvm::Failed()); +} + +// contains() — present entries return true; absent entries return false. +TEST_F(LUSummaryConsumerTest, Contains) { + auto LU = makeLUSummary(); + const auto E1 = addEntity(*LU, "Entity1"); + insertSummary<Analysis1EntitySummary>(*LU, "Analysis1", E1); + + LUSummaryConsumer Consumer(std::move(LU)); + SummaryDataStore Store = std::move(Consumer).run(); + + // Type-safe variant. + EXPECT_TRUE(Store.contains<Analysis1Data>()); + EXPECT_FALSE(Store.contains<Analysis2Data>()); + + // Name-based variant. + EXPECT_TRUE(Store.contains(SummaryName("Analysis1"))); + EXPECT_FALSE(Store.contains(SummaryName("Analysis2"))); + + // After take(), contains() returns false. + ASSERT_THAT_EXPECTED(Store.take<Analysis1Data>(), llvm::Succeeded()); + EXPECT_FALSE(Store.contains<Analysis1Data>()); + EXPECT_FALSE(Store.contains(SummaryName("Analysis1"))); +} + +} // namespace _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
