https://github.com/steakhal created https://github.com/llvm/llvm-project/pull/181220
Also adds a ssaf::TestFixture to provide access to the private fields of the SSAF object for introspection. Assisted-By: claude rdar://168773578 From d3e254571e30ff1e7a0087ee03b8be8b10b375e7 Mon Sep 17 00:00:00 2001 From: Balazs Benics <[email protected]> Date: Thu, 12 Feb 2026 20:42:39 +0100 Subject: [PATCH] [clang][ssaf] Implement TUSummaryBuilder with test infrastructure Also adds a ssaf::TestFixture to provide access to the private fields of the SSAF object for introspection. Assisted-By: claude rdar://168773578 --- .../Analysis/Scalable/Model/BuildNamespace.h | 7 +- .../clang/Analysis/Scalable/Model/EntityId.h | 1 + .../Analysis/Scalable/Model/EntityName.h | 2 +- .../Analysis/Scalable/Model/SummaryName.h | 1 + .../Analysis/Scalable/TUSummary/TUSummary.h | 2 + .../Scalable/TUSummary/TUSummaryBuilder.h | 22 +- clang/lib/Analysis/Scalable/CMakeLists.txt | 1 + .../Scalable/TUSummary/TUSummaryBuilder.cpp | 17 + .../Analysis/Scalable/CMakeLists.txt | 2 + .../Registries/MockTUSummaryBuilder.h | 1 + .../SummaryExtractorRegistryTest.cpp | 15 +- .../Scalable/TUSummaryBuilderTest.cpp | 297 ++++++++++++++++++ .../Analysis/Scalable/TestFixture.cpp | 29 ++ .../unittests/Analysis/Scalable/TestFixture.h | 34 ++ 14 files changed, 422 insertions(+), 9 deletions(-) create mode 100644 clang/lib/Analysis/Scalable/TUSummary/TUSummaryBuilder.cpp 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/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 6fa059445d853..755a60e40cc68 100644 --- a/clang/include/clang/Analysis/Scalable/Model/EntityId.h +++ b/clang/include/clang/Analysis/Scalable/Model/EntityId.h @@ -29,6 +29,7 @@ class EntityIdTable; /// \see EntityIdTable class EntityId { friend class EntityIdTable; + friend class TestFixture; size_t Index; 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/TUSummary.h b/clang/include/clang/Analysis/Scalable/TUSummary/TUSummary.h index 4af1c70e1a488..7d25234a7ca2c 100644 --- a/clang/include/clang/Analysis/Scalable/TUSummary/TUSummary.h +++ b/clang/include/clang/Analysis/Scalable/TUSummary/TUSummary.h @@ -32,6 +32,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..ad41bfb9450bc 100644 --- a/clang/include/clang/Analysis/Scalable/TUSummary/TUSummaryBuilder.h +++ b/clang/include/clang/Analysis/Scalable/TUSummary/TUSummaryBuilder.h @@ -9,10 +9,30 @@ #ifndef LLVM_CLANG_ANALYSIS_SCALABLE_TUSUMMARY_TUSUMMARYBUILDER_H #define LLVM_CLANG_ANALYSIS_SCALABLE_TUSUMMARY_TUSUMMARYBUILDER_H +#include <memory> + namespace clang::ssaf { +class EntityId; +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); + + /// 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 4145e7a521ba4..1d51a2fa1e67f 100644 --- a/clang/lib/Analysis/Scalable/CMakeLists.txt +++ b/clang/lib/Analysis/Scalable/CMakeLists.txt @@ -9,6 +9,7 @@ add_clang_library(clangAnalysisScalable Model/EntityName.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..f5ed26b144522 --- /dev/null +++ b/clang/lib/Analysis/Scalable/TUSummary/TUSummaryBuilder.cpp @@ -0,0 +1,17 @@ +#include "clang/Analysis/Scalable/TUSummary/TUSummaryBuilder.h" +#include "clang/Analysis/Scalable/Model/EntityId.h" +#include "clang/Analysis/Scalable/TUSummary/TUSummary.h" +#include <memory> + +using namespace clang; +using namespace ssaf; + +EntityId TUSummaryBuilder::addEntity(const EntityName &E) { + return Summary.IdTable.getId(E); +} + +void TUSummaryBuilder::addFact(EntityId ContributingEntity, + std::unique_ptr<EntitySummary> NewData) { + Summary.Data[NewData->getSummaryName()][ContributingEntity] = + std::move(NewData); +} 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/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..2e40b5caf9ce5 --- /dev/null +++ b/clang/unittests/Analysis/Scalable/TUSummaryBuilderTest.cpp @@ -0,0 +1,297 @@ +//===- 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() { + return BuildNamespace(BuildNamespaceKind::CompilationUnit, "Mock.cpp"); +} + +[[nodiscard]] +static EntityId addTestEntity(TUSummaryBuilder &Builder, llvm::StringRef USR) { + return Builder.addEntity(EntityName(USR, /*Suffix=*/"", /*Namespace=*/{})); +} + +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 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 { + [[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) { + TUSummary Summary = makeFakeSummary(); + TUSummaryBuilder Builder(Summary); + + EntityName EN1("c:@F@foo", "", /*Namespace=*/{}); + EntityName EN2("c:@F@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, 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..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..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
