[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
This commit is contained in:
parent
637d3dc122
commit
98c76d3643
@ -63,6 +63,7 @@ public:
|
||||
bool operator<(const BuildNamespace &Other) const;
|
||||
|
||||
friend class SerializationFormat;
|
||||
friend class TestFixture;
|
||||
};
|
||||
|
||||
/// Represents a hierarchical sequence of build namespaces.
|
||||
@ -75,8 +76,6 @@ public:
|
||||
/// 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 @@ public:
|
||||
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
|
||||
|
||||
@ -30,6 +30,7 @@ class EntityIdTable;
|
||||
class EntityId {
|
||||
friend class EntityIdTable;
|
||||
friend class SerializationFormat;
|
||||
friend class TestFixture;
|
||||
|
||||
size_t Index;
|
||||
|
||||
|
||||
@ -22,6 +22,7 @@ namespace clang::ssaf {
|
||||
/// Entities are never removed.
|
||||
class EntityIdTable {
|
||||
friend class SerializationFormat;
|
||||
friend class TestFixture;
|
||||
|
||||
std::map<EntityName, EntityId> Entities;
|
||||
|
||||
|
||||
@ -18,6 +18,7 @@ namespace clang::ssaf {
|
||||
/// across translation units.
|
||||
class EntityLinkage {
|
||||
friend class SerializationFormat;
|
||||
friend class TestFixture;
|
||||
|
||||
public:
|
||||
enum class LinkageType {
|
||||
|
||||
@ -47,8 +47,8 @@ public:
|
||||
/// \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
|
||||
|
||||
@ -31,6 +31,7 @@ public:
|
||||
|
||||
private:
|
||||
std::string Name;
|
||||
friend class TestFixture;
|
||||
};
|
||||
|
||||
} // namespace clang::ssaf
|
||||
|
||||
@ -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 @@ public:
|
||||
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
|
||||
|
||||
@ -36,6 +36,8 @@ public:
|
||||
TUSummary(BuildNamespace TUNamespace) : TUNamespace(std::move(TUNamespace)) {}
|
||||
|
||||
friend class SerializationFormat;
|
||||
friend class TestFixture;
|
||||
friend class TUSummaryBuilder;
|
||||
};
|
||||
|
||||
} // namespace clang::ssaf
|
||||
|
||||
@ -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
|
||||
|
||||
@ -10,6 +10,7 @@ add_clang_library(clangAnalysisScalable
|
||||
Serialization/JSONFormat.cpp
|
||||
Serialization/SerializationFormatRegistry.cpp
|
||||
TUSummary/ExtractorRegistry.cpp
|
||||
TUSummary/TUSummaryBuilder.cpp
|
||||
|
||||
LINK_LIBS
|
||||
clangAST
|
||||
|
||||
21
clang/lib/Analysis/Scalable/TUSummary/TUSummaryBuilder.cpp
Normal file
21
clang/lib/Analysis/Scalable/TUSummary/TUSummaryBuilder.cpp
Normal file
@ -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};
|
||||
}
|
||||
@ -13,6 +13,8 @@ add_distinct_clang_unittest(ClangScalableAnalysisTests
|
||||
Registries/SummaryExtractorRegistryTest.cpp
|
||||
Serialization/JSONFormatTest.cpp
|
||||
SummaryNameTest.cpp
|
||||
TestFixture.cpp
|
||||
TUSummaryBuilderTest.cpp
|
||||
|
||||
CLANG_LIBS
|
||||
clangAnalysisScalable
|
||||
|
||||
@ -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); }
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
269
clang/unittests/Analysis/Scalable/TUSummaryBuilderTest.cpp
Normal file
269
clang/unittests/Analysis/Scalable/TUSummaryBuilderTest.cpp
Normal file
@ -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@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, TUSummaryBuilderAddSingleSummary) {
|
||||
EntityId ID = addTestEntity(Builder, "c:@F@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@foo");
|
||||
|
||||
// Add different 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@foo");
|
||||
EntityId ID2 = addTestEntity(Builder, "c:@F@bar");
|
||||
EntityId ID3 = addTestEntity(Builder, "c:@F@baz");
|
||||
|
||||
// Add the same summary type to different 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@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 different summary of the same kind.
|
||||
auto NewSummary = std::make_unique<MockSummaryData1>(20);
|
||||
ASSERT_EQ(NewSummary->getSummaryName(), Name);
|
||||
|
||||
// Let's add this different 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
|
||||
29
clang/unittests/Analysis/Scalable/TestFixture.cpp
Normal file
29
clang/unittests/Analysis/Scalable/TestFixture.cpp
Normal file
@ -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 << ")";
|
||||
}
|
||||
36
clang/unittests/Analysis/Scalable/TestFixture.h
Normal file
36
clang/unittests/Analysis/Scalable/TestFixture.h
Normal file
@ -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
|
||||
Loading…
x
Reference in New Issue
Block a user