blob: 69312fd2f2b8dc5da2129434c0a80a3ddb9a1a4e [file]
//===- EntityLinkerTest.cpp -----------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "clang/ScalableStaticAnalysisFramework/Core/EntityLinker/EntityLinker.h"
#include "TestFixture.h"
#include "clang/ScalableStaticAnalysisFramework/Core/EntityLinker/EntitySummaryEncoding.h"
#include "clang/ScalableStaticAnalysisFramework/Core/EntityLinker/LUSummaryEncoding.h"
#include "clang/ScalableStaticAnalysisFramework/Core/EntityLinker/TUSummaryEncoding.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/BuildNamespace.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityIdTable.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityLinkage.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/SummaryName.h"
#include "llvm/Testing/Support/Error.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <memory>
using namespace clang::ssaf;
using namespace llvm;
using ::testing::HasSubstr;
using ::testing::Not;
using ::testing::PrintToString;
namespace {
class MockEntitySummaryEncoding : public EntitySummaryEncoding {
public:
MockEntitySummaryEncoding() : Id(++Index) {}
size_t getId() const { return Id; }
llvm::Error
patch(const std::map<EntityId, EntityId> &EntityResolutionTable) override {
PatchedIds = EntityResolutionTable;
return llvm::Error::success();
}
const std::map<EntityId, EntityId> &getPatchedIds() const {
return PatchedIds;
}
static size_t Index;
private:
size_t Id;
std::map<EntityId, EntityId> PatchedIds;
};
size_t MockEntitySummaryEncoding::Index = 0;
class EntityLinkerTest : public TestFixture {
protected:
static constexpr EntityLinkage NoneLinkage =
EntityLinkage(EntityLinkageType::None);
static constexpr EntityLinkage InternalLinkage =
EntityLinkage(EntityLinkageType::Internal);
static constexpr EntityLinkage ExternalLinkage =
EntityLinkage(EntityLinkageType::External);
void SetUp() override {
// This ensures that the MockEntitySummary id assignment does not
// accidentally depend on test execution order.
MockEntitySummaryEncoding::Index = 0;
}
std::unique_ptr<TUSummaryEncoding>
createTUSummaryEncoding(BuildNamespaceKind Kind, llvm::StringRef Name) {
return std::make_unique<TUSummaryEncoding>(BuildNamespace(Kind, Name));
}
size_t addSummaryData(TUSummaryEncoding &TU, EntityId EId,
llvm::StringRef SummaryNameStr) {
SummaryName SN(SummaryNameStr.str());
auto Summary = std::make_unique<MockEntitySummaryEncoding>();
const size_t ESId = Summary->getId();
getData(TU)[SN][EId] = std::move(Summary);
return ESId;
}
EntityId addEntity(TUSummaryEncoding &TU, llvm::StringRef USR,
EntityLinkage Linkage) {
EntityName Name(USR, "", NestedBuildNamespace());
EntityId Id = getIdTable(TU).getId(Name);
getLinkageTable(TU).insert({Id, Linkage});
return Id;
}
};
// ============================================================================
// Entity ID Table Matchers
// ============================================================================
MATCHER_P(ContainsEntity, EntityName,
std::string(negation ? "does not contain" : "contains") +
" entity '" + PrintToString(EntityName) + "'") {
return arg.contains(EntityName);
}
MATCHER_P(IdTableHasSize, ExpectedCount,
std::string("has ") + PrintToString(ExpectedCount) + " entities") {
if (arg.count() != ExpectedCount) {
*result_listener << "has " << arg.count() << " entities";
return false;
}
return true;
}
// ============================================================================
// Linkage Table Matchers
// ============================================================================
MATCHER_P2(EntityHasLinkage, EId, ExpectedLinkage,
std::string("entity has ") + PrintToString(ExpectedLinkage) +
" linkage") {
auto It = arg.find(EId);
if (It == arg.end()) {
*result_listener << "entity " << PrintToString(EId)
<< " not found in linkage table";
return false;
}
const EntityLinkage &ActualLinkage = It->second;
if (ActualLinkage != ExpectedLinkage) {
*result_listener << "entity " << PrintToString(EId) << " has linkage "
<< PrintToString(ActualLinkage);
return false;
}
return true;
}
MATCHER_P(LinkageTableHasSize, ExpectedSize,
std::string("linkage table has size ") +
PrintToString(ExpectedSize)) {
if (arg.size() != ExpectedSize) {
*result_listener << "has size " << arg.size();
return false;
}
return true;
}
// ============================================================================
// Summary Data Matchers
// ============================================================================
MATCHER_P3(HasSummaryData, EId, ExpectedMockId, ExpectedResolutionMapping,
std::string("has summary data for entity with expected mock ID ") +
PrintToString(ExpectedMockId)) {
auto It = arg.find(EId);
if (It == arg.end()) {
*result_listener << "entity " << PrintToString(EId)
<< " not found in summary data";
return false;
}
auto *Mock = static_cast<const MockEntitySummaryEncoding *>(It->second.get());
if (Mock->getId() != ExpectedMockId) {
*result_listener << "entity " << PrintToString(EId) << " has mock ID "
<< Mock->getId() << " (expected " << ExpectedMockId << ")";
return false;
}
if (Mock->getPatchedIds() != ExpectedResolutionMapping) {
*result_listener << "entity " << PrintToString(EId)
<< " has different resolution mapping";
return false;
}
return true;
}
MATCHER_P(SummaryDataHasSize, ExpectedSize,
std::string("summary data has size ") + PrintToString(ExpectedSize)) {
if (arg.size() != ExpectedSize) {
*result_listener << "has size " << arg.size();
return false;
}
return true;
}
// ============================================================================
// ENTITY LINKER TESTS
// ============================================================================
TEST_F(EntityLinkerTest, CreatesEmptyLinker) {
NestedBuildNamespace LUNamespace(
{BuildNamespace(BuildNamespaceKind::LinkUnit, "LU")});
EntityLinker Linker(LUNamespace);
const auto Output = std::move(Linker).takeOutput();
EXPECT_EQ(getIdTable(Output).count(), 0u);
EXPECT_EQ(getLinkageTable(Output).size(), 0u);
EXPECT_EQ(getData(Output).size(), 0u);
}
TEST_F(EntityLinkerTest, LinksEmptyTranslationUnit) {
NestedBuildNamespace LUNamespace(
{BuildNamespace(BuildNamespaceKind::LinkUnit, "LU")});
EntityLinker Linker(LUNamespace);
auto TUEmpty =
createTUSummaryEncoding(BuildNamespaceKind::CompilationUnit, "TUEmpty");
EXPECT_THAT_ERROR(Linker.link(std::move(TUEmpty)), llvm::Succeeded());
const auto Output = std::move(Linker).takeOutput();
EXPECT_EQ(getIdTable(Output).count(), 0u);
EXPECT_EQ(getLinkageTable(Output).size(), 0u);
EXPECT_EQ(getData(Output).size(), 0u);
}
TEST_F(EntityLinkerTest, LinksOneTranslationUnit) {
NestedBuildNamespace LUNamespace(
{BuildNamespace(BuildNamespaceKind::LinkUnit, "LU")});
EntityLinker Linker(LUNamespace);
auto TU = createTUSummaryEncoding(BuildNamespaceKind::CompilationUnit, "TU");
const auto TU_A_Id = addEntity(*TU, "A", NoneLinkage);
const auto TU_A_S1_Data = addSummaryData(*TU, TU_A_Id, "S1");
const auto TU_A_S2_Data = addSummaryData(*TU, TU_A_Id, "S2");
const auto TU_B_Id = addEntity(*TU, "B", InternalLinkage);
const auto TU_B_S1_Data = addSummaryData(*TU, TU_B_Id, "S1");
const auto TU_B_S2_Data = addSummaryData(*TU, TU_B_Id, "S2");
const auto TU_C_Id = addEntity(*TU, "C", ExternalLinkage);
const auto TU_C_S1_Data = addSummaryData(*TU, TU_C_Id, "S1");
const auto TU_D_Id = addEntity(*TU, "D", ExternalLinkage);
const auto TU_D_S2_Data = addSummaryData(*TU, TU_D_Id, "S2");
const BuildNamespace TUNamespace = getTUNamespace(*TU);
ASSERT_THAT_ERROR(Linker.link(std::move(TU)), llvm::Succeeded());
const auto Output = std::move(Linker).takeOutput();
const auto &IdTable = getIdTable(Output);
const auto &Entities = getEntities(IdTable);
const auto &LinkageTable = getLinkageTable(Output);
const auto &Data = getData(Output);
NestedBuildNamespace LocalNamespace =
NestedBuildNamespace(TUNamespace).makeQualified(LUNamespace);
EntityName LU_A_Name("A", "", LocalNamespace);
EntityName LU_B_Name("B", "", LocalNamespace);
EntityName LU_C_Name("C", "", LUNamespace);
EntityName LU_D_Name("D", "", LUNamespace);
// EntityIdTable Tests.
{
ASSERT_THAT(IdTable, IdTableHasSize(4u));
ASSERT_THAT(IdTable, ContainsEntity(LU_A_Name));
ASSERT_THAT(IdTable, ContainsEntity(LU_B_Name));
ASSERT_THAT(IdTable, ContainsEntity(LU_C_Name));
ASSERT_THAT(IdTable, ContainsEntity(LU_D_Name));
}
// This is safe since we confirmed that these entities are present in the
// block above.
const auto LU_A_Id = Entities.at(LU_A_Name);
const auto LU_B_Id = Entities.at(LU_B_Name);
const auto LU_C_Id = Entities.at(LU_C_Name);
const auto LU_D_Id = Entities.at(LU_D_Name);
// LinkageTable Tests.
{
ASSERT_THAT(LinkageTable, LinkageTableHasSize(4u));
ASSERT_THAT(LinkageTable, EntityHasLinkage(LU_A_Id, NoneLinkage));
ASSERT_THAT(LinkageTable, EntityHasLinkage(LU_B_Id, InternalLinkage));
ASSERT_THAT(LinkageTable, EntityHasLinkage(LU_C_Id, ExternalLinkage));
ASSERT_THAT(LinkageTable, EntityHasLinkage(LU_D_Id, ExternalLinkage));
}
std::map<EntityId, EntityId> Resolution = {{TU_A_Id, LU_A_Id},
{TU_B_Id, LU_B_Id},
{TU_C_Id, LU_C_Id},
{TU_D_Id, LU_D_Id}};
// Data Tests.
{
ASSERT_EQ(Data.size(), 2u);
// S1 Data Tests.
{
SummaryName S1("S1");
ASSERT_NE(Data.find(S1), Data.end());
const auto &S1Data = Data.at(S1);
ASSERT_THAT(S1Data, SummaryDataHasSize(3u));
ASSERT_THAT(S1Data, HasSummaryData(LU_A_Id, TU_A_S1_Data, Resolution));
ASSERT_THAT(S1Data, HasSummaryData(LU_B_Id, TU_B_S1_Data, Resolution));
ASSERT_THAT(S1Data, HasSummaryData(LU_C_Id, TU_C_S1_Data, Resolution));
}
// S2 Data Tests.
{
SummaryName S2("S2");
ASSERT_NE(Data.find(S2), Data.end());
const auto &S2Data = Data.at(S2);
ASSERT_THAT(S2Data, SummaryDataHasSize(3u));
ASSERT_THAT(S2Data, HasSummaryData(LU_A_Id, TU_A_S2_Data, Resolution));
ASSERT_THAT(S2Data, HasSummaryData(LU_B_Id, TU_B_S2_Data, Resolution));
ASSERT_THAT(S2Data, HasSummaryData(LU_D_Id, TU_D_S2_Data, Resolution));
}
}
}
TEST_F(EntityLinkerTest, LinksTwoTranslationUnits) {
NestedBuildNamespace LUNamespace(
{BuildNamespace(BuildNamespaceKind::LinkUnit, "LU")});
EntityLinker Linker(LUNamespace);
auto TU1 =
createTUSummaryEncoding(BuildNamespaceKind::CompilationUnit, "TU1");
// None linkage entities in TU1
const auto TU1_X_Id = addEntity(*TU1, "X", NoneLinkage);
const auto TU1_X_S1_Data = addSummaryData(*TU1, TU1_X_Id, "S1");
const auto TU1_Y_Id = addEntity(*TU1, "Y", NoneLinkage);
const auto TU1_Y_S2_Data = addSummaryData(*TU1, TU1_Y_Id, "S2");
const auto TU1_Z_Id = addEntity(*TU1, "Z", NoneLinkage);
const auto TU1_Z_S1_Data = addSummaryData(*TU1, TU1_Z_Id, "S1");
const auto TU1_Z_S2_Data = addSummaryData(*TU1, TU1_Z_Id, "S2");
// Internal linkage entities in TU1
const auto TU1_A_Id = addEntity(*TU1, "A", InternalLinkage);
const auto TU1_A_S1_Data = addSummaryData(*TU1, TU1_A_Id, "S1");
const auto TU1_B_Id = addEntity(*TU1, "B", InternalLinkage);
const auto TU1_B_S2_Data = addSummaryData(*TU1, TU1_B_Id, "S2");
const auto TU1_C_Id = addEntity(*TU1, "C", InternalLinkage);
const auto TU1_C_S1_Data = addSummaryData(*TU1, TU1_C_Id, "S1");
const auto TU1_C_S2_Data = addSummaryData(*TU1, TU1_C_Id, "S2");
// External linkage entities in TU1
const auto TU1_P_Id = addEntity(*TU1, "P", ExternalLinkage);
const auto TU1_P_S1_Data = addSummaryData(*TU1, TU1_P_Id, "S1");
const auto TU1_Q_Id = addEntity(*TU1, "Q", ExternalLinkage);
const auto TU1_Q_S2_Data = addSummaryData(*TU1, TU1_Q_Id, "S2");
const auto TU1_R_Id = addEntity(*TU1, "R", ExternalLinkage);
const auto TU1_R_S1_Data = addSummaryData(*TU1, TU1_R_Id, "S1");
const auto TU1_R_S2_Data = addSummaryData(*TU1, TU1_R_Id, "S2");
const BuildNamespace TU1Namespace = getTUNamespace(*TU1);
ASSERT_THAT_ERROR(Linker.link(std::move(TU1)), llvm::Succeeded());
auto TU2 =
createTUSummaryEncoding(BuildNamespaceKind::CompilationUnit, "TU2");
// None linkage entities in TU2 - includes duplicates and uniques
const auto TU2_X_Id = addEntity(*TU2, "X", NoneLinkage);
const auto TU2_X_S2_Data = addSummaryData(*TU2, TU2_X_Id, "S2");
const auto TU2_Y_Id = addEntity(*TU2, "Y", NoneLinkage);
const auto TU2_Y_S1_Data = addSummaryData(*TU2, TU2_Y_Id, "S1");
const auto TU2_W_Id = addEntity(*TU2, "W", NoneLinkage);
const auto TU2_W_S1_Data = addSummaryData(*TU2, TU2_W_Id, "S1");
const auto TU2_W_S2_Data = addSummaryData(*TU2, TU2_W_Id, "S2");
// Internal linkage entities in TU2 - includes duplicates and unique
const auto TU2_A_Id = addEntity(*TU2, "A", InternalLinkage);
const auto TU2_A_S2_Data = addSummaryData(*TU2, TU2_A_Id, "S2");
const auto TU2_B_Id = addEntity(*TU2, "B", InternalLinkage);
const auto TU2_B_S1_Data = addSummaryData(*TU2, TU2_B_Id, "S1");
const auto TU2_D_Id = addEntity(*TU2, "D", InternalLinkage);
const auto TU2_D_S1_Data = addSummaryData(*TU2, TU2_D_Id, "S1");
const auto TU2_D_S2_Data = addSummaryData(*TU2, TU2_D_Id, "S2");
// External linkage entities in TU2 - includes duplicates (will be dropped)
// and uniques
const auto TU2_P_Id = addEntity(*TU2, "P", ExternalLinkage);
const auto TU2_P_S2_Data = addSummaryData(*TU2, TU2_P_Id, "S2");
const auto TU2_Q_Id = addEntity(*TU2, "Q", ExternalLinkage);
const auto TU2_Q_S1_Data = addSummaryData(*TU2, TU2_Q_Id, "S1");
const auto TU2_R_Id = addEntity(*TU2, "R", ExternalLinkage);
const auto TU2_R_S1_Data = addSummaryData(*TU2, TU2_R_Id, "S1");
const auto TU2_R_S2_Data = addSummaryData(*TU2, TU2_R_Id, "S2");
const auto TU2_S_Id = addEntity(*TU2, "S", ExternalLinkage);
const auto TU2_S_S1_Data = addSummaryData(*TU2, TU2_S_Id, "S1");
const auto TU2_S_S2_Data = addSummaryData(*TU2, TU2_S_Id, "S2");
const BuildNamespace TU2Namespace = getTUNamespace(*TU2);
ASSERT_THAT_ERROR(Linker.link(std::move(TU2)), llvm::Succeeded());
const auto Output = std::move(Linker).takeOutput();
const auto &IdTable = getIdTable(Output);
const auto &Entities = getEntities(IdTable);
const auto &LinkageTable = getLinkageTable(Output);
const auto &Data = getData(Output);
NestedBuildNamespace TU1LocalNamespace =
NestedBuildNamespace(TU1Namespace).makeQualified(LUNamespace);
NestedBuildNamespace TU2LocalNamespace =
NestedBuildNamespace(TU2Namespace).makeQualified(LUNamespace);
// None linkage entities use local namespace (TU scoped)
EntityName LU_TU1_X_Name("X", "", TU1LocalNamespace);
EntityName LU_TU1_Y_Name("Y", "", TU1LocalNamespace);
EntityName LU_TU1_Z_Name("Z", "", TU1LocalNamespace);
EntityName LU_TU2_X_Name("X", "", TU2LocalNamespace);
EntityName LU_TU2_Y_Name("Y", "", TU2LocalNamespace);
EntityName LU_TU2_W_Name("W", "", TU2LocalNamespace);
// Internal linkage entities use local namespace (TU scoped)
EntityName LU_TU1_A_Name("A", "", TU1LocalNamespace);
EntityName LU_TU1_B_Name("B", "", TU1LocalNamespace);
EntityName LU_TU1_C_Name("C", "", TU1LocalNamespace);
EntityName LU_TU2_A_Name("A", "", TU2LocalNamespace);
EntityName LU_TU2_B_Name("B", "", TU2LocalNamespace);
EntityName LU_TU2_D_Name("D", "", TU2LocalNamespace);
// External linkage entities use LU namespace (shared across TUs)
EntityName LU_P_Name("P", "", LUNamespace);
EntityName LU_Q_Name("Q", "", LUNamespace);
EntityName LU_R_Name("R", "", LUNamespace);
EntityName LU_S_Name("S", "", LUNamespace);
// EntityIdTable Tests.
{
// Should have 6 None + 6 Internal + 4 External = 16 entities total
ASSERT_THAT(IdTable, IdTableHasSize(16u));
ASSERT_THAT(IdTable, ContainsEntity(LU_TU1_X_Name));
ASSERT_THAT(IdTable, ContainsEntity(LU_TU1_Y_Name));
ASSERT_THAT(IdTable, ContainsEntity(LU_TU1_Z_Name));
ASSERT_THAT(IdTable, ContainsEntity(LU_TU2_X_Name));
ASSERT_THAT(IdTable, ContainsEntity(LU_TU2_Y_Name));
ASSERT_THAT(IdTable, ContainsEntity(LU_TU2_W_Name));
ASSERT_THAT(IdTable, ContainsEntity(LU_TU1_A_Name));
ASSERT_THAT(IdTable, ContainsEntity(LU_TU1_B_Name));
ASSERT_THAT(IdTable, ContainsEntity(LU_TU1_C_Name));
ASSERT_THAT(IdTable, ContainsEntity(LU_TU2_A_Name));
ASSERT_THAT(IdTable, ContainsEntity(LU_TU2_B_Name));
ASSERT_THAT(IdTable, ContainsEntity(LU_TU2_D_Name));
ASSERT_THAT(IdTable, ContainsEntity(LU_P_Name));
ASSERT_THAT(IdTable, ContainsEntity(LU_Q_Name));
ASSERT_THAT(IdTable, ContainsEntity(LU_R_Name));
ASSERT_THAT(IdTable, ContainsEntity(LU_S_Name));
}
// This is safe since we confirmed that these entities are present in the
// block above.
const auto LU_TU1_X_Id = Entities.at(LU_TU1_X_Name);
const auto LU_TU1_Y_Id = Entities.at(LU_TU1_Y_Name);
const auto LU_TU1_Z_Id = Entities.at(LU_TU1_Z_Name);
const auto LU_TU2_X_Id = Entities.at(LU_TU2_X_Name);
const auto LU_TU2_Y_Id = Entities.at(LU_TU2_Y_Name);
const auto LU_TU2_W_Id = Entities.at(LU_TU2_W_Name);
const auto LU_TU1_A_Id = Entities.at(LU_TU1_A_Name);
const auto LU_TU1_B_Id = Entities.at(LU_TU1_B_Name);
const auto LU_TU1_C_Id = Entities.at(LU_TU1_C_Name);
const auto LU_TU2_A_Id = Entities.at(LU_TU2_A_Name);
const auto LU_TU2_B_Id = Entities.at(LU_TU2_B_Name);
const auto LU_TU2_D_Id = Entities.at(LU_TU2_D_Name);
const auto LU_P_Id = Entities.at(LU_P_Name);
const auto LU_Q_Id = Entities.at(LU_Q_Name);
const auto LU_R_Id = Entities.at(LU_R_Name);
const auto LU_S_Id = Entities.at(LU_S_Name);
// LinkageTable Tests.
{
ASSERT_THAT(LinkageTable, LinkageTableHasSize(16u));
ASSERT_THAT(LinkageTable, EntityHasLinkage(LU_TU1_X_Id, NoneLinkage));
ASSERT_THAT(LinkageTable, EntityHasLinkage(LU_TU1_Y_Id, NoneLinkage));
ASSERT_THAT(LinkageTable, EntityHasLinkage(LU_TU1_Z_Id, NoneLinkage));
ASSERT_THAT(LinkageTable, EntityHasLinkage(LU_TU2_X_Id, NoneLinkage));
ASSERT_THAT(LinkageTable, EntityHasLinkage(LU_TU2_Y_Id, NoneLinkage));
ASSERT_THAT(LinkageTable, EntityHasLinkage(LU_TU2_W_Id, NoneLinkage));
ASSERT_THAT(LinkageTable, EntityHasLinkage(LU_TU1_A_Id, InternalLinkage));
ASSERT_THAT(LinkageTable, EntityHasLinkage(LU_TU1_B_Id, InternalLinkage));
ASSERT_THAT(LinkageTable, EntityHasLinkage(LU_TU1_C_Id, InternalLinkage));
ASSERT_THAT(LinkageTable, EntityHasLinkage(LU_TU2_A_Id, InternalLinkage));
ASSERT_THAT(LinkageTable, EntityHasLinkage(LU_TU2_B_Id, InternalLinkage));
ASSERT_THAT(LinkageTable, EntityHasLinkage(LU_TU2_D_Id, InternalLinkage));
ASSERT_THAT(LinkageTable, EntityHasLinkage(LU_P_Id, ExternalLinkage));
ASSERT_THAT(LinkageTable, EntityHasLinkage(LU_Q_Id, ExternalLinkage));
ASSERT_THAT(LinkageTable, EntityHasLinkage(LU_R_Id, ExternalLinkage));
ASSERT_THAT(LinkageTable, EntityHasLinkage(LU_S_Id, ExternalLinkage));
}
// Data Tests.
{
ASSERT_EQ(Data.size(), 2u);
// Build entity resolution mappings for each TU.
std::map<EntityId, EntityId> TU1Resolution = {
{TU1_X_Id, LU_TU1_X_Id}, {TU1_Y_Id, LU_TU1_Y_Id},
{TU1_Z_Id, LU_TU1_Z_Id}, {TU1_A_Id, LU_TU1_A_Id},
{TU1_B_Id, LU_TU1_B_Id}, {TU1_C_Id, LU_TU1_C_Id},
{TU1_P_Id, LU_P_Id}, {TU1_Q_Id, LU_Q_Id},
{TU1_R_Id, LU_R_Id}};
std::map<EntityId, EntityId> TU2Resolution = {
{TU2_X_Id, LU_TU2_X_Id}, {TU2_Y_Id, LU_TU2_Y_Id},
{TU2_W_Id, LU_TU2_W_Id}, {TU2_A_Id, LU_TU2_A_Id},
{TU2_B_Id, LU_TU2_B_Id}, {TU2_D_Id, LU_TU2_D_Id},
{TU2_P_Id, LU_P_Id}, {TU2_Q_Id, LU_Q_Id},
{TU2_R_Id, LU_R_Id}, {TU2_S_Id, LU_S_Id}};
// S1 Data Tests.
{
SummaryName S1("S1");
ASSERT_NE(Data.find(S1), Data.end());
const auto &S1Data = Data.at(S1);
// S1 should contain: TU1(X,Z,A,C,P,R) + TU2(Y,W,B,D,Q,S) = 12 entities.
ASSERT_THAT(S1Data, SummaryDataHasSize(12u));
// TU1 entities in S1.
ASSERT_THAT(S1Data,
HasSummaryData(LU_TU1_X_Id, TU1_X_S1_Data, TU1Resolution));
ASSERT_THAT(S1Data,
HasSummaryData(LU_TU1_Z_Id, TU1_Z_S1_Data, TU1Resolution));
ASSERT_THAT(S1Data,
HasSummaryData(LU_TU1_A_Id, TU1_A_S1_Data, TU1Resolution));
ASSERT_THAT(S1Data,
HasSummaryData(LU_TU1_C_Id, TU1_C_S1_Data, TU1Resolution));
ASSERT_THAT(S1Data,
HasSummaryData(LU_P_Id, TU1_P_S1_Data, TU1Resolution));
ASSERT_THAT(S1Data,
HasSummaryData(LU_R_Id, TU1_R_S1_Data, TU1Resolution));
// TU2 entities in S1.
ASSERT_THAT(S1Data,
HasSummaryData(LU_TU2_Y_Id, TU2_Y_S1_Data, TU2Resolution));
ASSERT_THAT(S1Data,
HasSummaryData(LU_TU2_W_Id, TU2_W_S1_Data, TU2Resolution));
ASSERT_THAT(S1Data,
HasSummaryData(LU_TU2_B_Id, TU2_B_S1_Data, TU2Resolution));
ASSERT_THAT(S1Data,
HasSummaryData(LU_TU2_D_Id, TU2_D_S1_Data, TU2Resolution));
ASSERT_THAT(S1Data,
HasSummaryData(LU_Q_Id, TU2_Q_S1_Data, TU2Resolution));
ASSERT_THAT(S1Data,
Not(HasSummaryData(LU_R_Id, TU2_R_S1_Data, TU2Resolution)));
ASSERT_THAT(S1Data,
HasSummaryData(LU_S_Id, TU2_S_S1_Data, TU2Resolution));
}
// S2 Data Tests.
{
SummaryName S2("S2");
ASSERT_NE(Data.find(S2), Data.end());
const auto &S2Data = Data.at(S2);
// S2 should contain: TU1(Y,Z,B,C,Q,R) + TU2(X,W,A,D,P,S) = 12 entities.
ASSERT_THAT(S2Data, SummaryDataHasSize(12u));
// TU1 entities in S2.
ASSERT_THAT(S2Data,
HasSummaryData(LU_TU1_Y_Id, TU1_Y_S2_Data, TU1Resolution));
ASSERT_THAT(S2Data,
HasSummaryData(LU_TU1_Z_Id, TU1_Z_S2_Data, TU1Resolution));
ASSERT_THAT(S2Data,
HasSummaryData(LU_TU1_B_Id, TU1_B_S2_Data, TU1Resolution));
ASSERT_THAT(S2Data,
HasSummaryData(LU_TU1_C_Id, TU1_C_S2_Data, TU1Resolution));
ASSERT_THAT(S2Data,
HasSummaryData(LU_Q_Id, TU1_Q_S2_Data, TU1Resolution));
ASSERT_THAT(S2Data,
HasSummaryData(LU_R_Id, TU1_R_S2_Data, TU1Resolution));
// TU2 entities in S2.
ASSERT_THAT(S2Data,
HasSummaryData(LU_TU2_X_Id, TU2_X_S2_Data, TU2Resolution));
ASSERT_THAT(S2Data,
HasSummaryData(LU_TU2_W_Id, TU2_W_S2_Data, TU2Resolution));
ASSERT_THAT(S2Data,
HasSummaryData(LU_TU2_A_Id, TU2_A_S2_Data, TU2Resolution));
ASSERT_THAT(S2Data,
HasSummaryData(LU_TU2_D_Id, TU2_D_S2_Data, TU2Resolution));
ASSERT_THAT(S2Data,
HasSummaryData(LU_P_Id, TU2_P_S2_Data, TU2Resolution));
ASSERT_THAT(S2Data,
Not(HasSummaryData(LU_R_Id, TU2_R_S2_Data, TU2Resolution)));
ASSERT_THAT(S2Data,
HasSummaryData(LU_S_Id, TU2_S_S2_Data, TU2Resolution));
}
}
}
TEST_F(EntityLinkerTest, RejectsDuplicateTUSummary) {
NestedBuildNamespace LUNamespace(
{BuildNamespace(BuildNamespaceKind::LinkUnit, "LU")});
EntityLinker Linker(LUNamespace);
auto TU1 = createTUSummaryEncoding(BuildNamespaceKind::CompilationUnit, "TU");
ASSERT_THAT_ERROR(Linker.link(std::move(TU1)), llvm::Succeeded());
auto TU2 = createTUSummaryEncoding(BuildNamespaceKind::CompilationUnit, "TU");
ASSERT_THAT_ERROR(Linker.link(std::move(TU2)),
llvm::FailedWithMessage(
HasSubstr("failed to link TU summary: duplicate "
"BuildNamespace(CompilationUnit, TU)")));
}
// Reproduces a crash when linking internal-linkage entities
// (e.g. "static inline" functions) that share the same USR across TUs.
TEST_F(EntityLinkerTest, InternalLinkageWithEmptyNamespaceAcrossTUs) {
constexpr auto LinkUnit = BuildNamespaceKind::LinkUnit;
constexpr auto CompilationUnit = BuildNamespaceKind::CompilationUnit;
EntityLinker Linker(NestedBuildNamespace{BuildNamespace(LinkUnit, "LU")});
auto TU1 = createTUSummaryEncoding(CompilationUnit, "TU1");
addEntity(*TU1, "some_static_inline", InternalLinkage);
ASSERT_THAT_ERROR(Linker.link(std::move(TU1)), llvm::Succeeded());
auto TU2 = createTUSummaryEncoding(CompilationUnit, "TU2");
addEntity(*TU2, "some_static_inline", InternalLinkage);
ASSERT_THAT_ERROR(Linker.link(std::move(TU2)), llvm::Succeeded());
// Check that the two internal symbols are not merged.
const auto Output = std::move(Linker).takeOutput();
const auto &IdTable = getIdTable(Output);
NestedBuildNamespace TU1NS{{{CompilationUnit, "TU1"}, {LinkUnit, "LU"}}};
NestedBuildNamespace TU2NS{{{CompilationUnit, "TU2"}, {LinkUnit, "LU"}}};
EntityName ExpectedTU1Name("some_static_inline", "", TU1NS);
EntityName ExpectedTU2Name("some_static_inline", "", TU2NS);
ASSERT_EQ(IdTable.count(), 2u);
EXPECT_THAT(IdTable, ContainsEntity(ExpectedTU1Name));
EXPECT_THAT(IdTable, ContainsEntity(ExpectedTU2Name));
}
} // namespace