[clang] [clang][ssaf] Add whole-program analysis execution layer (PR #186813)
Aviral Goel via cfe-commits
cfe-commits at lists.llvm.org
Mon Mar 16 21:50:45 PDT 2026
https://github.com/aviralg updated https://github.com/llvm/llvm-project/pull/186813
>From 397d5649770f59907fb910c6be127d322b9fdd6a Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Sat, 14 Mar 2026 09:23:07 -0700
Subject: [PATCH 01/11] Analysis Tree
---
.../Core/Analysis/AnalysisBase.h | 55 +++
.../Core/Analysis/AnalysisDriver.h | 91 ++++
.../Core/Analysis/AnalysisRegistry.h | 95 +++++
.../Core/Analysis/AnalysisResult.h | 30 ++
.../Core/Analysis/DerivedAnalysis.h | 133 ++++++
.../Core/Analysis/SummaryAnalysis.h | 132 ++++++
.../Core/Analysis/WPASuite.h | 92 +++++
.../Core/EntityLinker/LUSummary.h | 1 +
.../Core/Model/AnalysisName.h | 49 +++
.../Core/Model/AnalysisTraits.h | 33 ++
.../Core/Support/FormatProviders.h | 8 +
.../Core/Analysis/AnalysisDriver.cpp | 196 +++++++++
.../Core/Analysis/AnalysisRegistry.cpp | 37 ++
.../Core/CMakeLists.txt | 3 +
.../Core/Model/AnalysisName.cpp | 16 +
.../Analysis/AnalysisDriverTest.cpp | 390 ++++++++++++++++++
.../CMakeLists.txt | 1 +
17 files changed, 1362 insertions(+)
create mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisBase.h
create mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisDriver.h
create mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisRegistry.h
create mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisResult.h
create mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/DerivedAnalysis.h
create mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/SummaryAnalysis.h
create mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/WPASuite.h
create mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/Core/Model/AnalysisName.h
create mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/Core/Model/AnalysisTraits.h
create mode 100644 clang/lib/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisDriver.cpp
create mode 100644 clang/lib/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisRegistry.cpp
create mode 100644 clang/lib/ScalableStaticAnalysisFramework/Core/Model/AnalysisName.cpp
create mode 100644 clang/unittests/ScalableStaticAnalysisFramework/Analysis/AnalysisDriverTest.cpp
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisBase.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisBase.h
new file mode 100644
index 0000000000000..478a6fa85d4a8
--- /dev/null
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisBase.h
@@ -0,0 +1,55 @@
+//===- AnalysisBase.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
+//
+//===----------------------------------------------------------------------===//
+//
+// Minimal common base for SummaryAnalysisBase and DerivedAnalysisBase.
+// Carries the identity (analysisName()) and dependency list
+// (dependencyNames()) shared by every analysis regardless of kind.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSIS_ANALYSISBASE_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSIS_ANALYSISBASE_H
+
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/AnalysisName.h"
+#include <vector>
+
+namespace clang::ssaf {
+
+class AnalysisDriver;
+class SummaryAnalysisBase;
+class DerivedAnalysisBase;
+
+/// Minimal common base for both analysis kinds.
+///
+/// Not subclassed directly — use SummaryAnalysis<...> or
+/// DerivedAnalysis<...> instead.
+class AnalysisBase {
+ friend class AnalysisDriver;
+ friend class SummaryAnalysisBase;
+ friend class DerivedAnalysisBase;
+
+ enum class Kind { Summary, Derived };
+ Kind TheKind;
+
+protected:
+ explicit AnalysisBase(Kind K) : TheKind(K) {}
+
+public:
+ virtual ~AnalysisBase() = default;
+
+ /// Name of this analysis. Equal to ResultT::analysisName() in both typed
+ /// intermediates.
+ virtual AnalysisName analysisName() const = 0;
+
+ /// AnalysisNames of all AnalysisResult dependencies.
+ virtual const std::vector<AnalysisName> &dependencyNames() const = 0;
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSIS_ANALYSISBASE_H
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisDriver.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisDriver.h
new file mode 100644
index 0000000000000..1cc5c18d348b9
--- /dev/null
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisDriver.h
@@ -0,0 +1,91 @@
+//===- AnalysisDriver.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
+//
+//===----------------------------------------------------------------------===//
+//
+// Central orchestrator for whole-program analysis. Takes ownership of an
+// LUSummary, drives all registered analyses in topological dependency order,
+// and returns a WPASuite.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSIS_ANALYSISDRIVER_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSIS_ANALYSISDRIVER_H
+
+#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisRegistry.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/WPASuite.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/EntityLinker/LUSummary.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/Support/Error.h"
+#include <memory>
+#include <vector>
+
+namespace clang::ssaf {
+
+/// Orchestrates whole-program analysis over an LUSummary.
+///
+/// Three run() patterns are supported:
+/// - run() && — all registered analyses; silently skips any whose
+/// entity data is absent or whose dependency was skipped.
+/// Requires an rvalue driver because this exhausts the
+/// LUSummary.
+/// - run(names) — named subset plus transitive dependencies; returns
+/// Expected and fails if any listed name has no
+/// registered analysis or missing entity data.
+/// - run<ResultTs..> — type-safe variant of run(names).
+class AnalysisDriver final {
+public:
+ explicit AnalysisDriver(std::unique_ptr<LUSummary> LU);
+
+ /// Runs all registered analyses in topological dependency order.
+ /// Silently skips analyses with absent entity data or skipped dependencies.
+ ///
+ /// Requires an rvalue driver (std::move(Driver).run()) because this
+ /// exhausts all remaining LUSummary data.
+ [[nodiscard]] llvm::Expected<WPASuite> run() &&;
+
+ /// Runs only the named analyses (plus their transitive dependencies).
+ ///
+ /// Returns an error if any listed AnalysisName has no registered analysis
+ /// or if a required SummaryAnalysis has no matching entity data in the
+ /// LUSummary. The EntityIdTable is copied (not moved) so the driver remains
+ /// usable for subsequent calls.
+ [[nodiscard]] llvm::Expected<WPASuite>
+ run(llvm::ArrayRef<AnalysisName> Names);
+
+ /// Type-safe variant of run(names). Derives names from
+ /// ResultTs::analysisName().
+ template <typename... ResultTs> [[nodiscard]] llvm::Expected<WPASuite> run() {
+ return run({ResultTs::analysisName()...});
+ }
+
+private:
+ std::unique_ptr<LUSummary> LU;
+
+ /// Instantiates all analyses reachable from \p Roots (plus transitive
+ /// dependencies) and returns them in topological order via a single DFS.
+ /// Reports an error on unregistered names or cycles.
+ static llvm::Expected<std::vector<std::unique_ptr<AnalysisBase>>>
+ sortTopologically(llvm::ArrayRef<AnalysisName> Roots);
+
+ /// Executes a topologically-sorted analysis list and returns a WPASuite.
+ /// \p IdTable is moved into the returned WPASuite.
+ llvm::Expected<WPASuite>
+ execute(EntityIdTable IdTable,
+ std::vector<std::unique_ptr<AnalysisBase>> Sorted);
+
+ llvm::Error
+ executeSummaryAnalysis(std::unique_ptr<SummaryAnalysisBase> Summary,
+ WPASuite &Suite);
+
+ llvm::Error
+ executeDerivedAnalysis(std::unique_ptr<DerivedAnalysisBase> Derived,
+ WPASuite &Suite);
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSIS_ANALYSISDRIVER_H
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisRegistry.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisRegistry.h
new file mode 100644
index 0000000000000..e4faba62c070e
--- /dev/null
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisRegistry.h
@@ -0,0 +1,95 @@
+//===- AnalysisRegistry.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
+//
+//===----------------------------------------------------------------------===//
+//
+// Unified registry for both SummaryAnalysis and DerivedAnalysis subclasses.
+//
+// To register an analysis, add a static Add<AnalysisT> in its translation
+// unit:
+//
+// static AnalysisRegistry::Add<MyAnalysis>
+// Registered("One-line description of MyAnalysis");
+//
+// The registry entry name is derived automatically from
+// MyAnalysis::analysisName(), so name-mismatch bugs are impossible.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSIS_ANALYSISREGISTRY_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSIS_ANALYSISREGISTRY_H
+
+#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/DerivedAnalysis.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/SummaryAnalysis.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/AnalysisName.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Support/ErrorBuilder.h"
+#include "llvm/Support/Registry.h"
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+namespace clang::ssaf {
+
+/// Unified registry for SummaryAnalysis and DerivedAnalysis implementations.
+///
+/// Internally uses a single llvm::Registry<AnalysisBase>. The correct kind
+/// is carried by the AnalysisBase::TheKind tag set in each subclass
+/// constructor.
+class AnalysisRegistry {
+ using RegistryT = llvm::Registry<AnalysisBase>;
+
+ AnalysisRegistry() = delete;
+
+public:
+ /// Registers AnalysisT with the unified registry.
+ ///
+ /// The registry entry name is derived automatically from
+ /// AnalysisT::ResultType::analysisName(), so name-mismatch bugs are
+ /// impossible.
+ ///
+ /// Add objects must be declared static at namespace scope.
+ template <typename AnalysisT> struct Add {
+ static_assert(std::is_base_of_v<SummaryAnalysisBase, AnalysisT> ||
+ std::is_base_of_v<DerivedAnalysisBase, AnalysisT>,
+ "AnalysisT must derive from SummaryAnalysis<...> or "
+ "DerivedAnalysis<...>");
+
+ explicit Add(llvm::StringRef Desc)
+ : Name(AnalysisT::ResultType::analysisName().str().str()),
+ Node(Name, Desc) {
+ if (contains(Name)) {
+ ErrorBuilder::fatal("duplicate analysis registration for '{0}'", Name);
+ }
+ analysisNames.push_back(AnalysisT::ResultType::analysisName());
+ }
+
+ Add(const Add &) = delete;
+ Add &operator=(const Add &) = delete;
+
+ private:
+ std::string Name;
+ RegistryT::Add<AnalysisT> Node;
+ };
+
+ /// Returns true if an analysis is registered under \p Name.
+ static bool contains(llvm::StringRef Name);
+
+ /// Returns the names of all registered analyses.
+ static const std::vector<AnalysisName> &names();
+
+ /// Instantiates the analysis registered under \p Name, or returns
+ /// std::nullopt if no such analysis is registered.
+ static std::optional<std::unique_ptr<AnalysisBase>>
+ instantiate(llvm::StringRef Name);
+
+private:
+ static std::vector<AnalysisName> analysisNames;
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSIS_ANALYSISREGISTRY_H
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisResult.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisResult.h
new file mode 100644
index 0000000000000..87d781cff30a8
--- /dev/null
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisResult.h
@@ -0,0 +1,30 @@
+//===- AnalysisResult.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 results produced by AnalysisDriver.
+// Replaces SummaryData. Concrete subclasses carry a static analysisName().
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSIS_ANALYSISRESULT_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSIS_ANALYSISRESULT_H
+
+namespace clang::ssaf {
+
+/// Base class for whole-program analysis results.
+///
+/// Concrete subclasses must provide:
+/// static AnalysisName analysisName();
+class AnalysisResult {
+public:
+ virtual ~AnalysisResult() = default;
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSIS_ANALYSISRESULT_H
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/DerivedAnalysis.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/DerivedAnalysis.h
new file mode 100644
index 0000000000000..7b5695dab7c72
--- /dev/null
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/DerivedAnalysis.h
@@ -0,0 +1,133 @@
+//===- DerivedAnalysis.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
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines DerivedAnalysisBase (type-erased base known to AnalysisDriver) and
+// the typed intermediate DerivedAnalysis<ResultT, DepResultTs...> that
+// concrete analyses inherit from.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSIS_DERIVEDANALYSIS_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSIS_DERIVEDANALYSIS_H
+
+#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisBase.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisResult.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/AnalysisName.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/AnalysisTraits.h"
+#include "llvm/Support/Error.h"
+#include <map>
+#include <memory>
+#include <vector>
+
+namespace clang::ssaf {
+
+class AnalysisDriver;
+
+/// Type-erased base for derived analyses. Known to AnalysisDriver.
+///
+/// Not subclassed directly — use DerivedAnalysis<ResultT, DepResultTs...>.
+/// A derived analysis consumes previously produced AnalysisResult objects
+/// and computes a new one via an initialize/step/finalize lifecycle.
+class DerivedAnalysisBase : public AnalysisBase {
+ friend class AnalysisDriver;
+
+protected:
+ DerivedAnalysisBase() : AnalysisBase(AnalysisBase::Kind::Derived) {}
+
+private:
+ /// Called once with the dependency results before the step() loop.
+ ///
+ /// \param DepResults Immutable results of all declared dependencies, keyed
+ /// by AnalysisName. Guaranteed to contain every name
+ /// returned by dependencyNames().
+ virtual llvm::Error initialize(
+ const std::map<AnalysisName, const AnalysisResult *> &DepResults) = 0;
+
+ /// Performs one pass. Returns true if another pass is needed; false when
+ /// converged.
+ virtual llvm::Expected<bool> step() = 0;
+
+ /// Called after the step() loop converges. Default is a no-op.
+ virtual llvm::Error finalize() { return llvm::Error::success(); }
+
+ /// Transfers ownership of the computed result. Called once after finalize().
+ virtual std::unique_ptr<AnalysisResult> result() && = 0;
+};
+
+/// Typed intermediate that concrete derived analyses inherit from.
+///
+/// Concrete analyses must implement:
+/// llvm::Error initialize(const DepResultTs &...) override;
+/// llvm::Expected<bool> step() override;
+/// and may override finalize().
+///
+/// Dependencies are fixed for the lifetime of the analysis — initialize()
+/// binds them once, step() is called until it returns false, and
+/// finalize() post-processes after convergence.
+template <typename ResultT, typename... DepResultTs>
+class DerivedAnalysis : public DerivedAnalysisBase {
+ static_assert(std::is_base_of_v<AnalysisResult, ResultT>,
+ "ResultT must derive from AnalysisResult");
+ static_assert(HasAnalysisName<ResultT>::value,
+ "ResultT must have a static analysisName() method");
+ static_assert((std::is_base_of_v<AnalysisResult, DepResultTs> && ...),
+ "Every DepResultT must derive from AnalysisResult");
+ static_assert((HasAnalysisName<DepResultTs>::value && ...),
+ "Every DepResultT must have a static analysisName() method");
+
+ std::unique_ptr<ResultT> Result;
+
+public:
+ DerivedAnalysis() : Result(std::make_unique<ResultT>()) {}
+
+ using ResultType = ResultT;
+
+ /// Used by AnalysisRegistry::Add to derive the registry entry name.
+ AnalysisName analysisName() const final { return ResultT::analysisName(); }
+
+ const std::vector<AnalysisName> &dependencyNames() const final {
+ static const std::vector<AnalysisName> Names = {
+ DepResultTs::analysisName()...};
+ return Names;
+ }
+
+ /// Called once with the fixed dependency results before the step() loop.
+ virtual llvm::Error initialize(const DepResultTs &...) = 0;
+
+ /// Performs one step. Returns true if another step is needed; false when
+ /// converged. Single-step analyses always return false.
+ virtual llvm::Expected<bool> step() = 0;
+
+ /// Called after the step() loop converges. Override for post-processing.
+ virtual llvm::Error finalize() { return llvm::Error::success(); }
+
+protected:
+ /// Read-only access to the result being built.
+ const ResultT &result() const & { return *Result; }
+
+ /// Mutable access to the result being built.
+ ResultT &result() & { return *Result; }
+
+private:
+ /// Seals the type-erased base overload, downcasts, and dispatches to the
+ /// typed initialize().
+ llvm::Error
+ initialize(const std::map<AnalysisName, const AnalysisResult *> &Map) final {
+ return initialize(*static_cast<const DepResultTs *>(
+ Map.at(DepResultTs::analysisName()))...);
+ }
+
+ /// Type-erased result extraction for the driver.
+ std::unique_ptr<AnalysisResult> result() && final {
+ return std::move(Result);
+ }
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSIS_DERIVEDANALYSIS_H
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/SummaryAnalysis.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/SummaryAnalysis.h
new file mode 100644
index 0000000000000..3a53ec147db4f
--- /dev/null
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/SummaryAnalysis.h
@@ -0,0 +1,132 @@
+//===- SummaryAnalysis.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
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines SummaryAnalysisBase (type-erased base known to AnalysisDriver) and
+// the typed intermediate SummaryAnalysis<ResultT, EntitySummaryT> that
+// concrete analyses inherit from.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSIS_SUMMARYANALYSIS_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSIS_SUMMARYANALYSIS_H
+
+#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisBase.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisResult.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/AnalysisTraits.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/SummaryName.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
+#include "llvm/Support/Error.h"
+#include <memory>
+
+namespace clang::ssaf {
+
+class AnalysisDriver;
+
+/// Type-erased base for summary analyses. Known to AnalysisDriver.
+///
+/// Not subclassed directly — use SummaryAnalysis<ResultT, EntitySummaryT>.
+/// A summary analysis processes per-entity EntitySummary objects from the
+/// LUSummary one at a time, accumulating whole-program data into an
+/// AnalysisResult.
+class SummaryAnalysisBase : public AnalysisBase {
+ friend class AnalysisDriver;
+
+protected:
+ SummaryAnalysisBase() : AnalysisBase(AnalysisBase::Kind::Summary) {}
+
+public:
+ /// SummaryName of the EntitySummary type this analysis consumes.
+ /// Used by the driver to route entities from the LUSummary.
+ virtual SummaryName summaryName() const = 0;
+
+private:
+ /// Called once before any add() calls. Default is a no-op.
+ virtual llvm::Error initialize() { return llvm::Error::success(); }
+
+ /// Called once per matching entity. The driver retains ownership of the
+ /// summary; multiple SummaryAnalysis instances may receive the same entity.
+ virtual llvm::Error add(EntityId Id, const EntitySummary &Summary) = 0;
+
+ /// Called after all entities have been processed. Default is a no-op.
+ virtual llvm::Error finalize() { return llvm::Error::success(); }
+
+ /// Transfers ownership of the built result. Called once after finalize().
+ /// The rvalue ref-qualifier enforces single use.
+ virtual std::unique_ptr<AnalysisResult> result() && = 0;
+};
+
+/// Typed intermediate that concrete summary analyses inherit from.
+///
+/// Concrete analyses must implement:
+/// llvm::Error add(EntityId Id, const EntitySummaryT &Summary) override;
+/// and may override initialize() and finalize().
+///
+/// The result being built is accessible via result() const & (read-only) and
+/// result() & (mutable) within the analysis implementation.
+template <typename ResultT, typename EntitySummaryT>
+class SummaryAnalysis : public SummaryAnalysisBase {
+ static_assert(std::is_base_of_v<AnalysisResult, ResultT>,
+ "ResultT must derive from AnalysisResult");
+ static_assert(HasAnalysisName<ResultT>::value,
+ "ResultT must have a static analysisName() method");
+ static_assert(std::is_base_of_v<EntitySummary, EntitySummaryT>,
+ "EntitySummaryT must derive from EntitySummary");
+
+ std::unique_ptr<ResultT> Result;
+
+public:
+ SummaryAnalysis() : Result(std::make_unique<ResultT>()) {}
+
+ using ResultType = ResultT;
+
+ /// Used by AnalysisRegistry::Add to derive the registry entry name.
+ AnalysisName analysisName() const final { return ResultT::analysisName(); }
+
+ SummaryName summaryName() const final {
+ return EntitySummaryT::summaryName();
+ }
+
+ const std::vector<AnalysisName> &dependencyNames() const final {
+ static const std::vector<AnalysisName> Empty;
+ return Empty;
+ }
+
+ /// Called once before the first add() call. Override for initialization.
+ virtual llvm::Error initialize() override { return llvm::Error::success(); }
+
+ /// Called once per matching entity. Implement to accumulate data.
+ virtual llvm::Error add(EntityId Id, const EntitySummaryT &Summary) = 0;
+
+ /// Called after all entities have been processed. Override for
+ /// post-processing.
+ virtual llvm::Error finalize() override { return llvm::Error::success(); }
+
+protected:
+ /// Read-only access to the result being built.
+ const ResultT &result() const & { return *Result; }
+
+ /// Mutable access to the result being built.
+ ResultT &result() & { return *Result; }
+
+private:
+ /// Seals the type-erased base overload, downcasts, and dispatches to the
+ /// typed add().
+ llvm::Error add(EntityId Id, const EntitySummary &Summary) final {
+ return add(Id, static_cast<const EntitySummaryT &>(Summary));
+ }
+
+ /// Type-erased result extraction for the driver.
+ std::unique_ptr<AnalysisResult> result() && final {
+ return std::move(Result);
+ }
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSIS_SUMMARYANALYSIS_H
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/WPASuite.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/WPASuite.h
new file mode 100644
index 0000000000000..040f8ea79c0ec
--- /dev/null
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/WPASuite.h
@@ -0,0 +1,92 @@
+//===- WPASuite.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
+//
+//===----------------------------------------------------------------------===//
+//
+// The value returned by AnalysisDriver::run(). Bundles the EntityIdTable
+// (moved from the LUSummary) with the analysis results keyed by AnalysisName.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSIS_WPASUITE_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSIS_WPASUITE_H
+
+#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisResult.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/AnalysisName.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/AnalysisTraits.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityIdTable.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Support/ErrorBuilder.h"
+#include "llvm/Support/Error.h"
+#include <map>
+#include <memory>
+
+namespace clang::ssaf {
+
+class AnalysisDriver;
+
+/// Bundles the EntityIdTable (moved from the LUSummary) and the analysis
+/// results produced by one AnalysisDriver::run() call, keyed by AnalysisName.
+///
+/// This is the natural unit of persistence: entity names and analysis results
+/// are self-contained in one object.
+class WPASuite {
+ friend class AnalysisDriver;
+
+ EntityIdTable IdTable;
+ std::map<AnalysisName, std::unique_ptr<AnalysisResult>> Data;
+
+ WPASuite() = default;
+
+public:
+ /// Returns the EntityIdTable that maps EntityId values to their symbolic
+ /// names. Moved from the LUSummary during AnalysisDriver::run().
+ const EntityIdTable &idTable() const { return IdTable; }
+
+ /// Returns true if a result for \p ResultT is present.
+ template <typename ResultT> [[nodiscard]] bool contains() const {
+ static_assert(std::is_base_of_v<AnalysisResult, ResultT>,
+ "ResultT must derive from AnalysisResult");
+ static_assert(HasAnalysisName<ResultT>::value,
+ "ResultT must have a static analysisName() method");
+
+ return contains(ResultT::analysisName());
+ }
+
+ /// Returns true if a result for \p Name is present.
+ [[nodiscard]] bool contains(AnalysisName Name) const {
+ return Data.find(Name) != Data.end();
+ }
+
+ /// Returns a reference to the result for \p ResultT, or an error if absent.
+ template <typename ResultT> [[nodiscard]] llvm::Expected<ResultT &> get() {
+ static_assert(std::is_base_of_v<AnalysisResult, ResultT>,
+ "ResultT must derive from AnalysisResult");
+ static_assert(HasAnalysisName<ResultT>::value,
+ "ResultT must have a static analysisName() method");
+
+ auto Result = get(ResultT::analysisName());
+ if (!Result) {
+ return Result.takeError();
+ }
+ return static_cast<ResultT &>(*Result);
+ }
+
+ /// Returns a reference to the result for \p Name, or an error if absent.
+ [[nodiscard]] llvm::Expected<AnalysisResult &> get(AnalysisName Name) {
+ auto It = Data.find(Name);
+ if (It == Data.end()) {
+ return ErrorBuilder::create(std::errc::invalid_argument,
+ "no result for analysis '{0}' in WPASuite",
+ Name.str())
+ .build();
+ }
+ return *It->second;
+ }
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSIS_WPASUITE_H
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/EntityLinker/LUSummary.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/EntityLinker/LUSummary.h
index 552fff04a4c01..44e7504009bee 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/EntityLinker/LUSummary.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/EntityLinker/LUSummary.h
@@ -34,6 +34,7 @@ class LUSummary {
friend class LUSummaryConsumer;
friend class SerializationFormat;
friend class TestFixture;
+ friend class AnalysisDriver;
NestedBuildNamespace LUNamespace;
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Model/AnalysisName.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Model/AnalysisName.h
new file mode 100644
index 0000000000000..167d8a8b0485e
--- /dev/null
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Model/AnalysisName.h
@@ -0,0 +1,49 @@
+//===- AnalysisName.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
+//
+//===----------------------------------------------------------------------===//
+//
+// Strong typedef identifying a whole-program analysis and its result type.
+// Distinct from SummaryName, which identifies per-entity EntitySummary types.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_MODEL_ANALYSISNAME_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_MODEL_ANALYSISNAME_H
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/raw_ostream.h"
+#include <string>
+
+namespace clang::ssaf {
+
+/// Uniquely identifies a whole-program analysis and the AnalysisResult it
+/// produces. Used as the key in WPASuite and AnalysisRegistry.
+///
+/// Distinct from SummaryName, which is used by EntitySummary types for routing
+/// through the LUSummary.
+class AnalysisName {
+public:
+ explicit AnalysisName(std::string Name) : Name(std::move(Name)) {}
+
+ bool operator==(const AnalysisName &Other) const {
+ return Name == Other.Name;
+ }
+ bool operator!=(const AnalysisName &Other) const { return !(*this == Other); }
+ bool operator<(const AnalysisName &Other) const { return Name < Other.Name; }
+
+ /// Explicit conversion to the underlying string representation.
+ llvm::StringRef str() const { return Name; }
+
+private:
+ std::string Name;
+};
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const AnalysisName &AN);
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_MODEL_ANALYSISNAME_H
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Model/AnalysisTraits.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Model/AnalysisTraits.h
new file mode 100644
index 0000000000000..888f4a8e6be4a
--- /dev/null
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Model/AnalysisTraits.h
@@ -0,0 +1,33 @@
+//===- AnalysisTraits.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 AnalysisResult subclasses.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_MODEL_ANALYSISTRAITS_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_MODEL_ANALYSISTRAITS_H
+
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/AnalysisName.h"
+#include <type_traits>
+
+namespace clang::ssaf {
+
+/// Type trait that checks whether \p T has a static \c analysisName() method
+/// returning \c AnalysisName. Used to enforce the convention on AnalysisResult
+/// subclasses and analysis classes at instantiation time.
+template <typename T, typename = void>
+struct HasAnalysisName : std::false_type {};
+
+template <typename T>
+struct HasAnalysisName<T, std::void_t<decltype(T::analysisName())>>
+ : std::is_same<decltype(T::analysisName()), AnalysisName> {};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_MODEL_ANALYSISTRAITS_H
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Support/FormatProviders.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Support/FormatProviders.h
index d49fd6cb4a1dc..f50d17d7f035a 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Support/FormatProviders.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Support/FormatProviders.h
@@ -14,6 +14,7 @@
#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_SUPPORT_FORMATPROVIDERS_H
#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_SUPPORT_FORMATPROVIDERS_H
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/AnalysisName.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/BuildNamespace.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityLinkage.h"
@@ -24,6 +25,13 @@
namespace llvm {
+template <> struct format_provider<clang::ssaf::AnalysisName> {
+ static void format(const clang::ssaf::AnalysisName &Val, raw_ostream &OS,
+ StringRef Style) {
+ OS << Val;
+ }
+};
+
template <> struct format_provider<clang::ssaf::EntityId> {
static void format(const clang::ssaf::EntityId &Val, raw_ostream &OS,
StringRef Style) {
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisDriver.cpp b/clang/lib/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisDriver.cpp
new file mode 100644
index 0000000000000..64b496142cd47
--- /dev/null
+++ b/clang/lib/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisDriver.cpp
@@ -0,0 +1,196 @@
+//===- AnalysisDriver.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/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisDriver.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisRegistry.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/DerivedAnalysis.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/SummaryAnalysis.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Support/ErrorBuilder.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/ErrorHandling.h"
+#include <map>
+#include <vector>
+
+using namespace clang;
+using namespace ssaf;
+
+AnalysisDriver::AnalysisDriver(std::unique_ptr<LUSummary> LU)
+ : LU(std::move(LU)) {}
+
+llvm::Expected<std::vector<std::unique_ptr<AnalysisBase>>>
+AnalysisDriver::sortTopologically(llvm::ArrayRef<AnalysisName> Roots) {
+ struct Visitor {
+ enum class State { Unvisited, Visiting, Visited };
+
+ std::map<AnalysisName, State> Marks;
+ std::map<AnalysisName, std::unique_ptr<AnalysisBase>> Analyses;
+ std::vector<std::unique_ptr<AnalysisBase>> Result;
+
+ State getState(const AnalysisName &Name) {
+ auto MarkIt = Marks.find(Name);
+ return MarkIt != Marks.end() ? MarkIt->second : State::Unvisited;
+ }
+
+ void setState(const AnalysisName &Name, State S) { Marks[Name] = S; }
+
+ llvm::Error visit(const AnalysisName &Name) {
+ State S = getState(Name);
+
+ if (S == State::Visited) {
+ return llvm::Error::success();
+ }
+
+ if (S == State::Visiting) {
+ return ErrorBuilder::create(std::errc::invalid_argument,
+ "cycle detected involving analysis '{0}'",
+ Name)
+ .build();
+ }
+
+ if (S == State::Unvisited) {
+ setState(Name, State::Visiting);
+
+ auto V = AnalysisRegistry::instantiate(Name.str());
+ if (!V) {
+ return ErrorBuilder::create(std::errc::invalid_argument,
+ "no analysis registered for '{0}'", Name)
+ .build();
+ }
+
+ const auto &Deps = (*V)->dependencyNames();
+ Analyses[Name] = std::move(*V);
+
+ for (const auto &Dep : Deps) {
+ if (auto Err = visit(Dep)) {
+ return Err;
+ }
+ }
+
+ setState(Name, State::Visited);
+ Result.push_back(std::move(Analyses[Name]));
+ Analyses.erase(Name);
+ return llvm::Error::success();
+ }
+ llvm_unreachable("unhandled State");
+ }
+ };
+
+ Visitor V;
+ for (const auto &Root : Roots) {
+ if (auto Err = V.visit(Root)) {
+ return std::move(Err);
+ }
+ }
+ return std::move(V.Result);
+}
+
+llvm::Error AnalysisDriver::executeSummaryAnalysis(
+ std::unique_ptr<SummaryAnalysisBase> Summary, WPASuite &Suite) {
+ SummaryName SN = Summary->summaryName();
+ auto DataIt = LU->Data.find(SN);
+ if (DataIt == LU->Data.end()) {
+ return ErrorBuilder::create(std::errc::invalid_argument,
+ "no data for analysis '{0}' in LUSummary",
+ Summary->analysisName().str())
+ .build();
+ }
+
+ if (auto Err = Summary->initialize()) {
+ return Err;
+ }
+
+ for (auto &[Id, EntitySummary] : DataIt->second) {
+ if (auto Err = Summary->add(Id, *EntitySummary)) {
+ return Err;
+ }
+ }
+
+ if (auto Err = Summary->finalize()) {
+ return Err;
+ }
+
+ Suite.Data.emplace(Summary->analysisName(), std::move(*Summary).result());
+
+ return llvm::Error::success();
+}
+
+llvm::Error AnalysisDriver::executeDerivedAnalysis(
+ std::unique_ptr<DerivedAnalysisBase> Derived, WPASuite &Suite) {
+ std::map<AnalysisName, const AnalysisResult *> DepMap;
+
+ for (const auto &DepName : Derived->dependencyNames()) {
+ auto It = Suite.Data.find(DepName);
+ if (It == Suite.Data.end()) {
+ ErrorBuilder::fatal("missing dependency '{0}' for analysis '{1}': "
+ "dependency graph is not topologically sorted",
+ DepName.str(), Derived->analysisName().str());
+ }
+ DepMap[DepName] = It->second.get();
+ }
+
+ if (auto Err = Derived->initialize(DepMap)) {
+ return Err;
+ }
+
+ while (true) {
+ auto StepOrErr = Derived->step();
+ if (!StepOrErr) {
+ return StepOrErr.takeError();
+ }
+ if (!*StepOrErr) {
+ break;
+ }
+ }
+
+ if (auto Err = Derived->finalize()) {
+ return Err;
+ }
+
+ Suite.Data.emplace(Derived->analysisName(), std::move(*Derived).result());
+
+ return llvm::Error::success();
+}
+
+llvm::Expected<WPASuite>
+AnalysisDriver::execute(EntityIdTable IdTable,
+ std::vector<std::unique_ptr<AnalysisBase>> Sorted) {
+ WPASuite Suite;
+ Suite.IdTable = std::move(IdTable);
+
+ for (auto &V : Sorted) {
+ if (V->TheKind == AnalysisBase::Kind::Summary) {
+ auto SA = std::unique_ptr<SummaryAnalysisBase>(
+ static_cast<SummaryAnalysisBase *>(V.release()));
+ if (auto Err = executeSummaryAnalysis(std::move(SA), Suite)) {
+ return std::move(Err);
+ }
+ } else {
+ auto DA = std::unique_ptr<DerivedAnalysisBase>(
+ static_cast<DerivedAnalysisBase *>(V.release()));
+ if (auto Err = executeDerivedAnalysis(std::move(DA), Suite)) {
+ return std::move(Err);
+ }
+ }
+ }
+
+ return Suite;
+}
+
+llvm::Expected<WPASuite> AnalysisDriver::run() && {
+ return run(AnalysisRegistry::names());
+}
+
+llvm::Expected<WPASuite>
+AnalysisDriver::run(llvm::ArrayRef<AnalysisName> Names) {
+ auto ExpectedSorted = sortTopologically(Names);
+ if (!ExpectedSorted) {
+ return ExpectedSorted.takeError();
+ }
+
+ return execute(LU->IdTable, std::move(*ExpectedSorted));
+}
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisRegistry.cpp b/clang/lib/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisRegistry.cpp
new file mode 100644
index 0000000000000..aac05fdb08453
--- /dev/null
+++ b/clang/lib/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisRegistry.cpp
@@ -0,0 +1,37 @@
+//===- AnalysisRegistry.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/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisRegistry.h"
+#include "llvm/ADT/STLExtras.h"
+
+using namespace clang;
+using namespace ssaf;
+
+using RegistryT = llvm::Registry<AnalysisBase>;
+
+LLVM_INSTANTIATE_REGISTRY(RegistryT)
+
+std::vector<AnalysisName> AnalysisRegistry::analysisNames;
+
+bool AnalysisRegistry::contains(llvm::StringRef Name) {
+ return llvm::is_contained(analysisNames, AnalysisName(std::string(Name)));
+}
+
+const std::vector<AnalysisName> &AnalysisRegistry::names() {
+ return analysisNames;
+}
+
+std::optional<std::unique_ptr<AnalysisBase>>
+AnalysisRegistry::instantiate(llvm::StringRef Name) {
+ for (const auto &Entry : RegistryT::entries()) {
+ if (Entry.getName() == Name) {
+ return std::unique_ptr<AnalysisBase>(Entry.instantiate());
+ }
+ }
+ return std::nullopt;
+}
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Core/CMakeLists.txt b/clang/lib/ScalableStaticAnalysisFramework/Core/CMakeLists.txt
index 190b9fe400a64..9e9786dae5a07 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Core/CMakeLists.txt
+++ b/clang/lib/ScalableStaticAnalysisFramework/Core/CMakeLists.txt
@@ -4,7 +4,10 @@ set(LLVM_LINK_COMPONENTS
add_clang_library(clangScalableStaticAnalysisFrameworkCore
ASTEntityMapping.cpp
+ Analysis/AnalysisDriver.cpp
+ Analysis/AnalysisRegistry.cpp
EntityLinker/EntityLinker.cpp
+ Model/AnalysisName.cpp
Model/BuildNamespace.cpp
Model/EntityId.cpp
Model/EntityIdTable.cpp
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Core/Model/AnalysisName.cpp b/clang/lib/ScalableStaticAnalysisFramework/Core/Model/AnalysisName.cpp
new file mode 100644
index 0000000000000..95e0c02f9a92f
--- /dev/null
+++ b/clang/lib/ScalableStaticAnalysisFramework/Core/Model/AnalysisName.cpp
@@ -0,0 +1,16 @@
+//===- AnalysisName.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/ScalableStaticAnalysisFramework/Core/Model/AnalysisName.h"
+
+using namespace clang::ssaf;
+
+llvm::raw_ostream &clang::ssaf::operator<<(llvm::raw_ostream &OS,
+ const AnalysisName &AN) {
+ return OS << "AnalysisName(" << AN.str() << ")";
+}
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analysis/AnalysisDriverTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analysis/AnalysisDriverTest.cpp
new file mode 100644
index 0000000000000..436e0da580910
--- /dev/null
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analysis/AnalysisDriverTest.cpp
@@ -0,0 +1,390 @@
+//===- AnalysisDriverTest.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/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisDriver.h"
+#include "../TestFixture.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisRegistry.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisResult.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/SummaryAnalysis.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/WPASuite.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/EntityLinker/LUSummary.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/AnalysisName.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/BuildNamespace.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityLinkage.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/SummaryName.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++;
+ static SummaryName summaryName() { return SummaryName("Analysis1"); }
+ SummaryName getSummaryName() const override {
+ return SummaryName("Analysis1");
+ }
+};
+
+class Analysis2EntitySummary final : public EntitySummary {
+public:
+ int InstanceId = NextSummaryInstanceId++;
+ static SummaryName summaryName() { return SummaryName("Analysis2"); }
+ SummaryName getSummaryName() const override {
+ return SummaryName("Analysis2");
+ }
+};
+
+class Analysis3EntitySummary final : public EntitySummary {
+public:
+ int InstanceId = NextSummaryInstanceId++;
+ static SummaryName summaryName() { return SummaryName("Analysis3"); }
+ SummaryName getSummaryName() const override {
+ return SummaryName("Analysis3");
+ }
+};
+
+class Analysis4EntitySummary final : public EntitySummary {
+public:
+ int InstanceId = NextSummaryInstanceId++;
+ static SummaryName summaryName() { return SummaryName("Analysis4"); }
+ SummaryName getSummaryName() const override {
+ return SummaryName("Analysis4");
+ }
+};
+
+// ---------------------------------------------------------------------------
+// Results
+// ---------------------------------------------------------------------------
+
+class Analysis1Result final : public AnalysisResult {
+public:
+ static AnalysisName analysisName() { return AnalysisName("Analysis1"); }
+ std::vector<std::pair<EntityId, int>> Entries;
+ bool WasFinalized = false;
+};
+
+class Analysis2Result final : public AnalysisResult {
+public:
+ static AnalysisName analysisName() { return AnalysisName("Analysis2"); }
+ std::vector<std::pair<EntityId, int>> Entries;
+ bool WasFinalized = false;
+};
+
+// No analysis or registration for Analysis3. Data for Analysis3 is inserted
+// into the LUSummary to verify the driver silently skips it.
+class Analysis3Result final : public AnalysisResult {
+public:
+ static AnalysisName analysisName() { return AnalysisName("Analysis3"); }
+};
+
+// Analysis4 has a registered analysis but no data is inserted into the
+// LUSummary, so it is skipped and get() returns nullptr.
+class Analysis4Result final : public AnalysisResult {
+public:
+ static AnalysisName analysisName() { return AnalysisName("Analysis4"); }
+ std::vector<std::pair<EntityId, int>> Entries;
+ bool WasFinalized = false;
+};
+
+// ---------------------------------------------------------------------------
+// Analysis destruction flags (reset in SetUp)
+// ---------------------------------------------------------------------------
+
+static bool Analysis1WasDestroyed = false;
+static bool Analysis2WasDestroyed = false;
+static bool Analysis4WasDestroyed = false;
+
+// ---------------------------------------------------------------------------
+// Analyses
+// ---------------------------------------------------------------------------
+
+class Analysis1 final
+ : public SummaryAnalysis<Analysis1Result, Analysis1EntitySummary> {
+public:
+ ~Analysis1() { Analysis1WasDestroyed = true; }
+
+ llvm::Error add(EntityId Id, const Analysis1EntitySummary &S) override {
+ result().Entries.push_back({Id, S.InstanceId});
+ return llvm::Error::success();
+ }
+
+ llvm::Error finalize() override {
+ result().WasFinalized = true;
+ return llvm::Error::success();
+ }
+};
+
+static AnalysisRegistry::Add<Analysis1> RegAnalysis1("Analysis for Analysis1");
+
+class Analysis2 final
+ : public SummaryAnalysis<Analysis2Result, Analysis2EntitySummary> {
+public:
+ ~Analysis2() { Analysis2WasDestroyed = true; }
+
+ llvm::Error add(EntityId Id, const Analysis2EntitySummary &S) override {
+ result().Entries.push_back({Id, S.InstanceId});
+ return llvm::Error::success();
+ }
+
+ llvm::Error finalize() override {
+ result().WasFinalized = true;
+ return llvm::Error::success();
+ }
+};
+
+static AnalysisRegistry::Add<Analysis2> RegAnalysis2("Analysis for Analysis2");
+
+class Analysis4 final
+ : public SummaryAnalysis<Analysis4Result, Analysis4EntitySummary> {
+public:
+ ~Analysis4() { Analysis4WasDestroyed = true; }
+
+ llvm::Error add(EntityId Id, const Analysis4EntitySummary &S) override {
+ result().Entries.push_back({Id, S.InstanceId});
+ return llvm::Error::success();
+ }
+
+ llvm::Error finalize() override {
+ result().WasFinalized = true;
+ return llvm::Error::success();
+ }
+};
+
+static AnalysisRegistry::Add<Analysis4> RegAnalysis4("Analysis for Analysis4");
+
+// ---------------------------------------------------------------------------
+// Fixture
+// ---------------------------------------------------------------------------
+
+class AnalysisDriverTest : public TestFixture {
+protected:
+ static constexpr EntityLinkage ExternalLinkage =
+ EntityLinkage(EntityLinkageType::External);
+
+ void SetUp() override {
+ NextSummaryInstanceId = 0;
+ Analysis1WasDestroyed = false;
+ Analysis2WasDestroyed = false;
+ Analysis4WasDestroyed = 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));
+ }
+
+ 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(AnalysisRegistryTest, AnalysisIsRegistered) {
+ EXPECT_FALSE(AnalysisRegistry::contains("AnalysisNonExisting"));
+ EXPECT_TRUE(AnalysisRegistry::contains("Analysis1"));
+ EXPECT_TRUE(AnalysisRegistry::contains("Analysis2"));
+ EXPECT_TRUE(AnalysisRegistry::contains("Analysis4"));
+}
+
+TEST(AnalysisRegistryTest, AnalysisCanBeInstantiated) {
+ EXPECT_FALSE(
+ AnalysisRegistry::instantiate("AnalysisNonExisting").has_value());
+ EXPECT_TRUE(AnalysisRegistry::instantiate("Analysis1").has_value());
+ EXPECT_TRUE(AnalysisRegistry::instantiate("Analysis2").has_value());
+ EXPECT_TRUE(AnalysisRegistry::instantiate("Analysis4").has_value());
+}
+
+// run() — processes all registered analyses present in the LUSummary.
+// Silently skips data whose analysis is unregistered (Analysis3).
+TEST_F(AnalysisDriverTest, RunAll) {
+ auto LU = makeLUSummary();
+ const auto E1 = addEntity(*LU, "Entity1");
+ const auto E2 = addEntity(*LU, "Entity2");
+ const auto E3 = addEntity(*LU, "Entity3");
+ const auto E4 = addEntity(*LU, "Entity4");
+
+ 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);
+ int s4a = insertSummary<Analysis4EntitySummary>(*LU, "Analysis4", E4);
+
+ // No registered analysis — Analysis3 data silently skipped.
+ (void)insertSummary<Analysis3EntitySummary>(*LU, "Analysis3", E1);
+
+ AnalysisDriver Driver(std::move(LU));
+ auto WPAOrErr = std::move(Driver).run();
+ ASSERT_THAT_EXPECTED(WPAOrErr, llvm::Succeeded());
+
+ {
+ auto R1OrErr = WPAOrErr->get<Analysis1Result>();
+ ASSERT_THAT_EXPECTED(R1OrErr, llvm::Succeeded());
+ EXPECT_EQ(R1OrErr->Entries.size(), 2u);
+ EXPECT_TRUE(hasEntry(R1OrErr->Entries, E1, s1a));
+ EXPECT_TRUE(hasEntry(R1OrErr->Entries, E2, s1b));
+ EXPECT_TRUE(R1OrErr->WasFinalized);
+ EXPECT_TRUE(Analysis1WasDestroyed);
+ }
+
+ {
+ auto R2OrErr = WPAOrErr->get<Analysis2Result>();
+ ASSERT_THAT_EXPECTED(R2OrErr, llvm::Succeeded());
+ EXPECT_EQ(R2OrErr->Entries.size(), 2u);
+ EXPECT_TRUE(hasEntry(R2OrErr->Entries, E2, s2a));
+ EXPECT_TRUE(hasEntry(R2OrErr->Entries, E3, s2b));
+ EXPECT_TRUE(R2OrErr->WasFinalized);
+ EXPECT_TRUE(Analysis2WasDestroyed);
+ }
+
+ {
+ auto R4OrErr = WPAOrErr->get<Analysis4Result>();
+ ASSERT_THAT_EXPECTED(R4OrErr, llvm::Succeeded());
+ EXPECT_EQ(R4OrErr->Entries.size(), 1u);
+ EXPECT_TRUE(hasEntry(R4OrErr->Entries, E4, s4a));
+ EXPECT_TRUE(R4OrErr->WasFinalized);
+ EXPECT_TRUE(Analysis4WasDestroyed);
+ }
+
+ // Unregistered analysis — not present in WPA.
+ EXPECT_THAT_EXPECTED(WPAOrErr->get<Analysis3Result>(), llvm::Failed());
+}
+
+// run(names) — processes only the analyses for the given names.
+TEST_F(AnalysisDriverTest, 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);
+
+ AnalysisDriver Driver(std::move(LU));
+ auto WPAOrErr = Driver.run({AnalysisName("Analysis1")});
+ ASSERT_THAT_EXPECTED(WPAOrErr, llvm::Succeeded());
+
+ // Analysis1 was requested and has data — present.
+ auto R1OrErr = WPAOrErr->get<Analysis1Result>();
+ ASSERT_THAT_EXPECTED(R1OrErr, llvm::Succeeded());
+ EXPECT_EQ(R1OrErr->Entries.size(), 1u);
+ EXPECT_TRUE(hasEntry(R1OrErr->Entries, E1, s1a));
+ EXPECT_TRUE(R1OrErr->WasFinalized);
+
+ // Analysis2 was not requested — not present even though data exists.
+ EXPECT_THAT_EXPECTED(WPAOrErr->get<Analysis2Result>(), llvm::Failed());
+}
+
+// run(names) — error when a requested name has no data in LUSummary.
+TEST_F(AnalysisDriverTest, RunByNameErrorMissingData) {
+ auto LU = makeLUSummary();
+ AnalysisDriver Driver(std::move(LU));
+
+ EXPECT_THAT_EXPECTED(Driver.run({AnalysisName("Analysis1")}), llvm::Failed());
+}
+
+// run(names) — error when a requested name has no registered analysis.
+TEST_F(AnalysisDriverTest, RunByNameErrorMissingAnalysis) {
+ auto LU = makeLUSummary();
+ const auto E1 = addEntity(*LU, "Entity1");
+ insertSummary<Analysis3EntitySummary>(*LU, "Analysis3", E1);
+
+ AnalysisDriver Driver(std::move(LU));
+
+ // Analysis3 has data but no registered analysis.
+ EXPECT_THAT_EXPECTED(Driver.run({AnalysisName("Analysis3")}), llvm::Failed());
+}
+
+// run<ResultTs...>() — type-safe subset.
+TEST_F(AnalysisDriverTest, 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);
+
+ AnalysisDriver Driver(std::move(LU));
+ auto WPAOrErr = Driver.run<Analysis1Result>();
+ ASSERT_THAT_EXPECTED(WPAOrErr, llvm::Succeeded());
+
+ // Analysis1 was requested — present.
+ auto R1OrErr = WPAOrErr->get<Analysis1Result>();
+ ASSERT_THAT_EXPECTED(R1OrErr, llvm::Succeeded());
+ EXPECT_EQ(R1OrErr->Entries.size(), 1u);
+ EXPECT_TRUE(hasEntry(R1OrErr->Entries, E1, s1a));
+ EXPECT_TRUE(R1OrErr->WasFinalized);
+
+ // Analysis2 was not requested — not present even though data exists.
+ EXPECT_THAT_EXPECTED(WPAOrErr->get<Analysis2Result>(), llvm::Failed());
+}
+
+// run<ResultTs...>() — error when a requested type has no data in LUSummary.
+TEST_F(AnalysisDriverTest, RunByTypeErrorMissingData) {
+ auto LU = makeLUSummary();
+ AnalysisDriver Driver(std::move(LU));
+
+ EXPECT_THAT_EXPECTED(Driver.run<Analysis1Result>(), llvm::Failed());
+}
+
+// contains() — present entries return true; absent entries return false.
+TEST_F(AnalysisDriverTest, Contains) {
+ auto LU = makeLUSummary();
+ const auto E1 = addEntity(*LU, "Entity1");
+ insertSummary<Analysis1EntitySummary>(*LU, "Analysis1", E1);
+ insertSummary<Analysis4EntitySummary>(*LU, "Analysis4", E1);
+
+ AnalysisDriver Driver(std::move(LU));
+ auto WPAOrErr = std::move(Driver).run();
+ ASSERT_THAT_EXPECTED(WPAOrErr, llvm::Succeeded());
+
+ EXPECT_TRUE(WPAOrErr->contains<Analysis1Result>());
+ EXPECT_FALSE(WPAOrErr->contains<Analysis2Result>());
+}
+
+} // namespace
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt b/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
index 7652ebb390f86..2fec611718475 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
+++ b/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
@@ -1,5 +1,6 @@
add_distinct_clang_unittest(ClangScalableAnalysisTests
Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+ Analysis/AnalysisDriverTest.cpp
ASTEntityMappingTest.cpp
BuildNamespaceTest.cpp
EntityIdTableTest.cpp
>From 6478cc09e7ff9fe17eeabd45fa2edb4ed09f89a6 Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Mon, 16 Mar 2026 08:04:19 -0700
Subject: [PATCH 02/11] Move
---
.../Core/Analysis/AnalysisBase.h | 2 +-
.../Core/{Model => Analysis}/AnalysisName.h | 6 +++---
.../Core/Analysis/AnalysisRegistry.h | 2 +-
.../Core/{Model => Analysis}/AnalysisTraits.h | 8 ++++----
.../Core/Analysis/DerivedAnalysis.h | 4 ++--
.../Core/Analysis/SummaryAnalysis.h | 2 +-
.../Core/Analysis/WPASuite.h | 4 ++--
.../Core/Support/FormatProviders.h | 2 +-
.../Core/{Model => Analysis}/AnalysisName.cpp | 2 +-
.../ScalableStaticAnalysisFramework/Core/CMakeLists.txt | 2 +-
.../Analysis/AnalysisDriverTest.cpp | 2 +-
11 files changed, 18 insertions(+), 18 deletions(-)
rename clang/include/clang/ScalableStaticAnalysisFramework/Core/{Model => Analysis}/AnalysisName.h (86%)
rename clang/include/clang/ScalableStaticAnalysisFramework/Core/{Model => Analysis}/AnalysisTraits.h (76%)
rename clang/lib/ScalableStaticAnalysisFramework/Core/{Model => Analysis}/AnalysisName.cpp (88%)
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisBase.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisBase.h
index 478a6fa85d4a8..873d21150ce87 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisBase.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisBase.h
@@ -15,7 +15,7 @@
#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSIS_ANALYSISBASE_H
#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSIS_ANALYSISBASE_H
-#include "clang/ScalableStaticAnalysisFramework/Core/Model/AnalysisName.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisName.h"
#include <vector>
namespace clang::ssaf {
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Model/AnalysisName.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisName.h
similarity index 86%
rename from clang/include/clang/ScalableStaticAnalysisFramework/Core/Model/AnalysisName.h
rename to clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisName.h
index 167d8a8b0485e..73ba96ccc594e 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Model/AnalysisName.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisName.h
@@ -11,8 +11,8 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_MODEL_ANALYSISNAME_H
-#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_MODEL_ANALYSISNAME_H
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSIS_ANALYSISNAME_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSIS_ANALYSISNAME_H
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/raw_ostream.h"
@@ -46,4 +46,4 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const AnalysisName &AN);
} // namespace clang::ssaf
-#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_MODEL_ANALYSISNAME_H
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSIS_ANALYSISNAME_H
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisRegistry.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisRegistry.h
index e4faba62c070e..f51d7845b36e9 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisRegistry.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisRegistry.h
@@ -22,9 +22,9 @@
#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSIS_ANALYSISREGISTRY_H
#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSIS_ANALYSISREGISTRY_H
+#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisName.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/DerivedAnalysis.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/SummaryAnalysis.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Model/AnalysisName.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Support/ErrorBuilder.h"
#include "llvm/Support/Registry.h"
#include <memory>
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Model/AnalysisTraits.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisTraits.h
similarity index 76%
rename from clang/include/clang/ScalableStaticAnalysisFramework/Core/Model/AnalysisTraits.h
rename to clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisTraits.h
index 888f4a8e6be4a..ef6a5a56d990a 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Model/AnalysisTraits.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisTraits.h
@@ -10,10 +10,10 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_MODEL_ANALYSISTRAITS_H
-#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_MODEL_ANALYSISTRAITS_H
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSIS_ANALYSISTRAITS_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSIS_ANALYSISTRAITS_H
-#include "clang/ScalableStaticAnalysisFramework/Core/Model/AnalysisName.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisName.h"
#include <type_traits>
namespace clang::ssaf {
@@ -30,4 +30,4 @@ struct HasAnalysisName<T, std::void_t<decltype(T::analysisName())>>
} // namespace clang::ssaf
-#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_MODEL_ANALYSISTRAITS_H
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSIS_ANALYSISTRAITS_H
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/DerivedAnalysis.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/DerivedAnalysis.h
index 7b5695dab7c72..a4de8b186fc76 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/DerivedAnalysis.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/DerivedAnalysis.h
@@ -16,9 +16,9 @@
#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSIS_DERIVEDANALYSIS_H
#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisBase.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisName.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisResult.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Model/AnalysisName.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Model/AnalysisTraits.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisTraits.h"
#include "llvm/Support/Error.h"
#include <map>
#include <memory>
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/SummaryAnalysis.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/SummaryAnalysis.h
index 3a53ec147db4f..2c0f6e7821771 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/SummaryAnalysis.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/SummaryAnalysis.h
@@ -17,7 +17,7 @@
#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisBase.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisResult.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Model/AnalysisTraits.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisTraits.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/SummaryName.h"
#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/WPASuite.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/WPASuite.h
index 040f8ea79c0ec..c1a48bbdd3e2b 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/WPASuite.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/WPASuite.h
@@ -14,9 +14,9 @@
#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSIS_WPASUITE_H
#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSIS_WPASUITE_H
+#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisName.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisResult.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Model/AnalysisName.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Model/AnalysisTraits.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisTraits.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityIdTable.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Support/ErrorBuilder.h"
#include "llvm/Support/Error.h"
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Support/FormatProviders.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Support/FormatProviders.h
index f50d17d7f035a..1f8e34868c1d7 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Support/FormatProviders.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Support/FormatProviders.h
@@ -14,7 +14,7 @@
#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_SUPPORT_FORMATPROVIDERS_H
#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_SUPPORT_FORMATPROVIDERS_H
-#include "clang/ScalableStaticAnalysisFramework/Core/Model/AnalysisName.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisName.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/BuildNamespace.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityLinkage.h"
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Core/Model/AnalysisName.cpp b/clang/lib/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisName.cpp
similarity index 88%
rename from clang/lib/ScalableStaticAnalysisFramework/Core/Model/AnalysisName.cpp
rename to clang/lib/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisName.cpp
index 95e0c02f9a92f..d49f41ab24eb8 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Core/Model/AnalysisName.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisName.cpp
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/ScalableStaticAnalysisFramework/Core/Model/AnalysisName.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisName.h"
using namespace clang::ssaf;
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Core/CMakeLists.txt b/clang/lib/ScalableStaticAnalysisFramework/Core/CMakeLists.txt
index 9e9786dae5a07..7951e77e7c10b 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Core/CMakeLists.txt
+++ b/clang/lib/ScalableStaticAnalysisFramework/Core/CMakeLists.txt
@@ -5,9 +5,9 @@ set(LLVM_LINK_COMPONENTS
add_clang_library(clangScalableStaticAnalysisFrameworkCore
ASTEntityMapping.cpp
Analysis/AnalysisDriver.cpp
+ Analysis/AnalysisName.cpp
Analysis/AnalysisRegistry.cpp
EntityLinker/EntityLinker.cpp
- Model/AnalysisName.cpp
Model/BuildNamespace.cpp
Model/EntityId.cpp
Model/EntityIdTable.cpp
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analysis/AnalysisDriverTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analysis/AnalysisDriverTest.cpp
index 436e0da580910..22554764adf80 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analysis/AnalysisDriverTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analysis/AnalysisDriverTest.cpp
@@ -8,12 +8,12 @@
#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisDriver.h"
#include "../TestFixture.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisName.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisRegistry.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisResult.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/SummaryAnalysis.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/WPASuite.h"
#include "clang/ScalableStaticAnalysisFramework/Core/EntityLinker/LUSummary.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Model/AnalysisName.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/BuildNamespace.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityLinkage.h"
>From 949626d8550924d8987ef836fcf507da2fd9e169 Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Mon, 16 Mar 2026 08:12:38 -0700
Subject: [PATCH 03/11] Fixes
---
.../Core/Analysis/AnalysisBase.h | 2 +-
.../Core/Analysis/AnalysisDriver.h | 16 +++++-----
.../Core/Analysis/AnalysisResult.h | 2 +-
.../Core/Analysis/DerivedAnalysis.h | 16 +++++++---
.../Core/Analysis/SummaryAnalysis.h | 2 +-
.../Core/Analysis/WPASuite.h | 30 +++++++++++++++++++
.../Core/Analysis/AnalysisDriver.cpp | 6 ++--
7 files changed, 57 insertions(+), 17 deletions(-)
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisBase.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisBase.h
index 873d21150ce87..29c46f3c2e544 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisBase.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisBase.h
@@ -26,7 +26,7 @@ class DerivedAnalysisBase;
/// Minimal common base for both analysis kinds.
///
-/// Not subclassed directly — use SummaryAnalysis<...> or
+/// Not subclassed directly -- use SummaryAnalysis<...> or
/// DerivedAnalysis<...> instead.
class AnalysisBase {
friend class AnalysisDriver;
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisDriver.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisDriver.h
index 1cc5c18d348b9..3ac0d64e7de46 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisDriver.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisDriver.h
@@ -28,14 +28,14 @@ namespace clang::ssaf {
/// Orchestrates whole-program analysis over an LUSummary.
///
/// Three run() patterns are supported:
-/// - run() && — all registered analyses; silently skips any whose
-/// entity data is absent or whose dependency was skipped.
-/// Requires an rvalue driver because this exhausts the
-/// LUSummary.
-/// - run(names) — named subset plus transitive dependencies; returns
-/// Expected and fails if any listed name has no
-/// registered analysis or missing entity data.
-/// - run<ResultTs..> — type-safe variant of run(names).
+/// - run() && -- all registered analyses; silently skips any whose
+/// entity data is absent or whose dependency was
+/// skipped. Requires an rvalue driver because this
+/// exhausts the LUSummary.
+/// - run(names) -- named subset plus transitive dependencies; returns
+/// Expected and fails if any listed name has no
+/// registered analysis or missing entity data.
+/// - run<ResultTs..> -- type-safe variant of run(names).
class AnalysisDriver final {
public:
explicit AnalysisDriver(std::unique_ptr<LUSummary> LU);
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisResult.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisResult.h
index 87d781cff30a8..7ac2a9ad7db6a 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisResult.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisResult.h
@@ -7,7 +7,7 @@
//===----------------------------------------------------------------------===//
//
// Base class for all whole-program analysis results produced by AnalysisDriver.
-// Replaces SummaryData. Concrete subclasses carry a static analysisName().
+// Concrete subclasses carry a static analysisName().
//
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/DerivedAnalysis.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/DerivedAnalysis.h
index a4de8b186fc76..ecbf31c70c5fe 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/DerivedAnalysis.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/DerivedAnalysis.h
@@ -20,6 +20,7 @@
#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisResult.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisTraits.h"
#include "llvm/Support/Error.h"
+#include "llvm/Support/ErrorHandling.h"
#include <map>
#include <memory>
#include <vector>
@@ -30,7 +31,7 @@ class AnalysisDriver;
/// Type-erased base for derived analyses. Known to AnalysisDriver.
///
-/// Not subclassed directly — use DerivedAnalysis<ResultT, DepResultTs...>.
+/// Not subclassed directly -- use DerivedAnalysis<ResultT, DepResultTs...>.
/// A derived analysis consumes previously produced AnalysisResult objects
/// and computes a new one via an initialize/step/finalize lifecycle.
class DerivedAnalysisBase : public AnalysisBase {
@@ -66,7 +67,7 @@ class DerivedAnalysisBase : public AnalysisBase {
/// llvm::Expected<bool> step() override;
/// and may override finalize().
///
-/// Dependencies are fixed for the lifetime of the analysis — initialize()
+/// Dependencies are fixed for the lifetime of the analysis: initialize()
/// binds them once, step() is called until it returns false, and
/// finalize() post-processes after convergence.
template <typename ResultT, typename... DepResultTs>
@@ -115,11 +116,18 @@ class DerivedAnalysis : public DerivedAnalysisBase {
private:
/// Seals the type-erased base overload, downcasts, and dispatches to the
- /// typed initialize().
+ /// typed initialize(). All dependencies are guaranteed present by the driver.
llvm::Error
initialize(const std::map<AnalysisName, const AnalysisResult *> &Map) final {
+ auto lookup = [&Map](const AnalysisName &Name) -> const AnalysisResult * {
+ auto It = Map.find(Name);
+ if (It == Map.end())
+ llvm_unreachable("dependency missing from DepResults map; "
+ "dependency graph is not topologically sorted");
+ return It->second;
+ };
return initialize(*static_cast<const DepResultTs *>(
- Map.at(DepResultTs::analysisName()))...);
+ lookup(DepResultTs::analysisName()))...);
}
/// Type-erased result extraction for the driver.
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/SummaryAnalysis.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/SummaryAnalysis.h
index 2c0f6e7821771..138e0e4754b5e 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/SummaryAnalysis.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/SummaryAnalysis.h
@@ -30,7 +30,7 @@ class AnalysisDriver;
/// Type-erased base for summary analyses. Known to AnalysisDriver.
///
-/// Not subclassed directly — use SummaryAnalysis<ResultT, EntitySummaryT>.
+/// Not subclassed directly -- use SummaryAnalysis<ResultT, EntitySummaryT>.
/// A summary analysis processes per-entity EntitySummary objects from the
/// LUSummary one at a time, accumulating whole-program data into an
/// AnalysisResult.
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/WPASuite.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/WPASuite.h
index c1a48bbdd3e2b..33a7124fbacf8 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/WPASuite.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/WPASuite.h
@@ -74,6 +74,22 @@ class WPASuite {
return static_cast<ResultT &>(*Result);
}
+ /// Returns a const reference to the result for \p ResultT, or an error if
+ /// absent.
+ template <typename ResultT>
+ [[nodiscard]] llvm::Expected<const ResultT &> get() const {
+ static_assert(std::is_base_of_v<AnalysisResult, ResultT>,
+ "ResultT must derive from AnalysisResult");
+ static_assert(HasAnalysisName<ResultT>::value,
+ "ResultT must have a static analysisName() method");
+
+ auto Result = get(ResultT::analysisName());
+ if (!Result) {
+ return Result.takeError();
+ }
+ return static_cast<const ResultT &>(*Result);
+ }
+
/// Returns a reference to the result for \p Name, or an error if absent.
[[nodiscard]] llvm::Expected<AnalysisResult &> get(AnalysisName Name) {
auto It = Data.find(Name);
@@ -85,6 +101,20 @@ class WPASuite {
}
return *It->second;
}
+
+ /// Returns a const reference to the result for \p Name, or an error if
+ /// absent.
+ [[nodiscard]] llvm::Expected<const AnalysisResult &>
+ get(AnalysisName Name) const {
+ auto It = Data.find(Name);
+ if (It == Data.end()) {
+ return ErrorBuilder::create(std::errc::invalid_argument,
+ "no result for analysis '{0}' in WPASuite",
+ Name.str())
+ .build();
+ }
+ return *It->second;
+ }
};
} // namespace clang::ssaf
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisDriver.cpp b/clang/lib/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisDriver.cpp
index 64b496142cd47..41fd55ce88c6b 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisDriver.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisDriver.cpp
@@ -114,7 +114,8 @@ llvm::Error AnalysisDriver::executeSummaryAnalysis(
return Err;
}
- Suite.Data.emplace(Summary->analysisName(), std::move(*Summary).result());
+ AnalysisName Name = Summary->analysisName();
+ Suite.Data.emplace(Name, std::move(*Summary).result());
return llvm::Error::success();
}
@@ -151,7 +152,8 @@ llvm::Error AnalysisDriver::executeDerivedAnalysis(
return Err;
}
- Suite.Data.emplace(Derived->analysisName(), std::move(*Derived).result());
+ AnalysisName Name = Derived->analysisName();
+ Suite.Data.emplace(Name, std::move(*Derived).result());
return llvm::Error::success();
}
>From 53f52415c6c7a848b689731558a530a0b673a449 Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Mon, 16 Mar 2026 08:14:26 -0700
Subject: [PATCH 04/11] More Fix
---
.../Core/Support/FormatProviders.h | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Support/FormatProviders.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Support/FormatProviders.h
index 1f8e34868c1d7..6cc816edfd967 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Support/FormatProviders.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Support/FormatProviders.h
@@ -25,13 +25,6 @@
namespace llvm {
-template <> struct format_provider<clang::ssaf::AnalysisName> {
- static void format(const clang::ssaf::AnalysisName &Val, raw_ostream &OS,
- StringRef Style) {
- OS << Val;
- }
-};
-
template <> struct format_provider<clang::ssaf::EntityId> {
static void format(const clang::ssaf::EntityId &Val, raw_ostream &OS,
StringRef Style) {
@@ -88,6 +81,13 @@ template <> struct format_provider<clang::ssaf::SummaryName> {
}
};
+template <> struct format_provider<clang::ssaf::AnalysisName> {
+ static void format(const clang::ssaf::AnalysisName &Val, raw_ostream &OS,
+ StringRef Style) {
+ OS << Val;
+ }
+};
+
} // namespace llvm
#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_SUPPORT_FORMATPROVIDERS_H
>From 5963996bb6734384145a702d5415430e9a761516 Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Mon, 16 Mar 2026 08:36:36 -0700
Subject: [PATCH 05/11] Remove non-const methods
---
.../Core/Analysis/DerivedAnalysis.h | 3 ++-
.../Core/Analysis/WPASuite.h | 26 -------------------
2 files changed, 2 insertions(+), 27 deletions(-)
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/DerivedAnalysis.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/DerivedAnalysis.h
index ecbf31c70c5fe..e8c2f2fb8c188 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/DerivedAnalysis.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/DerivedAnalysis.h
@@ -121,9 +121,10 @@ class DerivedAnalysis : public DerivedAnalysisBase {
initialize(const std::map<AnalysisName, const AnalysisResult *> &Map) final {
auto lookup = [&Map](const AnalysisName &Name) -> const AnalysisResult * {
auto It = Map.find(Name);
- if (It == Map.end())
+ if (It == Map.end()) {
llvm_unreachable("dependency missing from DepResults map; "
"dependency graph is not topologically sorted");
+ }
return It->second;
};
return initialize(*static_cast<const DepResultTs *>(
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/WPASuite.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/WPASuite.h
index 33a7124fbacf8..0e5470495ec23 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/WPASuite.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/WPASuite.h
@@ -60,20 +60,6 @@ class WPASuite {
return Data.find(Name) != Data.end();
}
- /// Returns a reference to the result for \p ResultT, or an error if absent.
- template <typename ResultT> [[nodiscard]] llvm::Expected<ResultT &> get() {
- static_assert(std::is_base_of_v<AnalysisResult, ResultT>,
- "ResultT must derive from AnalysisResult");
- static_assert(HasAnalysisName<ResultT>::value,
- "ResultT must have a static analysisName() method");
-
- auto Result = get(ResultT::analysisName());
- if (!Result) {
- return Result.takeError();
- }
- return static_cast<ResultT &>(*Result);
- }
-
/// Returns a const reference to the result for \p ResultT, or an error if
/// absent.
template <typename ResultT>
@@ -90,18 +76,6 @@ class WPASuite {
return static_cast<const ResultT &>(*Result);
}
- /// Returns a reference to the result for \p Name, or an error if absent.
- [[nodiscard]] llvm::Expected<AnalysisResult &> get(AnalysisName Name) {
- auto It = Data.find(Name);
- if (It == Data.end()) {
- return ErrorBuilder::create(std::errc::invalid_argument,
- "no result for analysis '{0}' in WPASuite",
- Name.str())
- .build();
- }
- return *It->second;
- }
-
/// Returns a const reference to the result for \p Name, or an error if
/// absent.
[[nodiscard]] llvm::Expected<const AnalysisResult &>
>From 01e8017f8d57812c38db1f7ce09e1b85b57e0d7c Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Mon, 16 Mar 2026 18:41:34 -0700
Subject: [PATCH 06/11] Fix
---
.../Core/Analysis/AnalysisDriver.h | 19 ++++---
.../Core/Analysis/AnalysisRegistry.h | 8 +--
.../Core/Analysis/AnalysisDriver.cpp | 57 +++++++++----------
.../Core/Analysis/AnalysisRegistry.cpp | 7 ++-
.../Analysis/AnalysisDriverTest.cpp | 13 +++--
5 files changed, 55 insertions(+), 49 deletions(-)
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisDriver.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisDriver.h
index 3ac0d64e7de46..c17cd584f2434 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisDriver.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisDriver.h
@@ -15,7 +15,6 @@
#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSIS_ANALYSISDRIVER_H
#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSIS_ANALYSISDRIVER_H
-#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisRegistry.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/WPASuite.h"
#include "clang/ScalableStaticAnalysisFramework/Core/EntityLinker/LUSummary.h"
#include "llvm/ADT/ArrayRef.h"
@@ -25,13 +24,18 @@
namespace clang::ssaf {
+class AnalysisBase;
+class SummaryAnalysisBase;
+class DerivedAnalysisBase;
+
/// Orchestrates whole-program analysis over an LUSummary.
///
/// Three run() patterns are supported:
-/// - run() && -- all registered analyses; silently skips any whose
-/// entity data is absent or whose dependency was
-/// skipped. Requires an rvalue driver because this
-/// exhausts the LUSummary.
+/// - run() && -- all registered analyses in topological dependency
+/// order. Returns an error if any registered analysis
+/// has no matching entity data in the LUSummary.
+/// Requires an rvalue driver because this exhausts the
+/// LUSummary.
/// - run(names) -- named subset plus transitive dependencies; returns
/// Expected and fails if any listed name has no
/// registered analysis or missing entity data.
@@ -41,7 +45,8 @@ class AnalysisDriver final {
explicit AnalysisDriver(std::unique_ptr<LUSummary> LU);
/// Runs all registered analyses in topological dependency order.
- /// Silently skips analyses with absent entity data or skipped dependencies.
+ /// Returns an error if any registered analysis has no matching entity data
+ /// in the LUSummary.
///
/// Requires an rvalue driver (std::move(Driver).run()) because this
/// exhausts all remaining LUSummary data.
@@ -69,7 +74,7 @@ class AnalysisDriver final {
/// dependencies) and returns them in topological order via a single DFS.
/// Reports an error on unregistered names or cycles.
static llvm::Expected<std::vector<std::unique_ptr<AnalysisBase>>>
- sortTopologically(llvm::ArrayRef<AnalysisName> Roots);
+ sort(llvm::ArrayRef<AnalysisName> Roots);
/// Executes a topologically-sorted analysis list and returns a WPASuite.
/// \p IdTable is moved into the returned WPASuite.
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisRegistry.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisRegistry.h
index f51d7845b36e9..33734870218b0 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisRegistry.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisRegistry.h
@@ -26,9 +26,9 @@
#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/DerivedAnalysis.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/SummaryAnalysis.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Support/ErrorBuilder.h"
+#include "llvm/Support/Error.h"
#include "llvm/Support/Registry.h"
#include <memory>
-#include <optional>
#include <string>
#include <vector>
@@ -81,9 +81,9 @@ class AnalysisRegistry {
/// Returns the names of all registered analyses.
static const std::vector<AnalysisName> &names();
- /// Instantiates the analysis registered under \p Name, or returns
- /// std::nullopt if no such analysis is registered.
- static std::optional<std::unique_ptr<AnalysisBase>>
+ /// Instantiates the analysis registered under \p Name, or returns an error
+ /// if no such analysis is registered.
+ static llvm::Expected<std::unique_ptr<AnalysisBase>>
instantiate(llvm::StringRef Name);
private:
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisDriver.cpp b/clang/lib/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisDriver.cpp
index 41fd55ce88c6b..b010583a03d4e 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisDriver.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisDriver.cpp
@@ -23,59 +23,45 @@ AnalysisDriver::AnalysisDriver(std::unique_ptr<LUSummary> LU)
: LU(std::move(LU)) {}
llvm::Expected<std::vector<std::unique_ptr<AnalysisBase>>>
-AnalysisDriver::sortTopologically(llvm::ArrayRef<AnalysisName> Roots) {
+AnalysisDriver::sort(llvm::ArrayRef<AnalysisName> Roots) {
struct Visitor {
enum class State { Unvisited, Visiting, Visited };
std::map<AnalysisName, State> Marks;
- std::map<AnalysisName, std::unique_ptr<AnalysisBase>> Analyses;
std::vector<std::unique_ptr<AnalysisBase>> Result;
- State getState(const AnalysisName &Name) {
- auto MarkIt = Marks.find(Name);
- return MarkIt != Marks.end() ? MarkIt->second : State::Unvisited;
- }
-
- void setState(const AnalysisName &Name, State S) { Marks[Name] = S; }
-
llvm::Error visit(const AnalysisName &Name) {
- State S = getState(Name);
-
- if (S == State::Visited) {
+ auto It = Marks.find(Name);
+ switch (It != Marks.end() ? It->second : State::Unvisited) {
+ case State::Visited:
return llvm::Error::success();
- }
- if (S == State::Visiting) {
+ case State::Visiting:
return ErrorBuilder::create(std::errc::invalid_argument,
"cycle detected involving analysis '{0}'",
Name)
.build();
- }
- if (S == State::Unvisited) {
- setState(Name, State::Visiting);
+ case State::Unvisited: {
+ Marks[Name] = State::Visiting;
auto V = AnalysisRegistry::instantiate(Name.str());
if (!V) {
- return ErrorBuilder::create(std::errc::invalid_argument,
- "no analysis registered for '{0}'", Name)
- .build();
+ return V.takeError();
}
- const auto &Deps = (*V)->dependencyNames();
- Analyses[Name] = std::move(*V);
-
- for (const auto &Dep : Deps) {
+ auto Analysis = std::move(*V);
+ for (const auto &Dep : Analysis->dependencyNames()) {
if (auto Err = visit(Dep)) {
return Err;
}
}
- setState(Name, State::Visited);
- Result.push_back(std::move(Analyses[Name]));
- Analyses.erase(Name);
+ Marks[Name] = State::Visited;
+ Result.push_back(std::move(Analysis));
return llvm::Error::success();
}
+ }
llvm_unreachable("unhandled State");
}
};
@@ -165,18 +151,23 @@ AnalysisDriver::execute(EntityIdTable IdTable,
Suite.IdTable = std::move(IdTable);
for (auto &V : Sorted) {
- if (V->TheKind == AnalysisBase::Kind::Summary) {
+ switch (V->TheKind) {
+ case AnalysisBase::Kind::Summary: {
auto SA = std::unique_ptr<SummaryAnalysisBase>(
static_cast<SummaryAnalysisBase *>(V.release()));
if (auto Err = executeSummaryAnalysis(std::move(SA), Suite)) {
return std::move(Err);
}
- } else {
+ break;
+ }
+ case AnalysisBase::Kind::Derived: {
auto DA = std::unique_ptr<DerivedAnalysisBase>(
static_cast<DerivedAnalysisBase *>(V.release()));
if (auto Err = executeDerivedAnalysis(std::move(DA), Suite)) {
return std::move(Err);
}
+ break;
+ }
}
}
@@ -184,12 +175,16 @@ AnalysisDriver::execute(EntityIdTable IdTable,
}
llvm::Expected<WPASuite> AnalysisDriver::run() && {
- return run(AnalysisRegistry::names());
+ auto ExpectedSorted = sort(AnalysisRegistry::names());
+ if (!ExpectedSorted) {
+ return ExpectedSorted.takeError();
+ }
+ return execute(std::move(LU->IdTable), std::move(*ExpectedSorted));
}
llvm::Expected<WPASuite>
AnalysisDriver::run(llvm::ArrayRef<AnalysisName> Names) {
- auto ExpectedSorted = sortTopologically(Names);
+ auto ExpectedSorted = sort(Names);
if (!ExpectedSorted) {
return ExpectedSorted.takeError();
}
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisRegistry.cpp b/clang/lib/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisRegistry.cpp
index aac05fdb08453..e9c5208aa7be6 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisRegistry.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisRegistry.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisRegistry.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Support/ErrorBuilder.h"
#include "llvm/ADT/STLExtras.h"
using namespace clang;
@@ -26,12 +27,14 @@ const std::vector<AnalysisName> &AnalysisRegistry::names() {
return analysisNames;
}
-std::optional<std::unique_ptr<AnalysisBase>>
+llvm::Expected<std::unique_ptr<AnalysisBase>>
AnalysisRegistry::instantiate(llvm::StringRef Name) {
for (const auto &Entry : RegistryT::entries()) {
if (Entry.getName() == Name) {
return std::unique_ptr<AnalysisBase>(Entry.instantiate());
}
}
- return std::nullopt;
+ return ErrorBuilder::create(std::errc::invalid_argument,
+ "no analysis registered for '{0}'", Name)
+ .build();
}
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analysis/AnalysisDriverTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analysis/AnalysisDriverTest.cpp
index 22554764adf80..8e9c34e081403 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analysis/AnalysisDriverTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analysis/AnalysisDriverTest.cpp
@@ -234,11 +234,14 @@ TEST(AnalysisRegistryTest, AnalysisIsRegistered) {
}
TEST(AnalysisRegistryTest, AnalysisCanBeInstantiated) {
- EXPECT_FALSE(
- AnalysisRegistry::instantiate("AnalysisNonExisting").has_value());
- EXPECT_TRUE(AnalysisRegistry::instantiate("Analysis1").has_value());
- EXPECT_TRUE(AnalysisRegistry::instantiate("Analysis2").has_value());
- EXPECT_TRUE(AnalysisRegistry::instantiate("Analysis4").has_value());
+ EXPECT_THAT_EXPECTED(AnalysisRegistry::instantiate("AnalysisNonExisting"),
+ llvm::Failed());
+ EXPECT_THAT_EXPECTED(AnalysisRegistry::instantiate("Analysis1"),
+ llvm::Succeeded());
+ EXPECT_THAT_EXPECTED(AnalysisRegistry::instantiate("Analysis2"),
+ llvm::Succeeded());
+ EXPECT_THAT_EXPECTED(AnalysisRegistry::instantiate("Analysis4"),
+ llvm::Succeeded());
}
// run() — processes all registered analyses present in the LUSummary.
>From 84d650f704fbb0860e95e82540ef90939ab5ee24 Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Mon, 16 Mar 2026 18:52:28 -0700
Subject: [PATCH 07/11] Fix
---
clang/lib/ScalableStaticAnalysisFramework/Core/CMakeLists.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Core/CMakeLists.txt b/clang/lib/ScalableStaticAnalysisFramework/Core/CMakeLists.txt
index 7951e77e7c10b..5ab085c0ef07e 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Core/CMakeLists.txt
+++ b/clang/lib/ScalableStaticAnalysisFramework/Core/CMakeLists.txt
@@ -3,10 +3,10 @@ set(LLVM_LINK_COMPONENTS
)
add_clang_library(clangScalableStaticAnalysisFrameworkCore
- ASTEntityMapping.cpp
Analysis/AnalysisDriver.cpp
Analysis/AnalysisName.cpp
Analysis/AnalysisRegistry.cpp
+ ASTEntityMapping.cpp
EntityLinker/EntityLinker.cpp
Model/BuildNamespace.cpp
Model/EntityId.cpp
>From 3b3ed414e272ded7e807f4a3f10aa0dbbd2dd17e Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Mon, 16 Mar 2026 19:06:36 -0700
Subject: [PATCH 08/11] Fix
---
.../Core/Analysis/DerivedAnalysis.h | 6 ++++--
.../Core/Analysis/SummaryAnalysis.h | 6 ++++--
.../Core/Analysis/WPASuite.h | 4 ++--
3 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/DerivedAnalysis.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/DerivedAnalysis.h
index e8c2f2fb8c188..446778f2fd513 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/DerivedAnalysis.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/DerivedAnalysis.h
@@ -28,6 +28,7 @@
namespace clang::ssaf {
class AnalysisDriver;
+class AnalysisRegistry;
/// Type-erased base for derived analyses. Known to AnalysisDriver.
///
@@ -81,13 +82,14 @@ class DerivedAnalysis : public DerivedAnalysisBase {
static_assert((HasAnalysisName<DepResultTs>::value && ...),
"Every DepResultT must have a static analysisName() method");
+ friend class AnalysisRegistry;
+ using ResultType = ResultT;
+
std::unique_ptr<ResultT> Result;
public:
DerivedAnalysis() : Result(std::make_unique<ResultT>()) {}
- using ResultType = ResultT;
-
/// Used by AnalysisRegistry::Add to derive the registry entry name.
AnalysisName analysisName() const final { return ResultT::analysisName(); }
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/SummaryAnalysis.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/SummaryAnalysis.h
index 138e0e4754b5e..6d6892305d217 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/SummaryAnalysis.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/SummaryAnalysis.h
@@ -27,6 +27,7 @@
namespace clang::ssaf {
class AnalysisDriver;
+class AnalysisRegistry;
/// Type-erased base for summary analyses. Known to AnalysisDriver.
///
@@ -78,13 +79,14 @@ class SummaryAnalysis : public SummaryAnalysisBase {
static_assert(std::is_base_of_v<EntitySummary, EntitySummaryT>,
"EntitySummaryT must derive from EntitySummary");
+ friend class AnalysisRegistry;
+ using ResultType = ResultT;
+
std::unique_ptr<ResultT> Result;
public:
SummaryAnalysis() : Result(std::make_unique<ResultT>()) {}
- using ResultType = ResultT;
-
/// Used by AnalysisRegistry::Add to derive the registry entry name.
AnalysisName analysisName() const final { return ResultT::analysisName(); }
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/WPASuite.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/WPASuite.h
index 0e5470495ec23..ce2d334a15071 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/WPASuite.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/WPASuite.h
@@ -7,7 +7,7 @@
//===----------------------------------------------------------------------===//
//
// The value returned by AnalysisDriver::run(). Bundles the EntityIdTable
-// (moved from the LUSummary) with the analysis results keyed by AnalysisName.
+// with the analysis results keyed by AnalysisName.
//
//===----------------------------------------------------------------------===//
@@ -42,7 +42,7 @@ class WPASuite {
public:
/// Returns the EntityIdTable that maps EntityId values to their symbolic
- /// names. Moved from the LUSummary during AnalysisDriver::run().
+ /// names.
const EntityIdTable &idTable() const { return IdTable; }
/// Returns true if a result for \p ResultT is present.
>From e440a667154c8226c81a0d112c17020676f53d67 Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Mon, 16 Mar 2026 20:44:39 -0700
Subject: [PATCH 09/11] Fix
---
.../Core/Analysis/AnalysisDriver.cpp | 18 +-
.../Analysis/AnalysisDriverTest.cpp | 186 ++++++++++++++++--
2 files changed, 188 insertions(+), 16 deletions(-)
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisDriver.cpp b/clang/lib/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisDriver.cpp
index b010583a03d4e..d510632cf4278 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisDriver.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisDriver.cpp
@@ -11,6 +11,7 @@
#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/DerivedAnalysis.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/SummaryAnalysis.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Support/ErrorBuilder.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorHandling.h"
#include <map>
@@ -28,8 +29,18 @@ AnalysisDriver::sort(llvm::ArrayRef<AnalysisName> Roots) {
enum class State { Unvisited, Visiting, Visited };
std::map<AnalysisName, State> Marks;
+ std::vector<AnalysisName> Path;
std::vector<std::unique_ptr<AnalysisBase>> Result;
+ std::string formatCycle(const AnalysisName &CycleEntry) const {
+ auto CycleBegin = llvm::find(Path, CycleEntry);
+ std::string Cycle;
+ llvm::raw_string_ostream OS(Cycle);
+ llvm::interleave(llvm::make_range(CycleBegin, Path.end()), OS, " -> ");
+ OS << " -> " << CycleEntry;
+ return Cycle;
+ }
+
llvm::Error visit(const AnalysisName &Name) {
auto It = Marks.find(Name);
switch (It != Marks.end() ? It->second : State::Unvisited) {
@@ -38,26 +49,29 @@ AnalysisDriver::sort(llvm::ArrayRef<AnalysisName> Roots) {
case State::Visiting:
return ErrorBuilder::create(std::errc::invalid_argument,
- "cycle detected involving analysis '{0}'",
- Name)
+ "cycle detected: {0}", formatCycle(Name))
.build();
case State::Unvisited: {
Marks[Name] = State::Visiting;
+ Path.push_back(Name);
auto V = AnalysisRegistry::instantiate(Name.str());
if (!V) {
+ Path.pop_back();
return V.takeError();
}
auto Analysis = std::move(*V);
for (const auto &Dep : Analysis->dependencyNames()) {
if (auto Err = visit(Dep)) {
+ Path.pop_back();
return Err;
}
}
Marks[Name] = State::Visited;
+ Path.pop_back();
Result.push_back(std::move(Analysis));
return llvm::Error::success();
}
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analysis/AnalysisDriverTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analysis/AnalysisDriverTest.cpp
index 8e9c34e081403..f2bab5b40d914 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analysis/AnalysisDriverTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analysis/AnalysisDriverTest.cpp
@@ -11,6 +11,7 @@
#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisName.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisRegistry.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisResult.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/DerivedAnalysis.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/SummaryAnalysis.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/WPASuite.h"
#include "clang/ScalableStaticAnalysisFramework/Core/EntityLinker/LUSummary.h"
@@ -23,6 +24,7 @@
#include "llvm/Testing/Support/Error.h"
#include "gtest/gtest.h"
#include <memory>
+#include <string>
#include <utility>
#include <vector>
@@ -85,6 +87,7 @@ class Analysis1Result final : public AnalysisResult {
public:
static AnalysisName analysisName() { return AnalysisName("Analysis1"); }
std::vector<std::pair<EntityId, int>> Entries;
+ bool WasInitialized = false;
bool WasFinalized = false;
};
@@ -92,6 +95,7 @@ class Analysis2Result final : public AnalysisResult {
public:
static AnalysisName analysisName() { return AnalysisName("Analysis2"); }
std::vector<std::pair<EntityId, int>> Entries;
+ bool WasInitialized = false;
bool WasFinalized = false;
};
@@ -108,9 +112,36 @@ class Analysis4Result final : public AnalysisResult {
public:
static AnalysisName analysisName() { return AnalysisName("Analysis4"); }
std::vector<std::pair<EntityId, int>> Entries;
+ bool WasInitialized = false;
bool WasFinalized = false;
};
+// Analysis5 is a derived analysis that depends on Analysis1, Analysis2, and
+// Analysis4. It verifies that the driver passes dependency results to
+// initialize() and that the initialize/step/finalize lifecycle is respected.
+class Analysis5Result final : public AnalysisResult {
+public:
+ static AnalysisName analysisName() { return AnalysisName("Analysis5"); }
+ std::vector<std::string> CallSequence;
+ std::vector<std::pair<EntityId, int>> Analysis1Entries;
+ std::vector<std::pair<EntityId, int>> Analysis2Entries;
+ std::vector<std::pair<EntityId, int>> Analysis4Entries;
+};
+
+// CycleA and CycleB form a dependency cycle (CycleA → CycleB → CycleA).
+// Registered solely to exercise cycle detection in AnalysisDriver::sort().
+// initialize() and step() are unreachable stubs — the cycle is caught before
+// any analysis executes.
+class CycleAResult final : public AnalysisResult {
+public:
+ static AnalysisName analysisName() { return AnalysisName("CycleA"); }
+};
+
+class CycleBResult final : public AnalysisResult {
+public:
+ static AnalysisName analysisName() { return AnalysisName("CycleB"); }
+};
+
// ---------------------------------------------------------------------------
// Analysis destruction flags (reset in SetUp)
// ---------------------------------------------------------------------------
@@ -118,6 +149,7 @@ class Analysis4Result final : public AnalysisResult {
static bool Analysis1WasDestroyed = false;
static bool Analysis2WasDestroyed = false;
static bool Analysis4WasDestroyed = false;
+static bool Analysis5WasDestroyed = false;
// ---------------------------------------------------------------------------
// Analyses
@@ -128,6 +160,11 @@ class Analysis1 final
public:
~Analysis1() { Analysis1WasDestroyed = true; }
+ llvm::Error initialize() override {
+ result().WasInitialized = true;
+ return llvm::Error::success();
+ }
+
llvm::Error add(EntityId Id, const Analysis1EntitySummary &S) override {
result().Entries.push_back({Id, S.InstanceId});
return llvm::Error::success();
@@ -146,6 +183,11 @@ class Analysis2 final
public:
~Analysis2() { Analysis2WasDestroyed = true; }
+ llvm::Error initialize() override {
+ result().WasInitialized = true;
+ return llvm::Error::success();
+ }
+
llvm::Error add(EntityId Id, const Analysis2EntitySummary &S) override {
result().Entries.push_back({Id, S.InstanceId});
return llvm::Error::success();
@@ -159,11 +201,18 @@ class Analysis2 final
static AnalysisRegistry::Add<Analysis2> RegAnalysis2("Analysis for Analysis2");
+// No Analysis3 or registration for Analysis3.
+
class Analysis4 final
: public SummaryAnalysis<Analysis4Result, Analysis4EntitySummary> {
public:
~Analysis4() { Analysis4WasDestroyed = true; }
+ llvm::Error initialize() override {
+ result().WasInitialized = true;
+ return llvm::Error::success();
+ }
+
llvm::Error add(EntityId Id, const Analysis4EntitySummary &S) override {
result().Entries.push_back({Id, S.InstanceId});
return llvm::Error::success();
@@ -177,6 +226,56 @@ class Analysis4 final
static AnalysisRegistry::Add<Analysis4> RegAnalysis4("Analysis for Analysis4");
+class Analysis5 final
+ : public DerivedAnalysis<Analysis5Result, Analysis1Result, Analysis2Result,
+ Analysis4Result> {
+ int StepCount = 0;
+
+public:
+ ~Analysis5() { Analysis5WasDestroyed = true; }
+
+ llvm::Error initialize(const Analysis1Result &R1, const Analysis2Result &R2,
+ const Analysis4Result &R4) override {
+ result().CallSequence.push_back("initialize");
+ result().Analysis1Entries = R1.Entries;
+ result().Analysis2Entries = R2.Entries;
+ result().Analysis4Entries = R4.Entries;
+ return llvm::Error::success();
+ }
+
+ llvm::Expected<bool> step() override {
+ result().CallSequence.push_back("step");
+ return ++StepCount < 2;
+ }
+
+ llvm::Error finalize() override {
+ result().CallSequence.push_back("finalize");
+ return llvm::Error::success();
+ }
+};
+
+static AnalysisRegistry::Add<Analysis5> RegAnalysis5("Analysis for Analysis5");
+
+class CycleA final : public DerivedAnalysis<CycleAResult, CycleBResult> {
+public:
+ llvm::Error initialize(const CycleBResult &) override {
+ return llvm::Error::success();
+ }
+ llvm::Expected<bool> step() override { return false; }
+};
+
+static AnalysisRegistry::Add<CycleA> RegCycleA("Cyclic analysis A (test only)");
+
+class CycleB final : public DerivedAnalysis<CycleBResult, CycleAResult> {
+public:
+ llvm::Error initialize(const CycleAResult &) override {
+ return llvm::Error::success();
+ }
+ llvm::Expected<bool> step() override { return false; }
+};
+
+static AnalysisRegistry::Add<CycleB> RegCycleB("Cyclic analysis B (test only)");
+
// ---------------------------------------------------------------------------
// Fixture
// ---------------------------------------------------------------------------
@@ -191,6 +290,7 @@ class AnalysisDriverTest : public TestFixture {
Analysis1WasDestroyed = false;
Analysis2WasDestroyed = false;
Analysis4WasDestroyed = false;
+ Analysis5WasDestroyed = false;
}
std::unique_ptr<LUSummary> makeLUSummary() {
@@ -227,25 +327,36 @@ class AnalysisDriverTest : public TestFixture {
// ---------------------------------------------------------------------------
TEST(AnalysisRegistryTest, AnalysisIsRegistered) {
- EXPECT_FALSE(AnalysisRegistry::contains("AnalysisNonExisting"));
EXPECT_TRUE(AnalysisRegistry::contains("Analysis1"));
EXPECT_TRUE(AnalysisRegistry::contains("Analysis2"));
+ EXPECT_FALSE(AnalysisRegistry::contains("Analysis3"));
EXPECT_TRUE(AnalysisRegistry::contains("Analysis4"));
+ EXPECT_TRUE(AnalysisRegistry::contains("Analysis5"));
+ EXPECT_TRUE(AnalysisRegistry::contains("CycleA"));
+ EXPECT_TRUE(AnalysisRegistry::contains("CycleB"));
}
TEST(AnalysisRegistryTest, AnalysisCanBeInstantiated) {
EXPECT_THAT_EXPECTED(AnalysisRegistry::instantiate("AnalysisNonExisting"),
- llvm::Failed());
+ llvm::FailedWithMessage(
+ "no analysis registered for 'AnalysisNonExisting'"));
EXPECT_THAT_EXPECTED(AnalysisRegistry::instantiate("Analysis1"),
llvm::Succeeded());
EXPECT_THAT_EXPECTED(AnalysisRegistry::instantiate("Analysis2"),
llvm::Succeeded());
EXPECT_THAT_EXPECTED(AnalysisRegistry::instantiate("Analysis4"),
llvm::Succeeded());
+ EXPECT_THAT_EXPECTED(AnalysisRegistry::instantiate("Analysis5"),
+ llvm::Succeeded());
+ EXPECT_THAT_EXPECTED(AnalysisRegistry::instantiate("CycleA"),
+ llvm::Succeeded());
+ EXPECT_THAT_EXPECTED(AnalysisRegistry::instantiate("CycleB"),
+ llvm::Succeeded());
}
-// run() — processes all registered analyses present in the LUSummary.
-// Silently skips data whose analysis is unregistered (Analysis3).
+// run<T...>() — processes the non-cyclic analyses in topological order.
+// CycleA and CycleB are excluded because they form a cycle; run() && would
+// error on them, so the type-safe subset overload is used here instead.
TEST_F(AnalysisDriverTest, RunAll) {
auto LU = makeLUSummary();
const auto E1 = addEntity(*LU, "Entity1");
@@ -263,7 +374,8 @@ TEST_F(AnalysisDriverTest, RunAll) {
(void)insertSummary<Analysis3EntitySummary>(*LU, "Analysis3", E1);
AnalysisDriver Driver(std::move(LU));
- auto WPAOrErr = std::move(Driver).run();
+ auto WPAOrErr = Driver.run<Analysis1Result, Analysis2Result, Analysis4Result,
+ Analysis5Result>();
ASSERT_THAT_EXPECTED(WPAOrErr, llvm::Succeeded());
{
@@ -272,6 +384,7 @@ TEST_F(AnalysisDriverTest, RunAll) {
EXPECT_EQ(R1OrErr->Entries.size(), 2u);
EXPECT_TRUE(hasEntry(R1OrErr->Entries, E1, s1a));
EXPECT_TRUE(hasEntry(R1OrErr->Entries, E2, s1b));
+ EXPECT_TRUE(R1OrErr->WasInitialized);
EXPECT_TRUE(R1OrErr->WasFinalized);
EXPECT_TRUE(Analysis1WasDestroyed);
}
@@ -282,6 +395,7 @@ TEST_F(AnalysisDriverTest, RunAll) {
EXPECT_EQ(R2OrErr->Entries.size(), 2u);
EXPECT_TRUE(hasEntry(R2OrErr->Entries, E2, s2a));
EXPECT_TRUE(hasEntry(R2OrErr->Entries, E3, s2b));
+ EXPECT_TRUE(R2OrErr->WasInitialized);
EXPECT_TRUE(R2OrErr->WasFinalized);
EXPECT_TRUE(Analysis2WasDestroyed);
}
@@ -291,12 +405,32 @@ TEST_F(AnalysisDriverTest, RunAll) {
ASSERT_THAT_EXPECTED(R4OrErr, llvm::Succeeded());
EXPECT_EQ(R4OrErr->Entries.size(), 1u);
EXPECT_TRUE(hasEntry(R4OrErr->Entries, E4, s4a));
+ EXPECT_TRUE(R4OrErr->WasInitialized);
EXPECT_TRUE(R4OrErr->WasFinalized);
EXPECT_TRUE(Analysis4WasDestroyed);
}
+ {
+ auto R5OrErr = WPAOrErr->get<Analysis5Result>();
+ ASSERT_THAT_EXPECTED(R5OrErr, llvm::Succeeded());
+ EXPECT_EQ(
+ R5OrErr->CallSequence,
+ (std::vector<std::string>{"initialize", "step", "step", "finalize"}));
+ EXPECT_EQ(R5OrErr->Analysis1Entries.size(), 2u);
+ EXPECT_TRUE(hasEntry(R5OrErr->Analysis1Entries, E1, s1a));
+ EXPECT_TRUE(hasEntry(R5OrErr->Analysis1Entries, E2, s1b));
+ EXPECT_EQ(R5OrErr->Analysis2Entries.size(), 2u);
+ EXPECT_TRUE(hasEntry(R5OrErr->Analysis2Entries, E2, s2a));
+ EXPECT_TRUE(hasEntry(R5OrErr->Analysis2Entries, E3, s2b));
+ EXPECT_EQ(R5OrErr->Analysis4Entries.size(), 1u);
+ EXPECT_TRUE(hasEntry(R5OrErr->Analysis4Entries, E4, s4a));
+ EXPECT_TRUE(Analysis5WasDestroyed);
+ }
+
// Unregistered analysis — not present in WPA.
- EXPECT_THAT_EXPECTED(WPAOrErr->get<Analysis3Result>(), llvm::Failed());
+ EXPECT_THAT_EXPECTED(WPAOrErr->get<Analysis3Result>(),
+ llvm::FailedWithMessage(
+ "no result for analysis 'Analysis3' in WPASuite"));
}
// run(names) — processes only the analyses for the given names.
@@ -317,10 +451,13 @@ TEST_F(AnalysisDriverTest, RunByName) {
ASSERT_THAT_EXPECTED(R1OrErr, llvm::Succeeded());
EXPECT_EQ(R1OrErr->Entries.size(), 1u);
EXPECT_TRUE(hasEntry(R1OrErr->Entries, E1, s1a));
+ EXPECT_TRUE(R1OrErr->WasInitialized);
EXPECT_TRUE(R1OrErr->WasFinalized);
// Analysis2 was not requested — not present even though data exists.
- EXPECT_THAT_EXPECTED(WPAOrErr->get<Analysis2Result>(), llvm::Failed());
+ EXPECT_THAT_EXPECTED(WPAOrErr->get<Analysis2Result>(),
+ llvm::FailedWithMessage(
+ "no result for analysis 'Analysis2' in WPASuite"));
}
// run(names) — error when a requested name has no data in LUSummary.
@@ -328,7 +465,9 @@ TEST_F(AnalysisDriverTest, RunByNameErrorMissingData) {
auto LU = makeLUSummary();
AnalysisDriver Driver(std::move(LU));
- EXPECT_THAT_EXPECTED(Driver.run({AnalysisName("Analysis1")}), llvm::Failed());
+ EXPECT_THAT_EXPECTED(
+ Driver.run({AnalysisName("Analysis1")}),
+ llvm::FailedWithMessage("no data for analysis 'Analysis1' in LUSummary"));
}
// run(names) — error when a requested name has no registered analysis.
@@ -340,7 +479,9 @@ TEST_F(AnalysisDriverTest, RunByNameErrorMissingAnalysis) {
AnalysisDriver Driver(std::move(LU));
// Analysis3 has data but no registered analysis.
- EXPECT_THAT_EXPECTED(Driver.run({AnalysisName("Analysis3")}), llvm::Failed());
+ EXPECT_THAT_EXPECTED(
+ Driver.run({AnalysisName("Analysis3")}),
+ llvm::FailedWithMessage("no analysis registered for 'Analysis3'"));
}
// run<ResultTs...>() — type-safe subset.
@@ -361,10 +502,13 @@ TEST_F(AnalysisDriverTest, RunByType) {
ASSERT_THAT_EXPECTED(R1OrErr, llvm::Succeeded());
EXPECT_EQ(R1OrErr->Entries.size(), 1u);
EXPECT_TRUE(hasEntry(R1OrErr->Entries, E1, s1a));
+ EXPECT_TRUE(R1OrErr->WasInitialized);
EXPECT_TRUE(R1OrErr->WasFinalized);
// Analysis2 was not requested — not present even though data exists.
- EXPECT_THAT_EXPECTED(WPAOrErr->get<Analysis2Result>(), llvm::Failed());
+ EXPECT_THAT_EXPECTED(WPAOrErr->get<Analysis2Result>(),
+ llvm::FailedWithMessage(
+ "no result for analysis 'Analysis2' in WPASuite"));
}
// run<ResultTs...>() — error when a requested type has no data in LUSummary.
@@ -372,7 +516,9 @@ TEST_F(AnalysisDriverTest, RunByTypeErrorMissingData) {
auto LU = makeLUSummary();
AnalysisDriver Driver(std::move(LU));
- EXPECT_THAT_EXPECTED(Driver.run<Analysis1Result>(), llvm::Failed());
+ EXPECT_THAT_EXPECTED(
+ Driver.run<Analysis1Result>(),
+ llvm::FailedWithMessage("no data for analysis 'Analysis1' in LUSummary"));
}
// contains() — present entries return true; absent entries return false.
@@ -380,14 +526,26 @@ TEST_F(AnalysisDriverTest, Contains) {
auto LU = makeLUSummary();
const auto E1 = addEntity(*LU, "Entity1");
insertSummary<Analysis1EntitySummary>(*LU, "Analysis1", E1);
+ insertSummary<Analysis2EntitySummary>(*LU, "Analysis2", E1);
insertSummary<Analysis4EntitySummary>(*LU, "Analysis4", E1);
AnalysisDriver Driver(std::move(LU));
- auto WPAOrErr = std::move(Driver).run();
+ auto WPAOrErr = Driver.run<Analysis1Result, Analysis2Result, Analysis4Result,
+ Analysis5Result>();
ASSERT_THAT_EXPECTED(WPAOrErr, llvm::Succeeded());
-
EXPECT_TRUE(WPAOrErr->contains<Analysis1Result>());
- EXPECT_FALSE(WPAOrErr->contains<Analysis2Result>());
+ // Analysis3 has no registered analysis — never present in WPA.
+ EXPECT_FALSE(WPAOrErr->contains<Analysis3Result>());
+}
+
+// run() && — errors when the registry contains a dependency cycle.
+TEST_F(AnalysisDriverTest, CycleDetected) {
+ auto LU = makeLUSummary();
+ AnalysisDriver Driver(std::move(LU));
+ EXPECT_THAT_EXPECTED(
+ std::move(Driver).run(),
+ llvm::FailedWithMessage("cycle detected: AnalysisName(CycleA) -> "
+ "AnalysisName(CycleB) -> AnalysisName(CycleA)"));
}
} // namespace
>From 0d8c983591d11f32bdff9bfe8a8713dcc02e352f Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Mon, 16 Mar 2026 21:23:40 -0700
Subject: [PATCH 10/11] Fix
---
.../Core/Analysis/AnalysisRegistry.h | 9 +++++++--
.../Core/Analysis/AnalysisRegistry.cpp | 10 +++++++---
2 files changed, 14 insertions(+), 5 deletions(-)
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisRegistry.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisRegistry.h
index 33734870218b0..4ed5999bda477 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisRegistry.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisRegistry.h
@@ -64,7 +64,7 @@ class AnalysisRegistry {
if (contains(Name)) {
ErrorBuilder::fatal("duplicate analysis registration for '{0}'", Name);
}
- analysisNames.push_back(AnalysisT::ResultType::analysisName());
+ getAnalysisNames().push_back(AnalysisT::ResultType::analysisName());
}
Add(const Add &) = delete;
@@ -87,7 +87,12 @@ class AnalysisRegistry {
instantiate(llvm::StringRef Name);
private:
- static std::vector<AnalysisName> analysisNames;
+ /// Returns the global list of registered analysis names.
+ ///
+ /// Uses a function-local static to avoid static initialization order
+ /// fiasco: Add<T> objects in other translation units may push names before
+ /// a plain static data member could be constructed.
+ static std::vector<AnalysisName> &getAnalysisNames();
};
} // namespace clang::ssaf
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisRegistry.cpp b/clang/lib/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisRegistry.cpp
index e9c5208aa7be6..91e95fa8b2379 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisRegistry.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisRegistry.cpp
@@ -17,14 +17,18 @@ using RegistryT = llvm::Registry<AnalysisBase>;
LLVM_INSTANTIATE_REGISTRY(RegistryT)
-std::vector<AnalysisName> AnalysisRegistry::analysisNames;
+std::vector<AnalysisName> &AnalysisRegistry::getAnalysisNames() {
+ static std::vector<AnalysisName> Names;
+ return Names;
+}
bool AnalysisRegistry::contains(llvm::StringRef Name) {
- return llvm::is_contained(analysisNames, AnalysisName(std::string(Name)));
+ return llvm::is_contained(getAnalysisNames(),
+ AnalysisName(std::string(Name)));
}
const std::vector<AnalysisName> &AnalysisRegistry::names() {
- return analysisNames;
+ return getAnalysisNames();
}
llvm::Expected<std::unique_ptr<AnalysisBase>>
>From 5e2fd99672ccac872a924bd60e765d91ab290881 Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Mon, 16 Mar 2026 21:50:01 -0700
Subject: [PATCH 11/11] More fixes
---
.../Core/Analysis/DerivedAnalysis.h | 7 ++++---
.../Core/EntityLinker/LUSummary.h | 2 +-
.../Core/Analysis/AnalysisDriver.cpp | 4 ++--
3 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/DerivedAnalysis.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/DerivedAnalysis.h
index 446778f2fd513..7066f3451a3d0 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/DerivedAnalysis.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analysis/DerivedAnalysis.h
@@ -19,8 +19,8 @@
#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisName.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisResult.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisTraits.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Support/ErrorBuilder.h"
#include "llvm/Support/Error.h"
-#include "llvm/Support/ErrorHandling.h"
#include <map>
#include <memory>
#include <vector>
@@ -124,8 +124,9 @@ class DerivedAnalysis : public DerivedAnalysisBase {
auto lookup = [&Map](const AnalysisName &Name) -> const AnalysisResult * {
auto It = Map.find(Name);
if (It == Map.end()) {
- llvm_unreachable("dependency missing from DepResults map; "
- "dependency graph is not topologically sorted");
+ ErrorBuilder::fatal("dependency '{0}' missing from DepResults map; "
+ "dependency graph is not topologically sorted",
+ Name);
}
return It->second;
};
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/EntityLinker/LUSummary.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/EntityLinker/LUSummary.h
index 44e7504009bee..a36002006430c 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/EntityLinker/LUSummary.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/EntityLinker/LUSummary.h
@@ -31,10 +31,10 @@ namespace clang::ssaf {
/// together. It contains deduplicated entities with their linkage information
/// and the merged entity summaries.
class LUSummary {
+ friend class AnalysisDriver;
friend class LUSummaryConsumer;
friend class SerializationFormat;
friend class TestFixture;
- friend class AnalysisDriver;
NestedBuildNamespace LUNamespace;
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisDriver.cpp b/clang/lib/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisDriver.cpp
index d510632cf4278..9572304d8d8c5 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisDriver.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Core/Analysis/AnalysisDriver.cpp
@@ -96,7 +96,7 @@ llvm::Error AnalysisDriver::executeSummaryAnalysis(
if (DataIt == LU->Data.end()) {
return ErrorBuilder::create(std::errc::invalid_argument,
"no data for analysis '{0}' in LUSummary",
- Summary->analysisName().str())
+ Summary->analysisName())
.build();
}
@@ -129,7 +129,7 @@ llvm::Error AnalysisDriver::executeDerivedAnalysis(
if (It == Suite.Data.end()) {
ErrorBuilder::fatal("missing dependency '{0}' for analysis '{1}': "
"dependency graph is not topologically sorted",
- DepName.str(), Derived->analysisName().str());
+ DepName, Derived->analysisName());
}
DepMap[DepName] = It->second.get();
}
More information about the cfe-commits
mailing list