[clang] 98c76d3 - [clang][ssaf] Implement TUSummaryBuilder with test infrastructure (#181220)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Feb 17 11:33:57 PST 2026
Author: Balázs Benics
Date: 2026-02-17T19:33:51Z
New Revision: 98c76d36432ccd85b88a82949391bd0ce7243bfd
URL: https://github.com/llvm/llvm-project/commit/98c76d36432ccd85b88a82949391bd0ce7243bfd
DIFF: https://github.com/llvm/llvm-project/commit/98c76d36432ccd85b88a82949391bd0ce7243bfd.diff
LOG: [clang][ssaf] Implement TUSummaryBuilder with test infrastructure (#181220)
Also adds a ssaf::TestFixture to provide access to the private fields of
the SSAF object for introspection.
Assisted-By: claude
rdar://168773578
Added:
clang/lib/Analysis/Scalable/TUSummary/TUSummaryBuilder.cpp
clang/unittests/Analysis/Scalable/TUSummaryBuilderTest.cpp
clang/unittests/Analysis/Scalable/TestFixture.cpp
clang/unittests/Analysis/Scalable/TestFixture.h
Modified:
clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h
clang/include/clang/Analysis/Scalable/Model/EntityId.h
clang/include/clang/Analysis/Scalable/Model/EntityIdTable.h
clang/include/clang/Analysis/Scalable/Model/EntityLinkage.h
clang/include/clang/Analysis/Scalable/Model/EntityName.h
clang/include/clang/Analysis/Scalable/Model/SummaryName.h
clang/include/clang/Analysis/Scalable/TUSummary/EntitySummary.h
clang/include/clang/Analysis/Scalable/TUSummary/TUSummary.h
clang/include/clang/Analysis/Scalable/TUSummary/TUSummaryBuilder.h
clang/lib/Analysis/Scalable/CMakeLists.txt
clang/unittests/Analysis/Scalable/CMakeLists.txt
clang/unittests/Analysis/Scalable/Registries/MockTUSummaryBuilder.h
clang/unittests/Analysis/Scalable/Registries/SummaryExtractorRegistryTest.cpp
Removed:
################################################################################
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/EntityId.h b/clang/include/clang/Analysis/Scalable/Model/EntityId.h
index e348486386cb6..00a6f3447bbce 100644
--- a/clang/include/clang/Analysis/Scalable/Model/EntityId.h
+++ b/clang/include/clang/Analysis/Scalable/Model/EntityId.h
@@ -30,6 +30,7 @@ class EntityIdTable;
class EntityId {
friend class EntityIdTable;
friend class SerializationFormat;
+ friend class TestFixture;
size_t Index;
diff --git a/clang/include/clang/Analysis/Scalable/Model/EntityIdTable.h b/clang/include/clang/Analysis/Scalable/Model/EntityIdTable.h
index a1099c4e4d0f8..6c5f27907adb4 100644
--- a/clang/include/clang/Analysis/Scalable/Model/EntityIdTable.h
+++ b/clang/include/clang/Analysis/Scalable/Model/EntityIdTable.h
@@ -22,6 +22,7 @@ namespace clang::ssaf {
/// Entities are never removed.
class EntityIdTable {
friend class SerializationFormat;
+ friend class TestFixture;
std::map<EntityName, EntityId> Entities;
diff --git a/clang/include/clang/Analysis/Scalable/Model/EntityLinkage.h b/clang/include/clang/Analysis/Scalable/Model/EntityLinkage.h
index ba5f7d3073a30..155ec69ea0b3a 100644
--- a/clang/include/clang/Analysis/Scalable/Model/EntityLinkage.h
+++ b/clang/include/clang/Analysis/Scalable/Model/EntityLinkage.h
@@ -18,6 +18,7 @@ namespace clang::ssaf {
/// across translation units.
class EntityLinkage {
friend class SerializationFormat;
+ friend class TestFixture;
public:
enum class LinkageType {
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/SummaryName.h b/clang/include/clang/Analysis/Scalable/Model/SummaryName.h
index 785fe0eb10372..36a8cf5da78b1 100644
--- a/clang/include/clang/Analysis/Scalable/Model/SummaryName.h
+++ b/clang/include/clang/Analysis/Scalable/Model/SummaryName.h
@@ -31,6 +31,7 @@ class SummaryName {
private:
std::string Name;
+ friend class TestFixture;
};
} // namespace clang::ssaf
diff --git a/clang/include/clang/Analysis/Scalable/TUSummary/EntitySummary.h b/clang/include/clang/Analysis/Scalable/TUSummary/EntitySummary.h
index 4bdb385d49a01..dddd69cb7d372 100644
--- a/clang/include/clang/Analysis/Scalable/TUSummary/EntitySummary.h
+++ b/clang/include/clang/Analysis/Scalable/TUSummary/EntitySummary.h
@@ -10,6 +10,7 @@
#define LLVM_CLANG_ANALYSIS_SCALABLE_TUSUMMARY_ENTITYSUMMARY_H
#include "clang/Analysis/Scalable/Model/SummaryName.h"
+#include <type_traits>
namespace clang::ssaf {
@@ -20,6 +21,10 @@ class EntitySummary {
virtual SummaryName getSummaryName() const = 0;
};
+template <typename Derived>
+using DerivesFromEntitySummary =
+ std::enable_if_t<std::is_base_of_v<EntitySummary, Derived>>;
+
} // namespace clang::ssaf
#endif // LLVM_CLANG_ANALYSIS_SCALABLE_TUSUMMARY_ENTITYSUMMARY_H
diff --git a/clang/include/clang/Analysis/Scalable/TUSummary/TUSummary.h b/clang/include/clang/Analysis/Scalable/TUSummary/TUSummary.h
index 2520ce87f3959..40cb7d582cf6e 100644
--- a/clang/include/clang/Analysis/Scalable/TUSummary/TUSummary.h
+++ b/clang/include/clang/Analysis/Scalable/TUSummary/TUSummary.h
@@ -36,6 +36,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..64d8adc0791c1 100644
--- a/clang/include/clang/Analysis/Scalable/TUSummary/TUSummaryBuilder.h
+++ b/clang/include/clang/Analysis/Scalable/TUSummary/TUSummaryBuilder.h
@@ -9,12 +9,68 @@
#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_TUSUMMARY_TUSUMMARYBUILDER_H
#define LLVM_CLANG_ANALYSIS_SCALABLE_TUSUMMARY_TUSUMMARYBUILDER_H
+#include "clang/Analysis/Scalable/Model/EntityId.h"
+#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
+#include <memory>
+#include <utility>
+
namespace clang::ssaf {
+class EntityName;
+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);
+
+ /// Associate the \p Data \c EntitySummary with the \p Entity.
+ /// This consumes the \p Data only if \p Entity wasn't associated yet with the
+ /// same kind of \c EntitySummary.
+ /// \returns a pointer to the \c EntitySummary and whether it inserted or not.
+ template <typename ConcreteEntitySummary,
+ DerivesFromEntitySummary<ConcreteEntitySummary> * = nullptr>
+ std::pair<EntitySummary *, bool>
+ addSummary(EntityId Entity, std::unique_ptr<ConcreteEntitySummary> &&Data);
+
+private:
+ TUSummary &Summary;
+
+ std::pair<EntitySummary *, bool>
+ addSummaryImpl(EntityId Entity, std::unique_ptr<EntitySummary> &&Data);
};
+// Why is this a template?
+//
+// We use template here to avoid an implicit conversion from
+// `std::unique_ptr<ConcreteEntitySummary>` to `std::unique_ptr<EntitySummary>`
+// because constructing that implicit temporary would unconditionally "consume"
+// the Data. This would make it impossible to recover from the call-site the
+// Data you pass in even if no insertion happens.
+template <typename ConcreteEntitySummary,
+ DerivesFromEntitySummary<ConcreteEntitySummary> *>
+std::pair<EntitySummary *, bool>
+TUSummaryBuilder::addSummary(EntityId Entity,
+ std::unique_ptr<ConcreteEntitySummary> &&Data) {
+ // Prepare a unique_ptr of the base type to avoid implicit conversions at the
+ // call-site.
+ std::unique_ptr<EntitySummary> TypeErasedData = std::move(Data);
+
+ // This only moves (consumes) TypeErasedData if insertion happened.
+ // Otherwise it doesn't touch the `TypeErasedData`.
+ auto [It, Inserted] = addSummaryImpl(Entity, std::move(TypeErasedData));
+
+ // Move it back on failue to keep the `Data` unconsumed.
+ if (!Inserted) {
+ Data = std::unique_ptr<ConcreteEntitySummary>(
+ static_cast<ConcreteEntitySummary *>(TypeErasedData.release()));
+ }
+ return {It, Inserted};
+}
+
} // namespace clang::ssaf
#endif // LLVM_CLANG_ANALYSIS_SCALABLE_TUSUMMARY_TUSUMMARYBUILDER_H
diff --git a/clang/lib/Analysis/Scalable/CMakeLists.txt b/clang/lib/Analysis/Scalable/CMakeLists.txt
index 522fc9dcf078d..a7311e4d69d67 100644
--- a/clang/lib/Analysis/Scalable/CMakeLists.txt
+++ b/clang/lib/Analysis/Scalable/CMakeLists.txt
@@ -10,6 +10,7 @@ add_clang_library(clangAnalysisScalable
Serialization/JSONFormat.cpp
Serialization/SerializationFormatRegistry.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..61c332e831ccb
--- /dev/null
+++ b/clang/lib/Analysis/Scalable/TUSummary/TUSummaryBuilder.cpp
@@ -0,0 +1,21 @@
+#include "clang/Analysis/Scalable/TUSummary/TUSummaryBuilder.h"
+#include "clang/Analysis/Scalable/Model/EntityId.h"
+#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
+#include "clang/Analysis/Scalable/TUSummary/TUSummary.h"
+#include <memory>
+#include <utility>
+
+using namespace clang;
+using namespace ssaf;
+
+EntityId TUSummaryBuilder::addEntity(const EntityName &E) {
+ return Summary.IdTable.getId(E);
+}
+
+std::pair<EntitySummary *, bool>
+TUSummaryBuilder::addSummaryImpl(EntityId Entity,
+ std::unique_ptr<EntitySummary> &&Data) {
+ auto &EntitySummaries = Summary.Data[Data->getSummaryName()];
+ auto [It, Inserted] = EntitySummaries.try_emplace(Entity, std::move(Data));
+ return {It->second.get(), Inserted};
+}
diff --git a/clang/unittests/Analysis/Scalable/CMakeLists.txt b/clang/unittests/Analysis/Scalable/CMakeLists.txt
index f1e1c874cc5cd..5529ca06de170 100644
--- a/clang/unittests/Analysis/Scalable/CMakeLists.txt
+++ b/clang/unittests/Analysis/Scalable/CMakeLists.txt
@@ -13,6 +13,8 @@ add_distinct_clang_unittest(ClangScalableAnalysisTests
Registries/SummaryExtractorRegistryTest.cpp
Serialization/JSONFormatTest.cpp
SummaryNameTest.cpp
+ TestFixture.cpp
+ TUSummaryBuilderTest.cpp
CLANG_LIBS
clangAnalysisScalable
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..a17a4f145b038 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,11 @@
using namespace clang;
using namespace ssaf;
+[[nodiscard]]
+static TUSummary makeFakeSummary() {
+ return BuildNamespace(BuildNamespaceKind::CompilationUnit, "Mock.cpp");
+}
+
namespace {
TEST(SummaryExtractorRegistryTest, isTUSummaryExtractorRegistered) {
@@ -39,7 +45,8 @@ TEST(SummaryExtractorRegistryTest, EnumeratingRegistryEntries) {
}
TEST(SummaryExtractorRegistryTest, InstantiatingExtractor1) {
- MockTUSummaryBuilder FakeBuilder;
+ TUSummary Summary = makeFakeSummary();
+ MockTUSummaryBuilder FakeBuilder(Summary);
{
auto Consumer =
makeTUSummaryExtractor("MockSummaryExtractor1", FakeBuilder);
@@ -52,7 +59,8 @@ TEST(SummaryExtractorRegistryTest, InstantiatingExtractor1) {
}
TEST(SummaryExtractorRegistryTest, InstantiatingExtractor2) {
- MockTUSummaryBuilder FakeBuilder;
+ TUSummary Summary = makeFakeSummary();
+ MockTUSummaryBuilder FakeBuilder(Summary);
{
auto Consumer =
makeTUSummaryExtractor("MockSummaryExtractor2", FakeBuilder);
@@ -65,7 +73,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);
diff --git a/clang/unittests/Analysis/Scalable/TUSummaryBuilderTest.cpp b/clang/unittests/Analysis/Scalable/TUSummaryBuilderTest.cpp
new file mode 100644
index 0000000000000..f70b39b97b841
--- /dev/null
+++ b/clang/unittests/Analysis/Scalable/TUSummaryBuilderTest.cpp
@@ -0,0 +1,269 @@
+//===- 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 <memory>
+#include <type_traits>
+
+using namespace clang;
+using namespace ssaf;
+
+using llvm::SmallVector;
+using testing::Field;
+using testing::Optional;
+using testing::UnorderedElementsAre;
+
+[[nodiscard]]
+static EntityId addTestEntity(TUSummaryBuilder &Builder, llvm::StringRef USR) {
+ return Builder.addEntity(EntityName(USR, /*Suffix=*/"", /*Namespace=*/{}));
+}
+
+struct SummaryResult {
+ EntitySummary *Summary;
+ bool Inserted;
+};
+
+template <class ConcreteEntitySummary>
+[[nodiscard]]
+static auto addSummaryTo(TUSummaryBuilder &Builder, EntityId ID,
+ ConcreteEntitySummary Summary) {
+ static_assert(std::is_base_of_v<EntitySummary, ConcreteEntitySummary>);
+ auto NewSummary = std::make_unique<ConcreteEntitySummary>(std::move(Summary));
+ SummaryName Name = NewSummary->getSummaryName();
+ auto [Place, Inserted] = Builder.addSummary(ID, std::move(NewSummary));
+ return std::pair{Name, SummaryResult{Place, Inserted}};
+}
+
+namespace {
+
+// Mock EntitySummary classes for testing
+struct MockSummaryData1 final : public EntitySummary {
+ explicit MockSummaryData1(int Value) : Value(Value) {}
+ SummaryName getSummaryName() const override {
+ return SummaryName("MockSummary1");
+ }
+ int Value;
+};
+
+struct MockSummaryData2 final : public EntitySummary {
+ explicit MockSummaryData2(std::string Text) : Text(std::move(Text)) {}
+ SummaryName getSummaryName() const override {
+ return SummaryName("MockSummary2");
+ }
+ std::string Text;
+};
+
+struct MockSummaryData3 final : public EntitySummary {
+ explicit MockSummaryData3(bool Flag) : Flag(Flag) {}
+ SummaryName getSummaryName() const override {
+ return SummaryName("MockSummary3");
+ }
+ bool Flag;
+};
+
+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 {
+ TUSummary Summary =
+ BuildNamespace(BuildNamespaceKind::CompilationUnit, "Mock.cpp");
+ TUSummaryBuilder Builder = TUSummaryBuilder(this->Summary);
+
+ [[nodiscard]] static SmallVector<SummaryName>
+ summaryNames(const TUSummary &Summary) {
+ return llvm::to_vector(llvm::make_first_range(getData(Summary)));
+ }
+
+ [[nodiscard]] static 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>
+ [[nodiscard]] 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(Name == SummaryIt->second->getSummaryName());
+ return static_cast<const ConcreteSummaryData &>(*SummaryIt->second);
+ }
+};
+
+TEST_F(TUSummaryBuilderTest, AddEntity) {
+ EntityName EN1("c:@F at foo", "", /*Namespace=*/{});
+ EntityName EN2("c:@F at bar", "", /*Namespace=*/{});
+
+ EntityId ID = Builder.addEntity(EN1);
+ EntityId IDAlias = Builder.addEntity(EN1);
+ EXPECT_EQ(ID, IDAlias); // Idenpotency
+
+ EntityId ID2 = Builder.addEntity(EN2);
+ 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));
+}
+
+TEST_F(TUSummaryBuilderTest, TUSummaryBuilderAddSingleSummary) {
+ EntityId ID = addTestEntity(Builder, "c:@F at foo");
+ auto [Name, Res] = addSummaryTo(Builder, ID, MockSummaryData1(10));
+ ASSERT_TRUE(Res.Inserted);
+ ASSERT_TRUE(Res.Summary);
+
+ // 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, AddMultipleSummariesToSameEntity) {
+ EntityId ID = addTestEntity(Builder, "c:@F at foo");
+
+ // Add
diff erent summary types to the same entity.
+ auto [Name1, Res1] = addSummaryTo(Builder, ID, MockSummaryData1(42));
+ auto [Name2, Res2] = addSummaryTo(Builder, ID, MockSummaryData2("test data"));
+ auto [Name3, Res3] = addSummaryTo(Builder, ID, MockSummaryData3(true));
+ ASSERT_TRUE(Res1.Inserted);
+ ASSERT_TRUE(Res2.Inserted);
+ ASSERT_TRUE(Res3.Inserted);
+ ASSERT_TRUE(Res1.Summary);
+ ASSERT_TRUE(Res2.Summary);
+ ASSERT_TRUE(Res3.Summary);
+
+ // 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, AddSameSummaryTypeToMultipleEntities) {
+ EntityId ID1 = addTestEntity(Builder, "c:@F at foo");
+ EntityId ID2 = addTestEntity(Builder, "c:@F at bar");
+ EntityId ID3 = addTestEntity(Builder, "c:@F at baz");
+
+ // Add the same summary type to
diff erent entities.
+ auto [Name1, Res1] = addSummaryTo(Builder, ID1, MockSummaryData1(1));
+ auto [Name2, Res2] = addSummaryTo(Builder, ID2, MockSummaryData1(2));
+ auto [Name3, Res3] = addSummaryTo(Builder, ID3, MockSummaryData1(3));
+ ASSERT_TRUE(Res1.Inserted);
+ ASSERT_TRUE(Res2.Inserted);
+ ASSERT_TRUE(Res3.Inserted);
+ ASSERT_TRUE(Res1.Summary);
+ ASSERT_TRUE(Res2.Summary);
+ ASSERT_TRUE(Res3.Summary);
+
+ // 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, AddConflictingSummaryToSameEntity) {
+ EntityId ID = addTestEntity(Builder, "c:@F at foo");
+
+ auto [Name, Res] = addSummaryTo(Builder, ID, MockSummaryData1(10));
+ ASSERT_TRUE(Res.Inserted);
+ ASSERT_TRUE(Res.Summary);
+
+ // 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)));
+
+ // This is a
diff erent summary of the same kind.
+ auto NewSummary = std::make_unique<MockSummaryData1>(20);
+ ASSERT_EQ(NewSummary->getSummaryName(), Name);
+
+ // Let's add this
diff erent summary.
+ // This should keep the map intact and give us the existing entity summary.
+ auto [Slot, Inserted] = Builder.addSummary(ID, std::move(NewSummary));
+ ASSERT_FALSE(Inserted);
+ ASSERT_TRUE(Slot);
+
+ // Check that the summary object is not consumed and remained the same.
+ ASSERT_TRUE(NewSummary);
+ ASSERT_EQ(static_cast<MockSummaryData1 *>(NewSummary.get())->Value, 20);
+
+ // Check that the Slot refers to the existing entity summary.
+ ASSERT_EQ(Slot->getSummaryName(), Name);
+ ASSERT_EQ(static_cast<MockSummaryData1 *>(Slot)->Value, 10);
+
+ // Check that the values remained the same.
+ 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)));
+
+ // We can update the existing summary.
+ static_cast<MockSummaryData1 *>(Res.Summary)->Value = 30;
+
+ // Check that the values remained the same except what we updated.
+ EXPECT_THAT(summaryNames(Summary), UnorderedElementsAre(Name));
+ EXPECT_THAT(entitiesOfSummary(Summary, Name), UnorderedElementsAre(ID));
+ EXPECT_THAT(getAsEntitySummary<MockSummaryData1>(Summary, Name, ID),
+ Optional(Field(&MockSummaryData1::Value, 30)));
+}
+
+} // namespace
diff --git a/clang/unittests/Analysis/Scalable/TestFixture.cpp b/clang/unittests/Analysis/Scalable/TestFixture.cpp
new file mode 100644
index 0000000000000..c3c7027c1dc3f
--- /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 << "EntityId(" << E.Index << ")";
+}
+void TestFixture::PrintTo(const SummaryName &N, std::ostream *OS) {
+ *OS << "SummaryName(" << N.Name << ")";
+}
diff --git a/clang/unittests/Analysis/Scalable/TestFixture.h b/clang/unittests/Analysis/Scalable/TestFixture.h
new file mode 100644
index 0000000000000..427769819a703
--- /dev/null
+++ b/clang/unittests/Analysis/Scalable/TestFixture.h
@@ -0,0 +1,36 @@
+//===- 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/EntityIdTable.h"
+#include "clang/Analysis/Scalable/Model/EntityLinkage.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
More information about the cfe-commits
mailing list