https://github.com/steakhal created https://github.com/llvm/llvm-project/pull/180779
This PR should be reviewed commit-by-commit. My intention is to merge the first 3 commits as-is, and also get some feedback on the TUSummaryBuilder class and the unit test for it. That builder will change to enable better error handling but the APIs shouldn't change much to void the work already invested into the testing. From 2232f02903690b00a821d378fe6093f93c9644a2 Mon Sep 17 00:00:00 2001 From: Balazs Benics <[email protected]> Date: Tue, 10 Feb 2026 15:13:51 +0100 Subject: [PATCH 1/5] [clang][ssaf][NFC] Refactor SerializationFormat to use macro-based field accessors This reduces code duplication and makes it easier to add new field accessors. Assisted-By: claude --- .../Scalable/Model/PrivateFieldNames.def | 30 +++++++++ .../Serialization/SerializationFormat.h | 32 +++------- .../Serialization/SerializationFormat.cpp | 64 +------------------ .../Registries/MockSerializationFormat.cpp | 2 +- 4 files changed, 44 insertions(+), 84 deletions(-) create mode 100644 clang/include/clang/Analysis/Scalable/Model/PrivateFieldNames.def diff --git a/clang/include/clang/Analysis/Scalable/Model/PrivateFieldNames.def b/clang/include/clang/Analysis/Scalable/Model/PrivateFieldNames.def new file mode 100644 index 0000000000000..59064659996b4 --- /dev/null +++ b/clang/include/clang/Analysis/Scalable/Model/PrivateFieldNames.def @@ -0,0 +1,30 @@ +//===-- PrivateFieldNames.def -----------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines a list of non-static data members of the SSAF objects. +// These are used for granting access for: +// - SerializationFormat +// - TextFixture +// +//===----------------------------------------------------------------------===// + +#ifndef FIELD +#define FIELD(CLASS, FIELD_NAME) +#endif + +FIELD(BuildNamespace, Kind) +FIELD(BuildNamespace, Name) +FIELD(EntityName, Namespace) +FIELD(EntityName, Suffix) +FIELD(EntityName, USR) +FIELD(NestedBuildNamespace, Namespaces) +FIELD(TUSummary, Data) +FIELD(TUSummary, IdTable) +FIELD(TUSummary, TUNamespace) + +#undef FIELD diff --git a/clang/include/clang/Analysis/Scalable/Serialization/SerializationFormat.h b/clang/include/clang/Analysis/Scalable/Serialization/SerializationFormat.h index c7438e2859da0..2c0ca57f6db46 100644 --- a/clang/include/clang/Analysis/Scalable/Serialization/SerializationFormat.h +++ b/clang/include/clang/Analysis/Scalable/Serialization/SerializationFormat.h @@ -17,11 +17,9 @@ #include "clang/Analysis/Scalable/Model/BuildNamespace.h" #include "clang/Analysis/Scalable/Model/SummaryName.h" #include "clang/Analysis/Scalable/TUSummary/TUSummary.h" -#include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/ExtensibleRTTI.h" #include "llvm/Support/VirtualFileSystem.h" -#include <vector> namespace clang::ssaf { @@ -33,26 +31,6 @@ class EntitySummary; /// Abstract base class for serialization formats. class SerializationFormat : public llvm::RTTIExtends<SerializationFormat, llvm::RTTIRoot> { -protected: - // Helpers providing access to implementation details of basic data structures - // for efficient serialization/deserialization. - static EntityIdTable &getIdTableForDeserialization(TUSummary &S); - static BuildNamespace &getTUNamespaceForDeserialization(TUSummary &S); - static const EntityIdTable &getIdTable(const TUSummary &S); - static const BuildNamespace &getTUNamespace(const TUSummary &S); - - static BuildNamespaceKind getBuildNamespaceKind(const BuildNamespace &BN); - static llvm::StringRef getBuildNamespaceName(const BuildNamespace &BN); - static const std::vector<BuildNamespace> & - getNestedBuildNamespaces(const NestedBuildNamespace &NBN); - - static llvm::StringRef getEntityNameUSR(const EntityName &EN); - static const llvm::SmallString<16> &getEntityNameSuffix(const EntityName &EN); - static const NestedBuildNamespace & - getEntityNameNamespace(const EntityName &EN); - static decltype(TUSummary::Data) &getData(TUSummary &S); - static const decltype(TUSummary::Data) &getData(const TUSummary &S); - public: explicit SerializationFormat( llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS); @@ -66,7 +44,17 @@ class SerializationFormat static char ID; // For RTTIExtends. protected: + // Helpers providing access to implementation details of basic data structures + // for efficient serialization/deserialization. +#define FIELD(CLASS, FIELD_NAME) \ + static const auto &get##FIELD_NAME(const CLASS &X) { return X.FIELD_NAME; } \ + static auto &get##FIELD_NAME(CLASS &X) { return X.FIELD_NAME; } +#include "clang/Analysis/Scalable/Model/PrivateFieldNames.def" + llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS; + +private: + void anchor() override; }; template <class SerializerFn, class DeserializerFn> struct FormatInfoEntry { diff --git a/clang/lib/Analysis/Scalable/Serialization/SerializationFormat.cpp b/clang/lib/Analysis/Scalable/Serialization/SerializationFormat.cpp index 1d62ea837616f..be2740d7a3e4c 100644 --- a/clang/lib/Analysis/Scalable/Serialization/SerializationFormat.cpp +++ b/clang/lib/Analysis/Scalable/Serialization/SerializationFormat.cpp @@ -7,70 +7,12 @@ //===----------------------------------------------------------------------===// #include "clang/Analysis/Scalable/Serialization/SerializationFormat.h" -#include "clang/Analysis/Scalable/Model/BuildNamespace.h" -#include "clang/Analysis/Scalable/Model/EntityId.h" -#include "clang/Analysis/Scalable/Model/EntityName.h" -#include "clang/Analysis/Scalable/TUSummary/TUSummary.h" using namespace clang::ssaf; +char SerializationFormat::ID = 0; +void SerializationFormat::anchor() {} + SerializationFormat::SerializationFormat( llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) : FS(FS) {} - -EntityIdTable &SerializationFormat::getIdTableForDeserialization(TUSummary &S) { - return S.IdTable; -} - -BuildNamespace & -SerializationFormat::getTUNamespaceForDeserialization(TUSummary &S) { - return S.TUNamespace; -} - -const EntityIdTable &SerializationFormat::getIdTable(const TUSummary &S) { - return S.IdTable; -} - -const BuildNamespace &SerializationFormat::getTUNamespace(const TUSummary &S) { - return S.TUNamespace; -} - -BuildNamespaceKind -SerializationFormat::getBuildNamespaceKind(const BuildNamespace &BN) { - return BN.Kind; -} - -llvm::StringRef -SerializationFormat::getBuildNamespaceName(const BuildNamespace &BN) { - return BN.Name; -} - -const std::vector<BuildNamespace> & -SerializationFormat::getNestedBuildNamespaces(const NestedBuildNamespace &NBN) { - return NBN.Namespaces; -} - -llvm::StringRef SerializationFormat::getEntityNameUSR(const EntityName &EN) { - return EN.USR; -} - -const llvm::SmallString<16> & -SerializationFormat::getEntityNameSuffix(const EntityName &EN) { - return EN.Suffix; -} - -const NestedBuildNamespace & -SerializationFormat::getEntityNameNamespace(const EntityName &EN) { - return EN.Namespace; -} - -const decltype(TUSummary::Data) & -SerializationFormat::getData(const TUSummary &S) { - return S.Data; -} - -decltype(TUSummary::Data) &SerializationFormat::getData(TUSummary &S) { - return S.Data; -} - -char SerializationFormat::ID = 0; diff --git a/clang/unittests/Analysis/Scalable/Registries/MockSerializationFormat.cpp b/clang/unittests/Analysis/Scalable/Registries/MockSerializationFormat.cpp index 03328e8bd9742..127e61b26c0cb 100644 --- a/clang/unittests/Analysis/Scalable/Registries/MockSerializationFormat.cpp +++ b/clang/unittests/Analysis/Scalable/Registries/MockSerializationFormat.cpp @@ -67,7 +67,7 @@ TUSummary MockSerializationFormat::readTUSummary(llvm::StringRef Path) { assert(InfoEntry.ForSummary == Name); SpecialFileRepresentation Repr{(*InputFile)->getBuffer().str()}; - auto &Table = getIdTableForDeserialization(Summary); + auto &Table = getIdTable(Summary); std::unique_ptr<EntitySummary> Result = InfoEntry.Deserialize(Repr, Table); if (!Result) // TODO: Handle error. From 0ac78a0f4fd596e5cef66a24f8b01e9a19b23ec7 Mon Sep 17 00:00:00 2001 From: Balazs Benics <[email protected]> Date: Tue, 10 Feb 2026 17:04:05 +0100 Subject: [PATCH 2/5] [clang][ssaf] Add EntityLinkage stub --- .../Analysis/Scalable/Model/EntityLinkage.h | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 clang/include/clang/Analysis/Scalable/Model/EntityLinkage.h diff --git a/clang/include/clang/Analysis/Scalable/Model/EntityLinkage.h b/clang/include/clang/Analysis/Scalable/Model/EntityLinkage.h new file mode 100644 index 0000000000000..a89ab200fbd79 --- /dev/null +++ b/clang/include/clang/Analysis/Scalable/Model/EntityLinkage.h @@ -0,0 +1,27 @@ +//===- EntityLinkage.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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_MODEL_ENTITY_LINKAGE_H +#define LLVM_CLANG_ANALYSIS_SCALABLE_MODEL_ENTITY_LINKAGE_H + +namespace clang::ssaf { + +class EntityLinkage { +public: + // Empty for now. + bool operator==(const EntityLinkage &Other) const { + return true; // FIXME: Adjust this once no longer empty. + } + bool operator!=(const EntityLinkage &Other) const { + return !(*this == Other); + } +}; + +} // namespace clang::ssaf + +#endif // LLVM_CLANG_ANALYSIS_SCALABLE_MODEL_ENTITY_LINKAGE_H From cdc2dbc1942e2228679874e506e618a53c724ef1 Mon Sep 17 00:00:00 2001 From: Balazs Benics <[email protected]> Date: Tue, 10 Feb 2026 17:04:59 +0100 Subject: [PATCH 3/5] [clang][ssaf] Add operator<< for debugging to SummaryName and EntityId --- .../clang/Analysis/Scalable/Model/EntityId.h | 6 ++++++ .../Analysis/Scalable/Model/SummaryName.h | 7 +++++++ clang/lib/Analysis/Scalable/CMakeLists.txt | 2 ++ clang/lib/Analysis/Scalable/Model/EntityId.cpp | 17 +++++++++++++++++ .../Analysis/Scalable/Model/SummaryName.cpp | 18 ++++++++++++++++++ 5 files changed, 50 insertions(+) create mode 100644 clang/lib/Analysis/Scalable/Model/EntityId.cpp create mode 100644 clang/lib/Analysis/Scalable/Model/SummaryName.cpp diff --git a/clang/include/clang/Analysis/Scalable/Model/EntityId.h b/clang/include/clang/Analysis/Scalable/Model/EntityId.h index 6fa059445d853..6e1b7ce276bda 100644 --- a/clang/include/clang/Analysis/Scalable/Model/EntityId.h +++ b/clang/include/clang/Analysis/Scalable/Model/EntityId.h @@ -17,6 +17,10 @@ #include <cstddef> +namespace llvm { +class raw_ostream; +} // namespace llvm + namespace clang::ssaf { class EntityIdTable; @@ -40,6 +44,8 @@ class EntityId { bool operator==(const EntityId &Other) const { return Index == Other.Index; } bool operator<(const EntityId &Other) const { return Index < Other.Index; } bool operator!=(const EntityId &Other) const { return !(*this == Other); } + + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const EntityId &); }; } // namespace clang::ssaf diff --git a/clang/include/clang/Analysis/Scalable/Model/SummaryName.h b/clang/include/clang/Analysis/Scalable/Model/SummaryName.h index 785fe0eb10372..14929592b266b 100644 --- a/clang/include/clang/Analysis/Scalable/Model/SummaryName.h +++ b/clang/include/clang/Analysis/Scalable/Model/SummaryName.h @@ -12,6 +12,10 @@ #include "llvm/ADT/StringRef.h" #include <string> +namespace llvm { +class raw_ostream; +} // namespace llvm + namespace clang::ssaf { /// Uniquely identifies an analysis summary. @@ -29,6 +33,9 @@ class SummaryName { /// Explicit conversion to the underlying string representation. llvm::StringRef str() const { return Name; } + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, + const SummaryName &); + private: std::string Name; }; diff --git a/clang/lib/Analysis/Scalable/CMakeLists.txt b/clang/lib/Analysis/Scalable/CMakeLists.txt index 47fe87074d728..227aeded475c1 100644 --- a/clang/lib/Analysis/Scalable/CMakeLists.txt +++ b/clang/lib/Analysis/Scalable/CMakeLists.txt @@ -5,8 +5,10 @@ set(LLVM_LINK_COMPONENTS add_clang_library(clangAnalysisScalable ASTEntityMapping.cpp Model/BuildNamespace.cpp + Model/EntityId.cpp Model/EntityIdTable.cpp Model/EntityName.cpp + Model/SummaryName.cpp Serialization/SerializationFormat.cpp Serialization/SerializationFormatRegistry.cpp TUSummary/EntitySummary.cpp diff --git a/clang/lib/Analysis/Scalable/Model/EntityId.cpp b/clang/lib/Analysis/Scalable/Model/EntityId.cpp new file mode 100644 index 0000000000000..2aa0ee312ca6b --- /dev/null +++ b/clang/lib/Analysis/Scalable/Model/EntityId.cpp @@ -0,0 +1,17 @@ +//===- EntityId.cpp -------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/Scalable/Model/EntityId.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ssaf; + +llvm::raw_ostream &ssaf::operator<<(llvm::raw_ostream &OS, const EntityId &ID) { + return OS << "EntityId(" << ID.Index << ")"; +} diff --git a/clang/lib/Analysis/Scalable/Model/SummaryName.cpp b/clang/lib/Analysis/Scalable/Model/SummaryName.cpp new file mode 100644 index 0000000000000..663f8900dbec6 --- /dev/null +++ b/clang/lib/Analysis/Scalable/Model/SummaryName.cpp @@ -0,0 +1,18 @@ +//===- SummaryName.cpp ----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/Scalable/Model/SummaryName.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ssaf; + +llvm::raw_ostream &ssaf::operator<<(llvm::raw_ostream &OS, + const SummaryName &Name) { + return OS << "SummaryName(\"" << Name.str() << "\")"; +} From fc76c4a4a7934d1ff4427c6b991d94a200fa15f1 Mon Sep 17 00:00:00 2001 From: Balazs Benics <[email protected]> Date: Tue, 10 Feb 2026 15:16:47 +0100 Subject: [PATCH 4/5] [clang][ssaf] Implement TUSummaryBuilder with addEntity and addFact methods Add core functionality to TUSummaryBuilder for building TUSummary objects. The addEntity method registers entities with their linkage information, and addFact attaches entity summaries to entities. This enables constructing TUSummary objects incrementally during analysis. Assisted-By: claude --- .../Analysis/Scalable/Model/BuildNamespace.h | 7 +++-- .../Analysis/Scalable/Model/EntityName.h | 2 +- .../Scalable/Model/PrivateFieldNames.def | 1 + .../Analysis/Scalable/TUSummary/TUSummary.h | 4 +++ .../Scalable/TUSummary/TUSummaryBuilder.h | 23 +++++++++++++++- clang/lib/Analysis/Scalable/CMakeLists.txt | 1 + .../Scalable/TUSummary/TUSummaryBuilder.cpp | 26 +++++++++++++++++++ .../Registries/MockTUSummaryBuilder.h | 1 + .../SummaryExtractorRegistryTest.cpp | 16 +++++++++--- 9 files changed, 72 insertions(+), 9 deletions(-) create mode 100644 clang/lib/Analysis/Scalable/TUSummary/TUSummaryBuilder.cpp diff --git a/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h b/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h index 5ca26df1e9252..2cd8990708b8d 100644 --- a/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h +++ b/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h @@ -63,6 +63,7 @@ class BuildNamespace { bool operator<(const BuildNamespace &Other) const; friend class SerializationFormat; + friend class TestFixture; }; /// Represents a hierarchical sequence of build namespaces. @@ -75,8 +76,6 @@ class BuildNamespace { /// For example, an entity might be qualified by a compilation unit namespace /// followed by a shared library namespace. class NestedBuildNamespace { - friend class SerializationFormat; - std::vector<BuildNamespace> Namespaces; public: @@ -114,8 +113,8 @@ class NestedBuildNamespace { bool operator!=(const NestedBuildNamespace &Other) const; bool operator<(const NestedBuildNamespace &Other) const; - friend class JSONWriter; - friend class LinkUnitResolution; + friend class SerializationFormat; + friend class TestFixture; }; } // namespace clang::ssaf diff --git a/clang/include/clang/Analysis/Scalable/Model/EntityName.h b/clang/include/clang/Analysis/Scalable/Model/EntityName.h index 23890ab7bea43..72dd9ac803052 100644 --- a/clang/include/clang/Analysis/Scalable/Model/EntityName.h +++ b/clang/include/clang/Analysis/Scalable/Model/EntityName.h @@ -47,8 +47,8 @@ class EntityName { /// \param Namespace The namespace steps to append to this entity's namespace. EntityName makeQualified(NestedBuildNamespace Namespace) const; - friend class LinkUnitResolution; friend class SerializationFormat; + friend class TestFixture; }; } // namespace clang::ssaf diff --git a/clang/include/clang/Analysis/Scalable/Model/PrivateFieldNames.def b/clang/include/clang/Analysis/Scalable/Model/PrivateFieldNames.def index 59064659996b4..a91ff5952b807 100644 --- a/clang/include/clang/Analysis/Scalable/Model/PrivateFieldNames.def +++ b/clang/include/clang/Analysis/Scalable/Model/PrivateFieldNames.def @@ -24,6 +24,7 @@ FIELD(EntityName, Suffix) FIELD(EntityName, USR) FIELD(NestedBuildNamespace, Namespaces) FIELD(TUSummary, Data) +FIELD(TUSummary, Entities) FIELD(TUSummary, IdTable) FIELD(TUSummary, TUNamespace) diff --git a/clang/include/clang/Analysis/Scalable/TUSummary/TUSummary.h b/clang/include/clang/Analysis/Scalable/TUSummary/TUSummary.h index 4af1c70e1a488..9ed35a2c3828f 100644 --- a/clang/include/clang/Analysis/Scalable/TUSummary/TUSummary.h +++ b/clang/include/clang/Analysis/Scalable/TUSummary/TUSummary.h @@ -12,6 +12,7 @@ #include "clang/Analysis/Scalable/Model/BuildNamespace.h" #include "clang/Analysis/Scalable/Model/EntityId.h" #include "clang/Analysis/Scalable/Model/EntityIdTable.h" +#include "clang/Analysis/Scalable/Model/EntityLinkage.h" #include "clang/Analysis/Scalable/Model/SummaryName.h" #include "clang/Analysis/Scalable/TUSummary/EntitySummary.h" #include <map> @@ -24,6 +25,7 @@ class TUSummary { /// Identifies the translation unit. BuildNamespace TUNamespace; EntityIdTable IdTable; + std::map<EntityId, EntityLinkage> Entities; std::map<SummaryName, std::map<EntityId, std::unique_ptr<EntitySummary>>> Data; @@ -32,6 +34,8 @@ class TUSummary { TUSummary(BuildNamespace TUNamespace) : TUNamespace(std::move(TUNamespace)) {} friend class SerializationFormat; + friend class TestFixture; + friend class TUSummaryBuilder; }; } // namespace clang::ssaf diff --git a/clang/include/clang/Analysis/Scalable/TUSummary/TUSummaryBuilder.h b/clang/include/clang/Analysis/Scalable/TUSummary/TUSummaryBuilder.h index fa679c145faa5..55dddb2d25656 100644 --- a/clang/include/clang/Analysis/Scalable/TUSummary/TUSummaryBuilder.h +++ b/clang/include/clang/Analysis/Scalable/TUSummary/TUSummaryBuilder.h @@ -9,10 +9,31 @@ #ifndef LLVM_CLANG_ANALYSIS_SCALABLE_TUSUMMARY_TUSUMMARYBUILDER_H #define LLVM_CLANG_ANALYSIS_SCALABLE_TUSUMMARY_TUSUMMARYBUILDER_H +#include <memory> + namespace clang::ssaf { +class EntityId; +class EntityLinkage; +class EntityName; +class EntitySummary; +class TUSummary; + class TUSummaryBuilder { - // Empty for now. +public: + explicit TUSummaryBuilder(TUSummary &Summary) : Summary(Summary) {} + + /// Add an entity to the summary and return its EntityId. + /// If the entity already exists, returns the existing ID (idempotent). + EntityId addEntity(const EntityName &E, const EntityLinkage &Linkage); + + /// Add analysis-specific fact data for an entity. + /// Precondition: The ContributingEntity must have been added via addEntity(). + void addFact(EntityId ContributingEntity, + std::unique_ptr<EntitySummary> NewData); + +private: + TUSummary &Summary; }; } // namespace clang::ssaf diff --git a/clang/lib/Analysis/Scalable/CMakeLists.txt b/clang/lib/Analysis/Scalable/CMakeLists.txt index 227aeded475c1..03bed630a78eb 100644 --- a/clang/lib/Analysis/Scalable/CMakeLists.txt +++ b/clang/lib/Analysis/Scalable/CMakeLists.txt @@ -13,6 +13,7 @@ add_clang_library(clangAnalysisScalable Serialization/SerializationFormatRegistry.cpp TUSummary/EntitySummary.cpp TUSummary/ExtractorRegistry.cpp + TUSummary/TUSummaryBuilder.cpp LINK_LIBS clangAST diff --git a/clang/lib/Analysis/Scalable/TUSummary/TUSummaryBuilder.cpp b/clang/lib/Analysis/Scalable/TUSummary/TUSummaryBuilder.cpp new file mode 100644 index 0000000000000..7dfae5e1bd0e2 --- /dev/null +++ b/clang/lib/Analysis/Scalable/TUSummary/TUSummaryBuilder.cpp @@ -0,0 +1,26 @@ +#include "clang/Analysis/Scalable/TUSummary/TUSummaryBuilder.h" +#include "clang/Analysis/Scalable/Model/EntityId.h" +#include "clang/Analysis/Scalable/Model/EntityLinkage.h" +#include "clang/Analysis/Scalable/TUSummary/TUSummary.h" +#include <memory> + +using namespace clang; +using namespace ssaf; + +EntityId TUSummaryBuilder::addEntity(const EntityName &E, + const EntityLinkage &Linkage) { + EntityId Id = Summary.IdTable.getId(E); + const EntityLinkage &ExistingLinkage = + Summary.Entities.try_emplace(Id, Linkage).first->second; + if (ExistingLinkage != Linkage) { + // print ExistingLinkage, Linkage, and ID; + llvm::report_fatal_error("Entity already exists: "); + } + return Id; +} + +void TUSummaryBuilder::addFact(EntityId ContributingEntity, + std::unique_ptr<EntitySummary> NewData) { + Summary.Data[NewData->getSummaryName()][ContributingEntity] = + std::move(NewData); +} \ No newline at end of file diff --git a/clang/unittests/Analysis/Scalable/Registries/MockTUSummaryBuilder.h b/clang/unittests/Analysis/Scalable/Registries/MockTUSummaryBuilder.h index ccb79ae042625..755a47471a9f8 100644 --- a/clang/unittests/Analysis/Scalable/Registries/MockTUSummaryBuilder.h +++ b/clang/unittests/Analysis/Scalable/Registries/MockTUSummaryBuilder.h @@ -14,6 +14,7 @@ namespace clang::ssaf { class MockTUSummaryBuilder : public TUSummaryBuilder { public: + using TUSummaryBuilder::TUSummaryBuilder; void sendMessage(llvm::Twine Message) { Stream << Message << '\n'; } std::string consumeMessages() { return std::move(OutputBuffer); } diff --git a/clang/unittests/Analysis/Scalable/Registries/SummaryExtractorRegistryTest.cpp b/clang/unittests/Analysis/Scalable/Registries/SummaryExtractorRegistryTest.cpp index 2076fae0b5ab0..70c84363f389e 100644 --- a/clang/unittests/Analysis/Scalable/Registries/SummaryExtractorRegistryTest.cpp +++ b/clang/unittests/Analysis/Scalable/Registries/SummaryExtractorRegistryTest.cpp @@ -8,6 +8,7 @@ #include "MockTUSummaryBuilder.h" #include "clang/Analysis/Scalable/TUSummary/ExtractorRegistry.h" +#include "clang/Analysis/Scalable/TUSummary/TUSummary.h" #include "clang/Frontend/MultiplexConsumer.h" #include "clang/Tooling/Tooling.h" #include "llvm/ADT/StringRef.h" @@ -17,6 +18,12 @@ using namespace clang; using namespace ssaf; +static TUSummary makeFakeSummary() { + BuildNamespace NS(BuildNamespaceKind::CompilationUnit, "Mock.cpp"); + TUSummary Summary(NS); + return Summary; +} + namespace { TEST(SummaryExtractorRegistryTest, isTUSummaryExtractorRegistered) { @@ -39,7 +46,8 @@ TEST(SummaryExtractorRegistryTest, EnumeratingRegistryEntries) { } TEST(SummaryExtractorRegistryTest, InstantiatingExtractor1) { - MockTUSummaryBuilder FakeBuilder; + TUSummary Summary = makeFakeSummary(); + MockTUSummaryBuilder FakeBuilder(Summary); { auto Consumer = makeTUSummaryExtractor("MockSummaryExtractor1", FakeBuilder); @@ -52,7 +60,8 @@ TEST(SummaryExtractorRegistryTest, InstantiatingExtractor1) { } TEST(SummaryExtractorRegistryTest, InstantiatingExtractor2) { - MockTUSummaryBuilder FakeBuilder; + TUSummary Summary = makeFakeSummary(); + MockTUSummaryBuilder FakeBuilder(Summary); { auto Consumer = makeTUSummaryExtractor("MockSummaryExtractor2", FakeBuilder); @@ -65,7 +74,8 @@ TEST(SummaryExtractorRegistryTest, InstantiatingExtractor2) { } TEST(SummaryExtractorRegistryTest, InvokingExtractors) { - MockTUSummaryBuilder FakeBuilder; + TUSummary Summary = makeFakeSummary(); + MockTUSummaryBuilder FakeBuilder(Summary); std::vector<std::unique_ptr<ASTConsumer>> Consumers; for (std::string Name : {"MockSummaryExtractor1", "MockSummaryExtractor2"}) { auto Consumer = makeTUSummaryExtractor(Name, FakeBuilder); From 78b33671ee16bd7bc6731a38f3552dfae3d7a497 Mon Sep 17 00:00:00 2001 From: Balazs Benics <[email protected]> Date: Tue, 10 Feb 2026 15:17:26 +0100 Subject: [PATCH 5/5] [clang][ssaf] Add tests for TUSummaryBuilder Add comprehensive unit tests for TUSummaryBuilder functionality, covering entity registration, fact addition, and various edge cases. Introduce TestFixture helper for common test setup and update related test files to use the new TUSummaryBuilder interface. Assisted-By: claude --- .../Analysis/Scalable/CMakeLists.txt | 2 + .../Scalable/TUSummaryBuilderTest.cpp | 319 ++++++++++++++++++ .../Analysis/Scalable/TestFixture.cpp | 29 ++ .../unittests/Analysis/Scalable/TestFixture.h | 34 ++ 4 files changed, 384 insertions(+) create mode 100644 clang/unittests/Analysis/Scalable/TUSummaryBuilderTest.cpp create mode 100644 clang/unittests/Analysis/Scalable/TestFixture.cpp create mode 100644 clang/unittests/Analysis/Scalable/TestFixture.h diff --git a/clang/unittests/Analysis/Scalable/CMakeLists.txt b/clang/unittests/Analysis/Scalable/CMakeLists.txt index 601845b4ab77a..ca3f67a005707 100644 --- a/clang/unittests/Analysis/Scalable/CMakeLists.txt +++ b/clang/unittests/Analysis/Scalable/CMakeLists.txt @@ -11,6 +11,8 @@ add_distinct_clang_unittest(ClangScalableAnalysisTests Registries/SerializationFormatRegistryTest.cpp Registries/SummaryExtractorRegistryTest.cpp SummaryNameTest.cpp + TestFixture.cpp + TUSummaryBuilderTest.cpp CLANG_LIBS clangAnalysisScalable diff --git a/clang/unittests/Analysis/Scalable/TUSummaryBuilderTest.cpp b/clang/unittests/Analysis/Scalable/TUSummaryBuilderTest.cpp new file mode 100644 index 0000000000000..2b610b17cdbe0 --- /dev/null +++ b/clang/unittests/Analysis/Scalable/TUSummaryBuilderTest.cpp @@ -0,0 +1,319 @@ +//===- unittests/Analysis/Scalable/TUSummaryBuilderTest.cpp ---------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/Scalable/TUSummary/TUSummaryBuilder.h" +#include "TestFixture.h" +#include "clang/Analysis/Scalable/Model/BuildNamespace.h" +#include "clang/Analysis/Scalable/Model/EntityId.h" +#include "clang/Analysis/Scalable/Model/EntityName.h" +#include "clang/Analysis/Scalable/Model/SummaryName.h" +#include "clang/Analysis/Scalable/Serialization/SerializationFormat.h" +#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h" +#include "clang/Analysis/Scalable/TUSummary/TUSummary.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include <type_traits> + +using namespace clang; +using namespace ssaf; + +using llvm::SmallVector; +using testing::Field; +using testing::Optional; +using testing::UnorderedElementsAre; + +[[nodiscard]] +static TUSummary makeFakeSummary() { + BuildNamespace NS(BuildNamespaceKind::CompilationUnit, "Mock.cpp"); + TUSummary Summary(NS); + return Summary; +} + +[[nodiscard]] +static EntityId addTestEntity(TUSummaryBuilder &Builder, llvm::StringRef USR) { + EntityName EN(USR, "", /*Namespace=*/{}); + EntityLinkage MockLinkage; + return Builder.addEntity(EN, MockLinkage); +} + +template <class ConcreteEntitySummary> +[[nodiscard]] +static SummaryName addFactTo(TUSummaryBuilder &Builder, EntityId ID, + ConcreteEntitySummary Fact) { + static_assert(std::is_base_of_v<EntitySummary, ConcreteEntitySummary>); + auto NewFact = std::make_unique<ConcreteEntitySummary>(std::move(Fact)); + SummaryName Name = NewFact->getSummaryName(); + Builder.addFact(ID, std::move(NewFact)); + return Name; +} + +namespace { + +// Mock EntitySummary classes for testing +struct MockSummaryData1 final + : public llvm::RTTIExtends<MockSummaryData1, EntitySummary> { + explicit MockSummaryData1(int Value) : Value(Value) {} + SummaryName getSummaryName() const override { + return SummaryName("MockSummary1"); + } + int Value = 0; + static char ID; +}; + +struct MockSummaryData2 final + : public llvm::RTTIExtends<MockSummaryData2, EntitySummary> { + explicit MockSummaryData2(std::string Text) : Text(std::move(Text)) {} + SummaryName getSummaryName() const override { + return SummaryName("MockSummary2"); + } + std::string Text; + static char ID; +}; + +struct MockSummaryData3 final + : public llvm::RTTIExtends<MockSummaryData3, EntitySummary> { + explicit MockSummaryData3(bool Flag) : Flag(Flag) {} + SummaryName getSummaryName() const override { + return SummaryName("MockSummary3"); + } + bool Flag = false; + static char ID; +}; + +char MockSummaryData1::ID = 0; +char MockSummaryData2::ID = 0; +char MockSummaryData3::ID = 0; + +void PrintTo(const MockSummaryData1 &S, std::ostream *OS) { + *OS << "MockSummaryData1(" << S.getSummaryName().str().str() << ")"; +} +void PrintTo(const MockSummaryData2 &S, std::ostream *OS) { + *OS << "MockSummaryData2(" << S.getSummaryName().str().str() << ")"; +} +void PrintTo(const MockSummaryData3 &S, std::ostream *OS) { + *OS << "MockSummaryData3(" << S.getSummaryName().str().str() << ")"; +} + +struct TUSummaryBuilderTest : ssaf::TestFixture { + static llvm::SmallVector<SummaryName> summaryNames(const TUSummary &Summary) { + return llvm::to_vector(llvm::make_first_range(getData(Summary))); + } + + static llvm::SmallVector<EntityId> + entitiesOfSummary(const TUSummary &Summary, const SummaryName &Name) { + const auto &MappingIt = getData(Summary).find(Name); + if (MappingIt == getData(Summary).end()) + return {}; + return llvm::to_vector(llvm::make_first_range(MappingIt->second)); + } + + template <class ConcreteSummaryData> + static std::optional<ConcreteSummaryData> + getAsEntitySummary(const TUSummary &Summary, const SummaryName &Name, + EntityId E) { + static_assert(std::is_base_of_v<EntitySummary, ConcreteSummaryData>); + const auto &MappingIt = getData(Summary).find(Name); + if (MappingIt == getData(Summary).end()) + return std::nullopt; + auto SummaryIt = MappingIt->second.find(E); + if (SummaryIt == MappingIt->second.end()) + return std::nullopt; + assert(llvm::isa<ConcreteSummaryData>(*SummaryIt->second)); + return llvm::cast<ConcreteSummaryData>(*SummaryIt->second); + } +}; + +TEST_F(TUSummaryBuilderTest, AddEntity) { + TUSummary Summary = makeFakeSummary(); + TUSummaryBuilder Builder(Summary); + + EntityName EN1("c:@F@foo", "", /*Namespace=*/{}); + EntityName EN2("c:@F@bar", "", /*Namespace=*/{}); + + EntityLinkage MockLinkage; + EntityId ID = Builder.addEntity(EN1, MockLinkage); + EntityId IDAlias = Builder.addEntity(EN1, MockLinkage); + EXPECT_EQ(ID, IDAlias); // Idenpotency + + EntityId ID2 = Builder.addEntity(EN2, MockLinkage); + EXPECT_NE(ID, ID2); + EXPECT_NE(IDAlias, ID2); + + const EntityIdTable &IdTable = getIdTable(Summary); + EXPECT_EQ(IdTable.count(), 2U); + EXPECT_TRUE(IdTable.contains(EN1)); + EXPECT_TRUE(IdTable.contains(EN2)); + + const auto &Entities = getEntities(Summary); + EXPECT_EQ(Entities.size(), 2U); + ASSERT_EQ(Entities.count(ID), 1U); + EXPECT_EQ(Entities.find(ID)->second, MockLinkage); + + ASSERT_EQ(Entities.count(ID2), 1U); + EXPECT_EQ(Entities.find(ID2)->second, MockLinkage); +} + +TEST_F(TUSummaryBuilderTest, TUSummaryBuilderAddSingleFact) { + TUSummary Summary = makeFakeSummary(); + TUSummaryBuilder Builder(Summary); + + EntityId ID = addTestEntity(Builder, "c:@F@foo"); + SummaryName Name = addFactTo(Builder, ID, MockSummaryData1(10)); + + // Should have a summary type with an entity. + EXPECT_THAT(summaryNames(Summary), UnorderedElementsAre(Name)); + EXPECT_THAT(entitiesOfSummary(Summary, Name), UnorderedElementsAre(ID)); + + EXPECT_THAT(getAsEntitySummary<MockSummaryData1>(Summary, Name, ID), + Optional(Field(&MockSummaryData1::Value, 10))); +} + +TEST_F(TUSummaryBuilderTest, AddMultipleFactsToSameEntity) { + TUSummary Summary = makeFakeSummary(); + TUSummaryBuilder Builder(Summary); + EntityId ID = addTestEntity(Builder, "c:@F@foo"); + + // Add different summary types to the same entity. + SummaryName Name1 = addFactTo(Builder, ID, MockSummaryData1(42)); + SummaryName Name2 = addFactTo(Builder, ID, MockSummaryData2("test data")); + SummaryName Name3 = addFactTo(Builder, ID, MockSummaryData3(true)); + + // All Names must be unique + EXPECT_EQ((std::set<SummaryName>{Name1, Name2, Name3}.size()), 3U); + + // Should have 3 summary type with the same entity. + EXPECT_THAT(summaryNames(Summary), UnorderedElementsAre(Name1, Name2, Name3)); + EXPECT_THAT(entitiesOfSummary(Summary, Name1), UnorderedElementsAre(ID)); + EXPECT_THAT(entitiesOfSummary(Summary, Name2), UnorderedElementsAre(ID)); + EXPECT_THAT(entitiesOfSummary(Summary, Name3), UnorderedElementsAre(ID)); + + EXPECT_THAT(getAsEntitySummary<MockSummaryData1>(Summary, Name1, ID), + Optional(Field(&MockSummaryData1::Value, 42))); + EXPECT_THAT(getAsEntitySummary<MockSummaryData2>(Summary, Name2, ID), + Optional(Field(&MockSummaryData2::Text, "test data"))); + EXPECT_THAT(getAsEntitySummary<MockSummaryData3>(Summary, Name3, ID), + Optional(Field(&MockSummaryData3::Flag, true))); +} + +TEST_F(TUSummaryBuilderTest, AddSameFactTypeToMultipleEntities) { + TUSummary Summary = makeFakeSummary(); + TUSummaryBuilder Builder(Summary); + + EntityId ID1 = addTestEntity(Builder, "c:@F@foo"); + EntityId ID2 = addTestEntity(Builder, "c:@F@bar"); + EntityId ID3 = addTestEntity(Builder, "c:@F@baz"); + + // Add the same summary type to different entities. + SummaryName Name1 = addFactTo(Builder, ID1, MockSummaryData1(1)); + SummaryName Name2 = addFactTo(Builder, ID2, MockSummaryData1(2)); + SummaryName Name3 = addFactTo(Builder, ID3, MockSummaryData1(3)); + + // All 3 should be the same summary type. + EXPECT_THAT((llvm::ArrayRef{Name1, Name2, Name3}), testing::Each(Name1)); + + // Should have only 1 summary type with 3 entities. + EXPECT_THAT(summaryNames(Summary), UnorderedElementsAre(Name1)); + EXPECT_THAT(entitiesOfSummary(Summary, Name1), + UnorderedElementsAre(ID1, ID2, ID3)); + + EXPECT_THAT(getAsEntitySummary<MockSummaryData1>(Summary, Name1, ID1), + Optional(Field(&MockSummaryData1::Value, 1))); + EXPECT_THAT(getAsEntitySummary<MockSummaryData1>(Summary, Name2, ID2), + Optional(Field(&MockSummaryData1::Value, 2))); + EXPECT_THAT(getAsEntitySummary<MockSummaryData1>(Summary, Name3, ID3), + Optional(Field(&MockSummaryData1::Value, 3))); +} + +TEST_F(TUSummaryBuilderTest, AddFactReplacesExistingFact) { + TUSummary Summary = makeFakeSummary(); + TUSummaryBuilder Builder(Summary); + EntityId ID = addTestEntity(Builder, "c:@F@foo"); + + SummaryName Name = addFactTo(Builder, ID, MockSummaryData1(10)); + + // Check the initial value. + EXPECT_THAT(summaryNames(Summary), UnorderedElementsAre(Name)); + EXPECT_THAT(entitiesOfSummary(Summary, Name), UnorderedElementsAre(ID)); + EXPECT_THAT(getAsEntitySummary<MockSummaryData1>(Summary, Name, ID), + Optional(Field(&MockSummaryData1::Value, 10))); + + // Add another fact with the same SummaryName. + // This should replace the previous fact. + SummaryName ReplacementName = addFactTo(Builder, ID, MockSummaryData1(20)); + ASSERT_EQ(ReplacementName, Name); + + // Check that the value was replaced. + EXPECT_THAT(summaryNames(Summary), UnorderedElementsAre(Name)); + EXPECT_THAT(entitiesOfSummary(Summary, Name), UnorderedElementsAre(ID)); + EXPECT_THAT(getAsEntitySummary<MockSummaryData1>(Summary, Name, ID), + Optional(Field(&MockSummaryData1::Value, 20))); +} + +TEST_F(TUSummaryBuilderTest, AddFactsComplexScenario) { + TUSummary Summary = makeFakeSummary(); + TUSummaryBuilder Builder(Summary); + + EntityId ID1 = addTestEntity(Builder, "c:@F@foo"); + EntityId ID2 = addTestEntity(Builder, "c:@F@bar"); + + SummaryName Name1 = addFactTo(Builder, ID1, MockSummaryData1(10)); + SummaryName Name2 = addFactTo(Builder, ID1, MockSummaryData2("twenty")); + + SummaryName Name3 = addFactTo(Builder, ID2, MockSummaryData1(30)); + SummaryName Name4 = addFactTo(Builder, ID2, MockSummaryData3(true)); + + // Check that we have only 3 distinct summary names. + EXPECT_EQ(Name1, Name3); + EXPECT_THAT((std::set{Name1, Name2, Name3, Name4}), + UnorderedElementsAre(Name1, Name2, Name4)); + + // Check that we have two facts for the two summaries each. + EXPECT_THAT(summaryNames(Summary), UnorderedElementsAre(Name1, Name2, Name4)); + EXPECT_THAT(entitiesOfSummary(Summary, Name1), + UnorderedElementsAre(ID1, ID2)); + EXPECT_THAT(entitiesOfSummary(Summary, Name2), UnorderedElementsAre(ID1)); + EXPECT_THAT(entitiesOfSummary(Summary, Name4), UnorderedElementsAre(ID2)); + + EXPECT_THAT(getAsEntitySummary<MockSummaryData1>(Summary, Name1, ID1), + Optional(Field(&MockSummaryData1::Value, 10))); + EXPECT_THAT(getAsEntitySummary<MockSummaryData1>(Summary, Name1, ID2), + Optional(Field(&MockSummaryData1::Value, 30))); + EXPECT_THAT(getAsEntitySummary<MockSummaryData2>(Summary, Name2, ID1), + Optional(Field(&MockSummaryData2::Text, "twenty"))); + EXPECT_THAT(getAsEntitySummary<MockSummaryData3>(Summary, Name4, ID2), + Optional(Field(&MockSummaryData3::Flag, true))); + + // Replace a fact of Name1 on entity 1 with the new value 50. + SummaryName Name5 = addFactTo(Builder, ID1, MockSummaryData1(50)); + ASSERT_EQ(Name5, Name1); + + // Check that the summary names and entity IDs didn't change. + EXPECT_THAT(summaryNames(Summary), UnorderedElementsAre(Name1, Name2, Name4)); + EXPECT_THAT(entitiesOfSummary(Summary, Name1), + UnorderedElementsAre(ID1, ID2)); + EXPECT_THAT(entitiesOfSummary(Summary, Name2), UnorderedElementsAre(ID1)); + EXPECT_THAT(entitiesOfSummary(Summary, Name4), UnorderedElementsAre(ID2)); + + // Check the Name1 ID1 entity summary value was changed to 50. + EXPECT_THAT(getAsEntitySummary<MockSummaryData1>(Summary, Name1, ID1), + Optional(Field(&MockSummaryData1::Value, 50))); + + // Check that the rest remained the same. + EXPECT_THAT(getAsEntitySummary<MockSummaryData1>(Summary, Name1, ID2), + Optional(Field(&MockSummaryData1::Value, 30))); + EXPECT_THAT(getAsEntitySummary<MockSummaryData2>(Summary, Name2, ID1), + Optional(Field(&MockSummaryData2::Text, "twenty"))); + EXPECT_THAT(getAsEntitySummary<MockSummaryData3>(Summary, Name4, ID2), + Optional(Field(&MockSummaryData3::Flag, true))); +} + +} // namespace diff --git a/clang/unittests/Analysis/Scalable/TestFixture.cpp b/clang/unittests/Analysis/Scalable/TestFixture.cpp new file mode 100644 index 0000000000000..dfe6ae1dfc7b9 --- /dev/null +++ b/clang/unittests/Analysis/Scalable/TestFixture.cpp @@ -0,0 +1,29 @@ +//===- unittests/Analysis/Scalable/TestFixture.cpp ------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "TestFixture.h" +#include "clang/Analysis/Scalable/Model/EntityId.h" +#include "llvm/Support/raw_ostream.h" +#include <ostream> +#include <string> + +using namespace clang; +using namespace ssaf; + +template <class T> static std::string asString(const T &Obj) { + std::string Repr; + llvm::raw_string_ostream(Repr) << Obj; + return Repr; +} + +void TestFixture::PrintTo(const EntityId &E, std::ostream *OS) { + *OS << asString(E); +} +void TestFixture::PrintTo(const SummaryName &N, std::ostream *OS) { + *OS << asString(N); +} diff --git a/clang/unittests/Analysis/Scalable/TestFixture.h b/clang/unittests/Analysis/Scalable/TestFixture.h new file mode 100644 index 0000000000000..44d07dcafc21f --- /dev/null +++ b/clang/unittests/Analysis/Scalable/TestFixture.h @@ -0,0 +1,34 @@ +//===- TestFixture.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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_UNITTESTS_ANALYSIS_SCALABLE_TESTFIXTURE_H +#define LLVM_CLANG_UNITTESTS_ANALYSIS_SCALABLE_TESTFIXTURE_H + +#include "clang/Analysis/Scalable/Model/BuildNamespace.h" +#include "clang/Analysis/Scalable/Model/EntityId.h" +#include "clang/Analysis/Scalable/Model/SummaryName.h" +#include "clang/Analysis/Scalable/TUSummary/TUSummary.h" +#include "gtest/gtest.h" +#include <iosfwd> + +namespace clang::ssaf { + +class TestFixture : public ::testing::Test { +protected: +#define FIELD(CLASS, FIELD_NAME) \ + static const auto &get##FIELD_NAME(const CLASS &X) { return X.FIELD_NAME; } \ + static auto &get##FIELD_NAME(CLASS &X) { return X.FIELD_NAME; } +#include "clang/Analysis/Scalable/Model/PrivateFieldNames.def" + + static void PrintTo(const EntityId &, std::ostream *); + static void PrintTo(const SummaryName &, std::ostream *); +}; + +} // namespace clang::ssaf + +#endif // LLVM_CLANG_UNITTESTS_ANALYSIS_SCALABLE_TESTFIXTURE_H _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
