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

Reply via email to