Author: Aviral Goel Date: 2026-03-20T15:40:28Z New Revision: c6ba0e00161e7d1f1a142a3827f005f3e86aae49
URL: https://github.com/llvm/llvm-project/commit/c6ba0e00161e7d1f1a142a3827f005f3e86aae49 DIFF: https://github.com/llvm/llvm-project/commit/c6ba0e00161e7d1f1a142a3827f005f3e86aae49.diff LOG: [clang][ssaf] Add whole-program analysis execution layer This change Introduces the analysis execution layer of the Scalable Static Analysis Framework. This layer bridges the LUSummary (entity summaries from the linker phase) to WPASuite (the collection of whole-program analysis results). It introduces the following classes: - `AnalysisResult` — base class for all per-analysis results. - `AnalysisBase` — minimal common base with a private kind tag used by the driver for dispatch - `SummaryAnalysis<ResultT, EntitySummaryT>` — processes per-entity EntitySummary objects from LUSummary. - `DerivedAnalysis<ResultT, DepResultTs...>` — consumes previously produced AnalysisResult objects. - `AnalysisRegistry` — unified `llvm::Registry` backed registry to register analyses. - `WPASuite` — Container bundling `EntityIdTable` and all `AnalysisResult` objects from a driver run. - `AnalysisDriver` — executes analyses topologically, feeding results of child analyses to parent analyses. Added: clang/include/clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisBase.h clang/include/clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisDriver.h clang/include/clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisName.h clang/include/clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisRegistry.h clang/include/clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisResult.h clang/include/clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisTraits.h clang/include/clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/DerivedAnalysis.h clang/include/clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/SummaryAnalysis.h clang/include/clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/WPASuite.h clang/lib/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisDriver.cpp clang/lib/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisName.cpp clang/lib/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisRegistry.cpp clang/unittests/ScalableStaticAnalysisFramework/WholeProgramAnalysis/AnalysisDriverTest.cpp Modified: clang/include/clang/ScalableStaticAnalysisFramework/Core/EntityLinker/LUSummary.h clang/include/clang/ScalableStaticAnalysisFramework/Core/Support/FormatProviders.h clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h clang/lib/ScalableStaticAnalysisFramework/Core/CMakeLists.txt clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt Removed: ################################################################################ diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/EntityLinker/LUSummary.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/EntityLinker/LUSummary.h index 552fff04a4c01..a36002006430c 100644 --- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/EntityLinker/LUSummary.h +++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/EntityLinker/LUSummary.h @@ -31,6 +31,7 @@ namespace clang::ssaf { /// together. It contains deduplicated entities with their linkage information /// and the merged entity summaries. class LUSummary { + friend class AnalysisDriver; friend class LUSummaryConsumer; friend class SerializationFormat; friend class TestFixture; diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Support/FormatProviders.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Support/FormatProviders.h index d49fd6cb4a1dc..437152d43f425 100644 --- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Support/FormatProviders.h +++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Support/FormatProviders.h @@ -19,6 +19,7 @@ #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityLinkage.h" #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h" #include "clang/ScalableStaticAnalysisFramework/Core/Model/SummaryName.h" +#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisName.h" #include "llvm/Support/FormatProviders.h" #include "llvm/Support/raw_ostream.h" @@ -80,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 diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisBase.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisBase.h new file mode 100644 index 0000000000000..b86a9c5828700 --- /dev/null +++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisBase.h @@ -0,0 +1,60 @@ +//===- AnalysisBase.h -------------------------------------------*- C++ -*-===// +// +// 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_WHOLEPROGRAMANALYSIS_ANALYSISBASE_H +#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_WHOLEPROGRAMANALYSIS_ANALYSISBASE_H + +#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisName.h" +#include <vector> + +namespace clang::ssaf { + +class AnalysisDriver; +class AnalysisResult; +class DerivedAnalysisBase; +class SummaryAnalysisBase; + +/// Minimal common base for both analysis kinds. +/// +/// Not subclassed directly -- use SummaryAnalysis<...> or +/// DerivedAnalysis<...> instead. +class AnalysisBase { + friend class AnalysisDriver; + friend class DerivedAnalysisBase; + friend class SummaryAnalysisBase; + + 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; + + /// Transfers ownership of the built result. Called once after finalize(). + /// The rvalue ref-qualifier enforces single use. + virtual std::unique_ptr<AnalysisResult> result() && = 0; +}; + +} // namespace clang::ssaf + +#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_WHOLEPROGRAMANALYSIS_ANALYSISBASE_H diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisDriver.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisDriver.h new file mode 100644 index 0000000000000..156d8e806bd0f --- /dev/null +++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisDriver.h @@ -0,0 +1,95 @@ +//===- AnalysisDriver.h -----------------------------------------*- C++ -*-===// +// +// 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_WHOLEPROGRAMANALYSIS_ANALYSISDRIVER_H +#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_WHOLEPROGRAMANALYSIS_ANALYSISDRIVER_H + +#include "clang/ScalableStaticAnalysisFramework/Core/EntityLinker/LUSummary.h" +#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/WPASuite.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/Error.h" +#include <memory> +#include <vector> + +namespace clang::ssaf { + +class AnalysisBase; +class DerivedAnalysisBase; +class SummaryAnalysisBase; + +/// Orchestrates whole-program analysis over an LUSummary. +/// +/// Three run() patterns are supported: +/// - 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. +/// - 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. + /// 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. + [[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) const; + + /// Type-safe variant of run(names). Derives names from + /// ResultTs::analysisName(). + template <typename... ResultTs> + [[nodiscard]] llvm::Expected<WPASuite> run() const { + 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>>> + toposort(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, + llvm::ArrayRef<std::unique_ptr<AnalysisBase>> Sorted) const; + + llvm::Error executeSummaryAnalysis(SummaryAnalysisBase &Summary, + WPASuite &Suite) const; + + llvm::Error executeDerivedAnalysis(DerivedAnalysisBase &Derived, + WPASuite &Suite) const; +}; + +} // namespace clang::ssaf + +#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_WHOLEPROGRAMANALYSIS_ANALYSISDRIVER_H diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisName.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisName.h new file mode 100644 index 0000000000000..32f76e73b14e0 --- /dev/null +++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisName.h @@ -0,0 +1,49 @@ +//===- AnalysisName.h -------------------------------------------*- C++ -*-===// +// +// 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_WHOLEPROGRAMANALYSIS_ANALYSISNAME_H +#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_WHOLEPROGRAMANALYSIS_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_WHOLEPROGRAMANALYSIS_ANALYSISNAME_H diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisRegistry.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisRegistry.h new file mode 100644 index 0000000000000..44eabce6c809c --- /dev/null +++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisRegistry.h @@ -0,0 +1,109 @@ +//===- AnalysisRegistry.h ---------------------------------------*- C++ -*-===// +// +// 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> and an anchor source +// in its translation unit, then add the matching anchor destination to the +// relevant force-linker header: +// +// // MyAnalysis.cpp +// static AnalysisRegistry::Add<MyAnalysis> +// Registered("One-line description of MyAnalysis"); +// +// volatile int SSAFMyAnalysisAnchorSource = 0; +// +// // SSAFBuiltinForceLinker.h (or the relevant force-linker header) +// extern volatile int SSAFMyAnalysisAnchorSource; +// [[maybe_unused]] static int SSAFMyAnalysisAnchorDestination = +// SSAFMyAnalysisAnchorSource; +// +// The registry entry name is derived automatically from +// MyAnalysis::analysisName(), so name-mismatch bugs are impossible. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_WHOLEPROGRAMANALYSIS_ANALYSISREGISTRY_H +#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_WHOLEPROGRAMANALYSIS_ANALYSISREGISTRY_H + +#include "clang/ScalableStaticAnalysisFramework/Core/Support/ErrorBuilder.h" +#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisName.h" +#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/DerivedAnalysis.h" +#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/SummaryAnalysis.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/Registry.h" +#include <memory> +#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(AnalysisT::ResultType::analysisName())) { + ErrorBuilder::fatal("duplicate analysis registration for '{0}'", Name); + } + getAnalysisNames().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(const AnalysisName &Name); + + /// Returns the names of all registered analyses. + static const std::vector<AnalysisName> &names(); + + /// 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(const AnalysisName &Name); + +private: + /// 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 + +#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_WHOLEPROGRAMANALYSIS_ANALYSISREGISTRY_H diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisResult.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisResult.h new file mode 100644 index 0000000000000..07d1f0549a9ee --- /dev/null +++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisResult.h @@ -0,0 +1,30 @@ +//===- AnalysisResult.h -----------------------------------------*- C++ -*-===// +// +// 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. +// Concrete subclasses carry a static analysisName(). +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_WHOLEPROGRAMANALYSIS_ANALYSISRESULT_H +#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_WHOLEPROGRAMANALYSIS_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_WHOLEPROGRAMANALYSIS_ANALYSISRESULT_H diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisTraits.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisTraits.h new file mode 100644 index 0000000000000..78df3b35648c2 --- /dev/null +++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisTraits.h @@ -0,0 +1,36 @@ +//===- AnalysisTraits.h -----------------------------------------*- C++ -*-===// +// +// 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_WHOLEPROGRAMANALYSIS_ANALYSISTRAITS_H +#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_WHOLEPROGRAMANALYSIS_ANALYSISTRAITS_H + +#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/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> {}; + +template <typename T> +inline constexpr bool HasAnalysisName_v = HasAnalysisName<T>::value; + +} // namespace clang::ssaf + +#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_WHOLEPROGRAMANALYSIS_ANALYSISTRAITS_H diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/DerivedAnalysis.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/DerivedAnalysis.h new file mode 100644 index 0000000000000..4eb35262d4625 --- /dev/null +++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/DerivedAnalysis.h @@ -0,0 +1,140 @@ +//===- DerivedAnalysis.h ----------------------------------------*- C++ -*-===// +// +// 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_WHOLEPROGRAMANALYSIS_DERIVEDANALYSIS_H +#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_WHOLEPROGRAMANALYSIS_DERIVEDANALYSIS_H + +#include "clang/ScalableStaticAnalysisFramework/Core/Support/ErrorBuilder.h" +#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisBase.h" +#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisName.h" +#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisResult.h" +#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisTraits.h" +#include "llvm/Support/Error.h" +#include <map> +#include <memory> +#include <vector> + +namespace clang::ssaf { + +class AnalysisDriver; +class AnalysisRegistry; + +/// 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(); } +}; + +/// 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_v<ResultT>, + "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_v<DepResultTs> && ...), + "Every DepResultT must have a static analysisName() method"); + + friend class AnalysisRegistry; + using ResultType = ResultT; + + std::unique_ptr<ResultT> Result = std::make_unique<ResultT>(); + +public: + /// 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(). 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()) { + ErrorBuilder::fatal("dependency '{0}' missing from DepResults map; " + "dependency graph is not topologically sorted", + Name); + } + return It->second; + }; + return initialize(*static_cast<const DepResultTs *>( + lookup(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_WHOLEPROGRAMANALYSIS_DERIVEDANALYSIS_H diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/SummaryAnalysis.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/SummaryAnalysis.h new file mode 100644 index 0000000000000..31b9e6ae4a6c3 --- /dev/null +++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/SummaryAnalysis.h @@ -0,0 +1,128 @@ +//===- SummaryAnalysis.h ----------------------------------------*- C++ -*-===// +// +// 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_WHOLEPROGRAMANALYSIS_SUMMARYANALYSIS_H +#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_WHOLEPROGRAMANALYSIS_SUMMARYANALYSIS_H + +#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h" +#include "clang/ScalableStaticAnalysisFramework/Core/Model/SummaryName.h" +#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h" +#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisBase.h" +#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisResult.h" +#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisTraits.h" +#include "llvm/Support/Error.h" +#include <memory> + +namespace clang::ssaf { + +class AnalysisDriver; +class AnalysisRegistry; + +/// 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(); } +}; + +/// 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_v<ResultT>, + "ResultT must have a static analysisName() method"); + 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 = std::make_unique<ResultT>(); + +public: + /// 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_WHOLEPROGRAMANALYSIS_SUMMARYANALYSIS_H diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/WPASuite.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/WPASuite.h new file mode 100644 index 0000000000000..5a0105fc1f4d9 --- /dev/null +++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/WPASuite.h @@ -0,0 +1,95 @@ +//===- WPASuite.h -----------------------------------------------*- C++ -*-===// +// +// 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 +// with the analysis results keyed by AnalysisName. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_WHOLEPROGRAMANALYSIS_WPASUITE_H +#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_WHOLEPROGRAMANALYSIS_WPASUITE_H + +#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityIdTable.h" +#include "clang/ScalableStaticAnalysisFramework/Core/Support/ErrorBuilder.h" +#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisName.h" +#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisResult.h" +#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisTraits.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. + 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_v<ResultT>, + "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 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_v<ResultT>, + "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 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 '{0}' in WPASuite", Name) + .build(); + } + return *It->second; + } +}; + +} // namespace clang::ssaf + +#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_WHOLEPROGRAMANALYSIS_WPASUITE_H diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h b/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h index 5f201487ca1fe..2f144b92a1a94 100644 --- a/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h +++ b/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h @@ -25,4 +25,9 @@ extern volatile int SSAFJSONFormatAnchorSource; [[maybe_unused]] static int SSAFJSONFormatAnchorDestination = SSAFJSONFormatAnchorSource; +// This anchor is used to force the linker to link the AnalysisRegistry. +extern volatile int SSAFAnalysisRegistryAnchorSource; +[[maybe_unused]] static int SSAFAnalysisRegistryAnchorDestination = + SSAFAnalysisRegistryAnchorSource; + #endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_SSAFBUILTINFORCELINKER_H diff --git a/clang/lib/ScalableStaticAnalysisFramework/Core/CMakeLists.txt b/clang/lib/ScalableStaticAnalysisFramework/Core/CMakeLists.txt index 190b9fe400a64..8c306163df1a7 100644 --- a/clang/lib/ScalableStaticAnalysisFramework/Core/CMakeLists.txt +++ b/clang/lib/ScalableStaticAnalysisFramework/Core/CMakeLists.txt @@ -23,6 +23,9 @@ add_clang_library(clangScalableStaticAnalysisFrameworkCore Support/ErrorBuilder.cpp TUSummary/ExtractorRegistry.cpp TUSummary/TUSummaryBuilder.cpp + WholeProgramAnalysis/AnalysisDriver.cpp + WholeProgramAnalysis/AnalysisName.cpp + WholeProgramAnalysis/AnalysisRegistry.cpp LINK_LIBS clangAST diff --git a/clang/lib/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisDriver.cpp b/clang/lib/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisDriver.cpp new file mode 100644 index 0000000000000..430b98f3916fd --- /dev/null +++ b/clang/lib/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisDriver.cpp @@ -0,0 +1,212 @@ +//===- 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/WholeProgramAnalysis/AnalysisDriver.h" +#include "clang/ScalableStaticAnalysisFramework/Core/Support/ErrorBuilder.h" +#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisRegistry.h" +#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/DerivedAnalysis.h" +#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/SummaryAnalysis.h" +#include "llvm/ADT/STLExtras.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::toposort(llvm::ArrayRef<AnalysisName> Roots) { + struct Visitor { + enum class State { Unvisited, Visiting, Visited }; + + std::map<AnalysisName, State> Marks; + std::vector<AnalysisName> Path; + std::vector<std::unique_ptr<AnalysisBase>> Result; + + explicit Visitor(size_t N) { + Path.reserve(N); + Result.reserve(N); + } + + 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.emplace(Name, State::Unvisited); + + switch (It->second) { + case State::Visited: + return llvm::Error::success(); + + case State::Visiting: + return ErrorBuilder::create(std::errc::invalid_argument, + "cycle detected: {0}", formatCycle(Name)) + .build(); + + case State::Unvisited: { + It->second = State::Visiting; + Path.push_back(Name); + + llvm::Expected<std::unique_ptr<AnalysisBase>> V = + AnalysisRegistry::instantiate(Name); + if (!V) { + return V.takeError(); + } + + // Unwrap for convenience to avoid the noise of dereferencing an + // Expected on every subsequent access. + std::unique_ptr<AnalysisBase> Analysis = std::move(*V); + + for (const auto &Dep : Analysis->dependencyNames()) { + if (auto Err = visit(Dep)) { + return Err; + } + } + + // std::map iterators are not invalidated by insertions, so It remains + // valid after recursive visit() calls that insert new entries. + It->second = State::Visited; + Path.pop_back(); + Result.push_back(std::move(Analysis)); + + return llvm::Error::success(); + } + } + llvm_unreachable("unhandled State"); + } + }; + + Visitor V(Roots.size()); + for (const auto &Root : Roots) { + if (auto Err = V.visit(Root)) { + return std::move(Err); + } + } + return std::move(V.Result); +} + +llvm::Error AnalysisDriver::executeSummaryAnalysis(SummaryAnalysisBase &Summary, + WPASuite &Suite) const { + 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()) + .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; + } + + return llvm::Error::success(); +} + +llvm::Error AnalysisDriver::executeDerivedAnalysis(DerivedAnalysisBase &Derived, + WPASuite &Suite) const { + 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, Derived.analysisName()); + } + 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; + } + + return llvm::Error::success(); +} + +llvm::Expected<WPASuite> AnalysisDriver::execute( + EntityIdTable IdTable, + llvm::ArrayRef<std::unique_ptr<AnalysisBase>> Sorted) const { + WPASuite Suite; + Suite.IdTable = std::move(IdTable); + + for (auto &Analysis : Sorted) { + switch (Analysis->TheKind) { + case AnalysisBase::Kind::Summary: { + SummaryAnalysisBase &SA = static_cast<SummaryAnalysisBase &>(*Analysis); + if (auto Err = executeSummaryAnalysis(SA, Suite)) { + return std::move(Err); + } + break; + } + case AnalysisBase::Kind::Derived: { + DerivedAnalysisBase &DA = static_cast<DerivedAnalysisBase &>(*Analysis); + if (auto Err = executeDerivedAnalysis(DA, Suite)) { + return std::move(Err); + } + break; + } + } + AnalysisName Name = Analysis->analysisName(); + Suite.Data.emplace(std::move(Name), std::move(*Analysis).result()); + } + + return std::move(Suite); +} + +llvm::Expected<WPASuite> AnalysisDriver::run() && { + auto ExpectedSorted = toposort(AnalysisRegistry::names()); + if (!ExpectedSorted) { + return ExpectedSorted.takeError(); + } + return execute(std::move(LU->IdTable), *ExpectedSorted); +} + +llvm::Expected<WPASuite> +AnalysisDriver::run(llvm::ArrayRef<AnalysisName> Names) const { + auto ExpectedSorted = toposort(Names); + if (!ExpectedSorted) { + return ExpectedSorted.takeError(); + } + + return execute(LU->IdTable, *ExpectedSorted); +} diff --git a/clang/lib/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisName.cpp b/clang/lib/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisName.cpp new file mode 100644 index 0000000000000..9719196ed4d6d --- /dev/null +++ b/clang/lib/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/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/WholeProgramAnalysis/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/lib/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisRegistry.cpp b/clang/lib/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisRegistry.cpp new file mode 100644 index 0000000000000..8e1ea954d9afd --- /dev/null +++ b/clang/lib/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisRegistry.cpp @@ -0,0 +1,45 @@ +//===- 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/WholeProgramAnalysis/AnalysisRegistry.h" +#include "clang/ScalableStaticAnalysisFramework/Core/Support/ErrorBuilder.h" +#include "llvm/ADT/STLExtras.h" + +using namespace clang; +using namespace ssaf; + +using RegistryT = llvm::Registry<AnalysisBase>; + +// NOLINTNEXTLINE(misc-use-internal-linkage) +volatile int SSAFAnalysisRegistryAnchorSource = 0; +LLVM_INSTANTIATE_REGISTRY(RegistryT) + +std::vector<AnalysisName> &AnalysisRegistry::getAnalysisNames() { + static std::vector<AnalysisName> Names; + return Names; +} + +bool AnalysisRegistry::contains(const AnalysisName &Name) { + return llvm::is_contained(getAnalysisNames(), Name); +} + +const std::vector<AnalysisName> &AnalysisRegistry::names() { + return getAnalysisNames(); +} + +llvm::Expected<std::unique_ptr<AnalysisBase>> +AnalysisRegistry::instantiate(const AnalysisName &Name) { + for (const auto &Entry : RegistryT::entries()) { + if (Entry.getName() == Name.str()) { + return std::unique_ptr<AnalysisBase>(Entry.instantiate()); + } + } + return ErrorBuilder::create(std::errc::invalid_argument, + "no analysis registered for '{0}'", Name) + .build(); +} diff --git a/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt b/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt index f541e81d42789..8eeaa982daaf6 100644 --- a/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt +++ b/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt @@ -23,6 +23,7 @@ add_distinct_clang_unittest(ClangScalableAnalysisTests SummaryNameTest.cpp TestFixture.cpp TUSummaryBuilderTest.cpp + WholeProgramAnalysis/AnalysisDriverTest.cpp CLANG_LIBS clangAST diff --git a/clang/unittests/ScalableStaticAnalysisFramework/WholeProgramAnalysis/AnalysisDriverTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/WholeProgramAnalysis/AnalysisDriverTest.cpp new file mode 100644 index 0000000000000..e206b33d80295 --- /dev/null +++ b/clang/unittests/ScalableStaticAnalysisFramework/WholeProgramAnalysis/AnalysisDriverTest.cpp @@ -0,0 +1,566 @@ +//===- 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/WholeProgramAnalysis/AnalysisDriver.h" +#include "../TestFixture.h" +#include "clang/ScalableStaticAnalysisFramework/Core/EntityLinker/LUSummary.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 "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisName.h" +#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisRegistry.h" +#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisResult.h" +#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/DerivedAnalysis.h" +#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/SummaryAnalysis.h" +#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/WPASuite.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Testing/Support/Error.h" +#include "gtest/gtest.h" +#include <memory> +#include <string> +#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"); + } +}; + +// --------------------------------------------------------------------------- +// Analysis names +// --------------------------------------------------------------------------- + +const AnalysisName Analysis1Name("Analysis1"); +const AnalysisName Analysis2Name("Analysis2"); +const AnalysisName Analysis3Name("Analysis3"); +const AnalysisName Analysis4Name("Analysis4"); +const AnalysisName Analysis5Name("Analysis5"); +const AnalysisName CycleAName("CycleA"); +const AnalysisName CycleBName("CycleB"); + +// --------------------------------------------------------------------------- +// Results +// --------------------------------------------------------------------------- + +class Analysis1Result final : public AnalysisResult { +public: + static AnalysisName analysisName() { return Analysis1Name; } + std::vector<std::pair<EntityId, int>> Entries; + bool WasInitialized = false; + bool WasFinalized = false; +}; + +class Analysis2Result final : public AnalysisResult { +public: + static AnalysisName analysisName() { return Analysis2Name; } + std::vector<std::pair<EntityId, int>> Entries; + bool WasInitialized = false; + 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 Analysis3Name; } +}; + +// 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 Analysis4Name; } + 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 Analysis5Name; } + 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::toposort(). +// initialize() and step() are unreachable stubs - the cycle is caught before +// any analysis executes. +class CycleAResult final : public AnalysisResult { +public: + static AnalysisName analysisName() { return CycleAName; } +}; + +class CycleBResult final : public AnalysisResult { +public: + static AnalysisName analysisName() { return CycleBName; } +}; + +// --------------------------------------------------------------------------- +// Analyses +// --------------------------------------------------------------------------- + +class Analysis1 final + : public SummaryAnalysis<Analysis1Result, Analysis1EntitySummary> { +public: + inline static bool WasDestroyed = false; + ~Analysis1() { WasDestroyed = 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(); + } + + llvm::Error finalize() override { + result().WasFinalized = true; + return llvm::Error::success(); + } +}; + +// These static registrations are safe without SSAFBuiltinTestForceLinker.h +// because this translation unit is compiled directly into the test binary - +// the linker cannot dead-strip it, so all static initializers are guaranteed +// to run. +static AnalysisRegistry::Add<Analysis1> RegAnalysis1("Analysis for Analysis1"); + +class Analysis2 final + : public SummaryAnalysis<Analysis2Result, Analysis2EntitySummary> { +public: + inline static bool WasDestroyed = false; + ~Analysis2() { WasDestroyed = 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(); + } + + llvm::Error finalize() override { + result().WasFinalized = true; + return llvm::Error::success(); + } +}; + +static AnalysisRegistry::Add<Analysis2> RegAnalysis2("Analysis for Analysis2"); + +// No Analysis3 or registration for Analysis3. + +class Analysis4 final + : public SummaryAnalysis<Analysis4Result, Analysis4EntitySummary> { +public: + inline static bool WasDestroyed = false; + ~Analysis4() { WasDestroyed = 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(); + } + + llvm::Error finalize() override { + result().WasFinalized = true; + return llvm::Error::success(); + } +}; + +static AnalysisRegistry::Add<Analysis4> RegAnalysis4("Analysis for Analysis4"); + +class Analysis5 final + : public DerivedAnalysis<Analysis5Result, Analysis1Result, Analysis2Result, + Analysis4Result> { + int StepCount = 0; + +public: + inline static bool WasDestroyed = false; + ~Analysis5() { WasDestroyed = 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 +// --------------------------------------------------------------------------- + +class AnalysisDriverTest : public TestFixture { +protected: + static constexpr EntityLinkage ExternalLinkage = + EntityLinkage(EntityLinkageType::External); + + void SetUp() override { + NextSummaryInstanceId = 0; + Analysis1::WasDestroyed = false; + Analysis2::WasDestroyed = false; + // No Analysis3 - not registered, so no WasDestroyed flag. + Analysis4::WasDestroyed = false; + Analysis5::WasDestroyed = 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_TRUE(AnalysisRegistry::contains(Analysis1Name)); + EXPECT_TRUE(AnalysisRegistry::contains(Analysis2Name)); + EXPECT_FALSE(AnalysisRegistry::contains(Analysis3Name)); + EXPECT_TRUE(AnalysisRegistry::contains(Analysis4Name)); + EXPECT_TRUE(AnalysisRegistry::contains(Analysis5Name)); + EXPECT_TRUE(AnalysisRegistry::contains(CycleAName)); + EXPECT_TRUE(AnalysisRegistry::contains(CycleBName)); +} + +TEST(AnalysisRegistryTest, AnalysisCanBeInstantiated) { + constexpr auto instantiate = AnalysisRegistry::instantiate; + EXPECT_THAT_EXPECTED( + instantiate(AnalysisName("AnalysisNonExisting")), + llvm::FailedWithMessage( + "no analysis registered for 'AnalysisName(AnalysisNonExisting)'")); + EXPECT_THAT_EXPECTED(instantiate(Analysis1Name), llvm::Succeeded()); + EXPECT_THAT_EXPECTED(instantiate(Analysis2Name), llvm::Succeeded()); + // No Analysis3 - not registered, so instantiate() would fail. + EXPECT_THAT_EXPECTED(instantiate(Analysis4Name), llvm::Succeeded()); + EXPECT_THAT_EXPECTED(instantiate(Analysis5Name), llvm::Succeeded()); + EXPECT_THAT_EXPECTED(instantiate(CycleAName), llvm::Succeeded()); + EXPECT_THAT_EXPECTED(instantiate(CycleBName), llvm::Succeeded()); +} + +// 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"); + 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 = Driver.run<Analysis1Result, Analysis2Result, Analysis4Result, + Analysis5Result>(); + 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->WasInitialized); + EXPECT_TRUE(R1OrErr->WasFinalized); + EXPECT_TRUE(Analysis1::WasDestroyed); + } + + { + 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->WasInitialized); + EXPECT_TRUE(R2OrErr->WasFinalized); + EXPECT_TRUE(Analysis2::WasDestroyed); + } + + { + 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->WasInitialized); + EXPECT_TRUE(R4OrErr->WasFinalized); + EXPECT_TRUE(Analysis4::WasDestroyed); + } + + { + 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(Analysis5::WasDestroyed); + } + + // Unregistered analysis — not present in WPA. + EXPECT_THAT_EXPECTED( + WPAOrErr->get<Analysis3Result>(), + llvm::FailedWithMessage( + "no result for 'AnalysisName(Analysis3)' in WPASuite")); +} + +// 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->WasInitialized); + EXPECT_TRUE(R1OrErr->WasFinalized); + + // Analysis2 was not requested — not present even though data exists. + EXPECT_THAT_EXPECTED( + WPAOrErr->get<Analysis2Result>(), + llvm::FailedWithMessage( + "no result for 'AnalysisName(Analysis2)' in WPASuite")); +} + +// 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::FailedWithMessage( + "no data for analysis 'AnalysisName(Analysis1)' in LUSummary")); +} + +// 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::FailedWithMessage( + "no analysis registered for 'AnalysisName(Analysis3)'")); +} + +// 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->WasInitialized); + EXPECT_TRUE(R1OrErr->WasFinalized); + + // Analysis2 was not requested — not present even though data exists. + EXPECT_THAT_EXPECTED( + WPAOrErr->get<Analysis2Result>(), + llvm::FailedWithMessage( + "no result for 'AnalysisName(Analysis2)' in WPASuite")); +} + +// 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::FailedWithMessage( + "no data for analysis 'AnalysisName(Analysis1)' in LUSummary")); +} + +// 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<Analysis2EntitySummary>(*LU, "Analysis2", E1); + insertSummary<Analysis4EntitySummary>(*LU, "Analysis4", E1); + + AnalysisDriver Driver(std::move(LU)); + auto WPAOrErr = Driver.run<Analysis1Result, Analysis2Result, Analysis4Result, + Analysis5Result>(); + ASSERT_THAT_EXPECTED(WPAOrErr, llvm::Succeeded()); + EXPECT_TRUE(WPAOrErr->contains<Analysis1Result>()); + // 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 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
