[clang] [clang][ssaf] Add Entity Linker and associated data structures (PR #181765)
Aviral Goel via cfe-commits
cfe-commits at lists.llvm.org
Wed Feb 18 14:00:34 PST 2026
https://github.com/aviralg updated https://github.com/llvm/llvm-project/pull/181765
>From a513fdb439c2245b22440d0658db936f79e1ca81 Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Mon, 16 Feb 2026 17:29:25 -0800
Subject: [PATCH 1/6] Add Entity Linker algorithm and associated data
structures
---
.../Scalable/EntityLinker/EntityLinker.h | 65 ++++++
.../EntityLinker/EntitySummaryEncoding.h | 41 ++++
.../Scalable/EntityLinker/LUSummary.h | 56 +++++
.../Scalable/EntityLinker/LUSummaryEncoding.h | 57 ++++++
.../Scalable/EntityLinker/TUSummaryEncoding.h | 59 ++++++
.../clang/Analysis/Scalable/Model/EntityId.h | 7 +-
.../Analysis/Scalable/Model/EntityIdTable.h | 5 +-
.../Analysis/Scalable/Model/EntityLinkage.h | 4 +-
.../Analysis/Scalable/Model/EntityName.h | 1 +
.../Analysis/Scalable/Support/ErrorBuilder.h | 89 ++++++++
clang/lib/Analysis/Scalable/CMakeLists.txt | 2 +
.../Scalable/EntityLinker/EntityLinker.cpp | 192 ++++++++++++++++++
.../Scalable/Serialization/JSONFormat.cpp | 97 +--------
.../Scalable/Support/ErrorBuilder.cpp | 59 ++++++
14 files changed, 631 insertions(+), 103 deletions(-)
create mode 100644 clang/include/clang/Analysis/Scalable/EntityLinker/EntityLinker.h
create mode 100644 clang/include/clang/Analysis/Scalable/EntityLinker/EntitySummaryEncoding.h
create mode 100644 clang/include/clang/Analysis/Scalable/EntityLinker/LUSummary.h
create mode 100644 clang/include/clang/Analysis/Scalable/EntityLinker/LUSummaryEncoding.h
create mode 100644 clang/include/clang/Analysis/Scalable/EntityLinker/TUSummaryEncoding.h
create mode 100644 clang/include/clang/Analysis/Scalable/Support/ErrorBuilder.h
create mode 100644 clang/lib/Analysis/Scalable/EntityLinker/EntityLinker.cpp
create mode 100644 clang/lib/Analysis/Scalable/Support/ErrorBuilder.cpp
diff --git a/clang/include/clang/Analysis/Scalable/EntityLinker/EntityLinker.h b/clang/include/clang/Analysis/Scalable/EntityLinker/EntityLinker.h
new file mode 100644
index 0000000000000..b628af9d25843
--- /dev/null
+++ b/clang/include/clang/Analysis/Scalable/EntityLinker/EntityLinker.h
@@ -0,0 +1,65 @@
+//===- EntityLinker.h - Class for linking entities --------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the EntityLinker class that combines multiple TU summaries
+// into a unified LU summary by deduplicating entities and patching summaries.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_ENTITYLINKER_ENTITYLINKER_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_ENTITYLINKER_ENTITYLINKER_H
+
+#include "clang/Analysis/Scalable/EntityLinker/LUSummaryEncoding.h"
+#include "clang/Analysis/Scalable/Model/BuildNamespace.h"
+#include "clang/Analysis/Scalable/Model/EntityId.h"
+#include "clang/Analysis/Scalable/Model/EntityLinkage.h"
+#include "clang/Analysis/Scalable/Model/EntityName.h"
+#include "clang/Analysis/Scalable/Model/SummaryName.h"
+#include "llvm/Support/Error.h"
+#include <map>
+#include <memory>
+#include <vector>
+
+namespace clang::ssaf {
+
+class EntitySummaryEncoding;
+class TUSummaryEncoding;
+
+class EntityLinker {
+ LUSummaryEncoding Output;
+
+public:
+ EntityLinker(NestedBuildNamespace LUNamespace)
+ : Output(std::move(LUNamespace)) {}
+
+ llvm::Error link(std::unique_ptr<TUSummaryEncoding> Summary);
+
+ const LUSummaryEncoding &getOutput() const { return Output; }
+
+private:
+ llvm::Expected<EntityId> resolve(const EntityName &OldName,
+ const EntityId OldId,
+ const EntityLinkage &EL);
+
+ llvm::Error
+ merge(std::map<SummaryName,
+ std::map<EntityId, std::unique_ptr<EntitySummaryEncoding>>>
+ &InputData,
+ std::map<SummaryName,
+ std::map<EntityId, std::unique_ptr<EntitySummaryEncoding>>>
+ &OutputData,
+ const EntityId OldId, const EntityId NewId, const EntityLinkage &EL,
+ std::vector<EntitySummaryEncoding *> &PatchTargets);
+
+ void patch(std::vector<EntitySummaryEncoding *> &PatchTargets,
+ const std::map<EntityId, EntityId> &EntityResolutionTable);
+};
+
+} // end namespace clang::ssaf
+
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_ENTITYLINKER_ENTITYLINKER_H
diff --git a/clang/include/clang/Analysis/Scalable/EntityLinker/EntitySummaryEncoding.h b/clang/include/clang/Analysis/Scalable/EntityLinker/EntitySummaryEncoding.h
new file mode 100644
index 0000000000000..a38dd0c895452
--- /dev/null
+++ b/clang/include/clang/Analysis/Scalable/EntityLinker/EntitySummaryEncoding.h
@@ -0,0 +1,41 @@
+//===- EntitySummaryEncoding.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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the EntitySummaryEncoding class, which represents
+// EntitySummary data in an encoded, format-specific form.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_ENTITYLINKER_ENTITYSUMMARYENCODING_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_ENTITYLINKER_ENTITYSUMMARYENCODING_H
+
+#include "clang/Analysis/Scalable/Model/EntityId.h"
+#include <map>
+
+namespace clang::ssaf {
+
+/// Represents EntitySummary data in its serialized, format-specific encoding.
+///
+/// This abstract base class allows the entity linker to manipulate serialized
+/// entity summary data without knowing the exact schema of the EntitySummary
+/// subclass. The primary operation is patching EntityId references when
+/// entities are merged during linking.
+class EntitySummaryEncoding {
+public:
+ virtual ~EntitySummaryEncoding() = default;
+
+ /// Updates EntityId references in the encoded data.
+ ///
+ /// \param EntityResolutionTable Mapping from old EntityIds to new EntityIds.
+ virtual void
+ patch(const std::map<EntityId, EntityId> &EntityResolutionTable) = 0;
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_ENTITYLINKER_ENTITYSUMMARYENCODING_H
diff --git a/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummary.h b/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummary.h
new file mode 100644
index 0000000000000..54d18d78d53bf
--- /dev/null
+++ b/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummary.h
@@ -0,0 +1,56 @@
+//===- LUSummary.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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the LUSummary class, which represents a link unit summary
+// containing merged and deduplicated entity summaries from multiple TUs.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_ENTITYLINKER_LUSUMMARY_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_ENTITYLINKER_LUSUMMARY_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 <map>
+#include <memory>
+
+namespace clang::ssaf {
+
+class EntitySummary;
+class SerializationFormat;
+class SummaryViewBuilder;
+
+/// Represents a link unit (LU) summary containing merged entity summaries.
+///
+/// LUSummary is the result of linking multiple translation unit summaries
+/// together. It contains deduplicated entities with their linkage information
+/// and the merged entity summaries.
+class LUSummary {
+ NestedBuildNamespace LUNamespace;
+
+ EntityIdTable IdTable;
+
+ std::map<EntityId, EntityLinkage> LinkageTable;
+
+ std::map<SummaryName, std::map<EntityId, std::unique_ptr<EntitySummary>>>
+ Data;
+
+public:
+ LUSummary(NestedBuildNamespace LUNamespace)
+ : LUNamespace(std::move(LUNamespace)) {}
+
+ friend class SerializationFormat;
+ friend class SummaryViewBuilder;
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_ENTITYLINKER_LUSUMMARY_H
diff --git a/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummaryEncoding.h b/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummaryEncoding.h
new file mode 100644
index 0000000000000..39185990a9ea6
--- /dev/null
+++ b/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummaryEncoding.h
@@ -0,0 +1,57 @@
+//===- LUSummaryEncoding.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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the LUSummaryEncoding class, which represents a link unit
+// summary in its serialized, format-specific encoding.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_ENTITYLINKER_LUSUMMARYENCODING_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_ENTITYLINKER_LUSUMMARYENCODING_H
+
+#include "clang/Analysis/Scalable/EntityLinker/EntitySummaryEncoding.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 <map>
+#include <memory>
+
+namespace clang::ssaf {
+
+class EntityLinker;
+class SerializationFormat;
+
+/// Represents a link unit summary in its serialized encoding.
+///
+/// LUSummaryEncoding holds the combined entity summary data from multiple
+/// translation units in a format-specific encoding. It is produced by the
+/// entity linker and contains deduplicated and patched entity summaries.
+class LUSummaryEncoding {
+ NestedBuildNamespace LUNamespace;
+
+ EntityIdTable IdTable;
+
+ std::map<EntityId, EntityLinkage> LinkageTable;
+
+ std::map<SummaryName,
+ std::map<EntityId, std::unique_ptr<EntitySummaryEncoding>>>
+ Data;
+
+public:
+ LUSummaryEncoding(NestedBuildNamespace LUNamespace)
+ : LUNamespace(std::move(LUNamespace)) {}
+
+ friend class EntityLinker;
+ friend class SerializationFormat;
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_ENTITYLINKER_LUSUMMARYENCODING_H
diff --git a/clang/include/clang/Analysis/Scalable/EntityLinker/TUSummaryEncoding.h b/clang/include/clang/Analysis/Scalable/EntityLinker/TUSummaryEncoding.h
new file mode 100644
index 0000000000000..1b42eb70d09d0
--- /dev/null
+++ b/clang/include/clang/Analysis/Scalable/EntityLinker/TUSummaryEncoding.h
@@ -0,0 +1,59 @@
+//===- TUSummaryEncoding.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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the TUSummaryEncoding class, which represents a
+// translation unit summary in its serialized, format-specific encoding.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_ENTITYLINKER_TUSUMMARYENCODING_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_ENTITYLINKER_TUSUMMARYENCODING_H
+
+#include "clang/Analysis/Scalable/EntityLinker/EntitySummaryEncoding.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 <map>
+#include <memory>
+
+namespace clang::ssaf {
+
+class EntityLinker;
+class SerializationFormat;
+
+/// Represents a translation unit summary in its serialized encoding.
+///
+/// TUSummaryEncoding holds entity summary data in a format-specific encoding
+/// that can be manipulated by the entity linker without deserializing the
+/// full EntitySummary objects. This enables efficient entity ID patching
+/// during the linking process.
+class TUSummaryEncoding {
+ /// Identifies the translation unit.
+ BuildNamespace TUNamespace;
+
+ EntityIdTable IdTable;
+
+ std::map<EntityId, EntityLinkage> LinkageTable;
+
+ std::map<SummaryName,
+ std::map<EntityId, std::unique_ptr<EntitySummaryEncoding>>>
+ Data;
+
+public:
+ TUSummaryEncoding(BuildNamespace TUNamespace)
+ : TUNamespace(std::move(TUNamespace)) {}
+
+ friend class EntityLinker;
+ friend class SerializationFormat;
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_ENTITYLINKER_TUSUMMARYENCODING_H
diff --git a/clang/include/clang/Analysis/Scalable/Model/EntityId.h b/clang/include/clang/Analysis/Scalable/Model/EntityId.h
index e348486386cb6..231525b445ca0 100644
--- a/clang/include/clang/Analysis/Scalable/Model/EntityId.h
+++ b/clang/include/clang/Analysis/Scalable/Model/EntityId.h
@@ -28,9 +28,6 @@ class EntityIdTable;
///
/// \see EntityIdTable
class EntityId {
- friend class EntityIdTable;
- friend class SerializationFormat;
-
size_t Index;
explicit EntityId(size_t Index) : Index(Index) {}
@@ -41,6 +38,10 @@ class EntityId {
bool operator==(const EntityId &Other) const { return Index == Other.Index; }
bool operator<(const EntityId &Other) const { return Index < Other.Index; }
bool operator!=(const EntityId &Other) const { return !(*this == Other); }
+
+ friend class EntityIdTable;
+ friend class EntityLinker;
+ friend class SerializationFormat;
};
} // namespace clang::ssaf
diff --git a/clang/include/clang/Analysis/Scalable/Model/EntityIdTable.h b/clang/include/clang/Analysis/Scalable/Model/EntityIdTable.h
index a1099c4e4d0f8..b12d3e0c0faec 100644
--- a/clang/include/clang/Analysis/Scalable/Model/EntityIdTable.h
+++ b/clang/include/clang/Analysis/Scalable/Model/EntityIdTable.h
@@ -21,8 +21,6 @@ namespace clang::ssaf {
/// The table maps each unique EntityName to exactly one EntityId.
/// Entities are never removed.
class EntityIdTable {
- friend class SerializationFormat;
-
std::map<EntityName, EntityId> Entities;
public:
@@ -45,6 +43,9 @@ class EntityIdTable {
/// Returns the number of unique entities in the table.
size_t count() const { return Entities.size(); }
+
+ friend class EntityLinker;
+ friend class SerializationFormat;
};
} // namespace clang::ssaf
diff --git a/clang/include/clang/Analysis/Scalable/Model/EntityLinkage.h b/clang/include/clang/Analysis/Scalable/Model/EntityLinkage.h
index ba5f7d3073a30..af775769dc1e4 100644
--- a/clang/include/clang/Analysis/Scalable/Model/EntityLinkage.h
+++ b/clang/include/clang/Analysis/Scalable/Model/EntityLinkage.h
@@ -17,8 +17,6 @@ namespace clang::ssaf {
/// or external linkage, which determines its visibility and accessibility
/// across translation units.
class EntityLinkage {
- friend class SerializationFormat;
-
public:
enum class LinkageType {
None, ///< local variables, function parameters
@@ -32,6 +30,8 @@ class EntityLinkage {
private:
LinkageType Linkage;
+
+ friend class SerializationFormat;
};
} // namespace clang::ssaf
diff --git a/clang/include/clang/Analysis/Scalable/Model/EntityName.h b/clang/include/clang/Analysis/Scalable/Model/EntityName.h
index 23890ab7bea43..6bf51844f2f5b 100644
--- a/clang/include/clang/Analysis/Scalable/Model/EntityName.h
+++ b/clang/include/clang/Analysis/Scalable/Model/EntityName.h
@@ -47,6 +47,7 @@ class EntityName {
/// \param Namespace The namespace steps to append to this entity's namespace.
EntityName makeQualified(NestedBuildNamespace Namespace) const;
+ friend class EntityLinker;
friend class LinkUnitResolution;
friend class SerializationFormat;
};
diff --git a/clang/include/clang/Analysis/Scalable/Support/ErrorBuilder.h b/clang/include/clang/Analysis/Scalable/Support/ErrorBuilder.h
new file mode 100644
index 0000000000000..4ccda48e2391a
--- /dev/null
+++ b/clang/include/clang/Analysis/Scalable/Support/ErrorBuilder.h
@@ -0,0 +1,89 @@
+//===- ErrorBuilder.h - Fluent API for contextual errors --------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the ErrorBuilder class, which provides a fluent API for
+// constructing contextual error messages with layered context information.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_SUPPORT_ERRORBUILDER_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_SUPPORT_ERRORBUILDER_H
+
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FormatVariadic.h"
+#include <string>
+#include <system_error>
+#include <vector>
+
+namespace clang::ssaf {
+
+/// Fluent API for constructing contextual errors.
+///
+/// ErrorBuilder allows building error messages with layered context
+/// information. Context is added from innermost to outermost, and the final
+/// error message presents the context in reverse order (outermost first).
+///
+/// Example usage:
+/// return ErrorBuilder::create(std::errc::invalid_argument,
+/// "invalid value {0}", value)
+/// .context("processing field '{0}'", fieldName)
+/// .context("reading configuration")
+/// .build();
+class ErrorBuilder {
+private:
+ std::error_code Code;
+ std::vector<std::string> ContextStack;
+
+ // Private constructor - only accessible via static factories.
+ explicit ErrorBuilder(std::error_code EC) : Code(EC) {}
+
+ // Helper: Format message and add to context stack.
+ template <typename... Args>
+ void addFormattedContext(const char *Fmt, Args &&...ArgVals) {
+ std::string Message =
+ llvm::formatv(Fmt, std::forward<Args>(ArgVals)...).str();
+ ContextStack.push_back(std::move(Message));
+ }
+
+public:
+ // Static factory: Create new error from error code and formatted message.
+ template <typename... Args>
+ static ErrorBuilder create(std::error_code EC, const char *Fmt,
+ Args &&...ArgVals) {
+ ErrorBuilder Builder(EC);
+ Builder.addFormattedContext(Fmt, std::forward<Args>(ArgVals)...);
+ return Builder;
+ }
+
+ // Convenience overload for std::errc.
+ template <typename... Args>
+ static ErrorBuilder create(std::errc EC, const char *Fmt, Args &&...ArgVals) {
+ return create(std::make_error_code(EC), Fmt,
+ std::forward<Args>(ArgVals)...);
+ }
+
+ // Static factory: Wrap existing error and optionally add context.
+ static ErrorBuilder wrap(llvm::Error E);
+
+ // Add context (plain string).
+ ErrorBuilder &context(const char *Msg);
+
+ // Add context (formatted string).
+ template <typename... Args>
+ ErrorBuilder &context(const char *Fmt, Args &&...ArgVals) {
+ addFormattedContext(Fmt, std::forward<Args>(ArgVals)...);
+ return *this;
+ }
+
+ // Build the final error.
+ llvm::Error build();
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_SUPPORT_ERRORBUILDER_H
diff --git a/clang/lib/Analysis/Scalable/CMakeLists.txt b/clang/lib/Analysis/Scalable/CMakeLists.txt
index 522fc9dcf078d..8ef1625cc704b 100644
--- a/clang/lib/Analysis/Scalable/CMakeLists.txt
+++ b/clang/lib/Analysis/Scalable/CMakeLists.txt
@@ -4,11 +4,13 @@ set(LLVM_LINK_COMPONENTS
add_clang_library(clangAnalysisScalable
ASTEntityMapping.cpp
+ EntityLinker/EntityLinker.cpp
Model/BuildNamespace.cpp
Model/EntityIdTable.cpp
Model/EntityName.cpp
Serialization/JSONFormat.cpp
Serialization/SerializationFormatRegistry.cpp
+ Support/ErrorBuilder.cpp
TUSummary/ExtractorRegistry.cpp
LINK_LIBS
diff --git a/clang/lib/Analysis/Scalable/EntityLinker/EntityLinker.cpp b/clang/lib/Analysis/Scalable/EntityLinker/EntityLinker.cpp
new file mode 100644
index 0000000000000..2c7344b19de55
--- /dev/null
+++ b/clang/lib/Analysis/Scalable/EntityLinker/EntityLinker.cpp
@@ -0,0 +1,192 @@
+//===- EntityLinker.cpp ----------------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/Scalable/EntityLinker/EntityLinker.h"
+#include "clang/Analysis/Scalable/EntityLinker/TUSummaryEncoding.h"
+#include "clang/Analysis/Scalable/Serialization/SerializationFormat.h"
+#include "clang/Analysis/Scalable/Support/ErrorBuilder.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/FormatVariadic.h"
+
+using namespace clang::ssaf;
+
+//----------------------------------------------------------------------------
+// Error Message Constants
+//----------------------------------------------------------------------------
+
+namespace {
+
+namespace ErrorMessages {
+
+constexpr const char *EntityIdAlreadyExistsInLinkageTable =
+ "EntityId({0}) already exists in LU linkage table";
+
+constexpr const char *FailedToMergeSummaryData =
+ "failed to merge summary data for TU EntityId({0}) resolved to LU "
+ "EntityId({1}) with linkage '{2}'";
+
+constexpr const char *MissingLinkageInformation =
+ "missing linkage information for TU EntityId({0})";
+
+constexpr const char *DuplicateEntityIdInLinking =
+ "duplicate TU EntityId({0}) encountered during linking";
+
+constexpr const char *ResolvingEntity =
+ "resolving entity '{0}' (TU EntityId({1}))";
+constexpr const char *MergingSummaryData = "merging summary data";
+constexpr const char *LinkingTUSummary = "linking TU summary";
+
+} // namespace ErrorMessages
+
+} // namespace
+
+static NestedBuildNamespace
+resolveNamespace(const NestedBuildNamespace &LUNamespace,
+ const NestedBuildNamespace &EntityNamespace,
+ const EntityLinkage::LinkageType Linkage) {
+ switch (Linkage) {
+ case EntityLinkage::LinkageType::None:
+ case EntityLinkage::LinkageType::Internal:
+ return EntityNamespace.makeQualified(LUNamespace);
+ case EntityLinkage::LinkageType::External:
+ return NestedBuildNamespace(LUNamespace);
+ }
+
+ llvm_unreachable("Unhandled EntityLinkage::LinkageType variant");
+}
+
+llvm::Expected<EntityId> EntityLinker::resolve(const EntityName &OldName,
+ const EntityId OldId,
+ const EntityLinkage &EL) {
+ NestedBuildNamespace NewNamespace =
+ resolveNamespace(Output.LUNamespace, OldName.Namespace, EL.getLinkage());
+
+ EntityName NewName(OldName.USR, OldName.Suffix, NewNamespace);
+
+ // NewId construction will always return a fresh id for `None` and `Internal`
+ // linkage entities since their namespaces will be different even if their
+ // names clash. For `External` linkage entities with clashing names this
+ // function will return the id assigned at the first insertion.
+ EntityId NewId = Output.IdTable.getId(NewName);
+
+ auto [It, Inserted] = Output.LinkageTable.try_emplace(NewId, EL);
+ if (!Inserted) {
+ return ErrorBuilder::create(
+ llvm::inconvertibleErrorCode(),
+ ErrorMessages::EntityIdAlreadyExistsInLinkageTable, NewId.Index)
+ .build();
+ }
+
+ return NewId;
+}
+
+llvm::Error EntityLinker::merge(
+ std::map<SummaryName,
+ std::map<EntityId, std::unique_ptr<EntitySummaryEncoding>>>
+ &InputData,
+ std::map<SummaryName,
+ std::map<EntityId, std::unique_ptr<EntitySummaryEncoding>>>
+ &OutputData,
+ const EntityId OldId, const EntityId NewId, const EntityLinkage &EL,
+ std::vector<EntitySummaryEncoding *> &PatchTargets) {
+ for (auto &[Name, DataMap] : InputData) {
+ auto Iter = DataMap.find(OldId);
+ if (Iter == DataMap.end())
+ continue;
+
+ auto &OutputMap = OutputData[Name];
+ auto Result = OutputMap.insert({NewId, std::move(Iter->second)});
+
+ // If insertion is successful, we will have to replace OldId with NewId in
+ // this EntitySummaryEncoding.
+ if (Result.second) {
+ PatchTargets.push_back(Result.first->second.get());
+ } else {
+ switch (EL.getLinkage()) {
+ // Insertion should never fail for `None` and `Internal` linkage
+ // entities because these entities have different namespaces even if
+ // their names clash.
+ case EntityLinkage::LinkageType::None:
+ return ErrorBuilder::create(llvm::inconvertibleErrorCode(),
+ ErrorMessages::FailedToMergeSummaryData,
+ OldId.Index, NewId.Index, "None")
+ .build();
+ case EntityLinkage::LinkageType::Internal:
+ return ErrorBuilder::create(llvm::inconvertibleErrorCode(),
+ ErrorMessages::FailedToMergeSummaryData,
+ OldId.Index, NewId.Index, "Internal")
+ .build();
+ case EntityLinkage::LinkageType::External:
+ // Insertion is expected to fail for duplicate occurrences of `External`
+ // linkage entities. We will report these cases to help users debug
+ // potential ODR violations.
+ // TODO - issue diagnostic log for dropping data using instrumentation
+ // framework.
+ break;
+ }
+ }
+ }
+
+ return llvm::Error::success();
+}
+
+void EntityLinker::patch(
+ std::vector<EntitySummaryEncoding *> &PatchTargets,
+ const std::map<EntityId, EntityId> &EntityResolutionTable) {
+ for (auto *PatchTarget : PatchTargets) {
+ PatchTarget->patch(EntityResolutionTable);
+ }
+}
+
+llvm::Error EntityLinker::link(std::unique_ptr<TUSummaryEncoding> Summary) {
+ std::map<EntityId, EntityId> EntityResolutionTable;
+ std::vector<EntitySummaryEncoding *> PatchTargets;
+
+ for (const auto &[OldName, OldId] : Summary->IdTable.Entities) {
+
+ auto Iter = Summary->LinkageTable.find(OldId);
+ if (Iter == Summary->LinkageTable.end()) {
+ return ErrorBuilder::create(llvm::inconvertibleErrorCode(),
+ ErrorMessages::MissingLinkageInformation,
+ OldId.Index)
+ .context(ErrorMessages::LinkingTUSummary)
+ .build();
+ }
+
+ EntityLinkage &Linkage = Iter->second;
+
+ llvm::Expected<EntityId> NewIdOrErr = resolve(OldName, OldId, Linkage);
+ if (!NewIdOrErr)
+ return ErrorBuilder::wrap(NewIdOrErr.takeError())
+ .context(ErrorMessages::LinkingTUSummary)
+ .build();
+
+ EntityId NewId = *NewIdOrErr;
+
+ auto Res = EntityResolutionTable.insert({OldId, NewId});
+ if (!Res.second) {
+ return ErrorBuilder::create(llvm::inconvertibleErrorCode(),
+ ErrorMessages::DuplicateEntityIdInLinking,
+ OldId.Index)
+ .context(ErrorMessages::LinkingTUSummary)
+ .build();
+ }
+
+ if (llvm::Error Err = merge(Summary->Data, Output.Data, OldId, NewId,
+ Linkage, PatchTargets))
+ return ErrorBuilder::wrap(std::move(Err))
+ .context(ErrorMessages::MergingSummaryData)
+ .context(ErrorMessages::LinkingTUSummary)
+ .build();
+ }
+
+ patch(PatchTargets, EntityResolutionTable);
+
+ return llvm::Error::success();
+}
diff --git a/clang/lib/Analysis/Scalable/Serialization/JSONFormat.cpp b/clang/lib/Analysis/Scalable/Serialization/JSONFormat.cpp
index 6f7de45e863d1..0007ddf6a275f 100644
--- a/clang/lib/Analysis/Scalable/Serialization/JSONFormat.cpp
+++ b/clang/lib/Analysis/Scalable/Serialization/JSONFormat.cpp
@@ -1,4 +1,5 @@
#include "clang/Analysis/Scalable/Serialization/JSONFormat.h"
+#include "clang/Analysis/Scalable/Support/ErrorBuilder.h"
#include "clang/Analysis/Scalable/TUSummary/TUSummary.h"
#include "llvm/ADT/StringExtras.h"
@@ -15,102 +16,6 @@ using Array = llvm::json::Array;
using Object = llvm::json::Object;
using Value = llvm::json::Value;
-//----------------------------------------------------------------------------
-// ErrorBuilder - Fluent API for constructing contextual errors.
-//----------------------------------------------------------------------------
-
-namespace {
-
-class ErrorBuilder {
-private:
- std::error_code Code;
- std::vector<std::string> ContextStack;
-
- // Private constructor - only accessible via static factories.
- explicit ErrorBuilder(std::error_code EC) : Code(EC) {}
-
- // Helper: Format message and add to context stack.
- template <typename... Args>
- void addFormattedContext(const char *Fmt, Args &&...ArgVals) {
- std::string Message =
- llvm::formatv(Fmt, std::forward<Args>(ArgVals)...).str();
- ContextStack.push_back(std::move(Message));
- }
-
-public:
- // Static factory: Create new error from error code and formatted message.
- template <typename... Args>
- static ErrorBuilder create(std::error_code EC, const char *Fmt,
- Args &&...ArgVals) {
- ErrorBuilder Builder(EC);
- Builder.addFormattedContext(Fmt, std::forward<Args>(ArgVals)...);
- return Builder;
- }
-
- // Convenience overload for std::errc.
- template <typename... Args>
- static ErrorBuilder create(std::errc EC, const char *Fmt, Args &&...ArgVals) {
- return create(std::make_error_code(EC), Fmt,
- std::forward<Args>(ArgVals)...);
- }
-
- // Static factory: Wrap existing error and optionally add context.
- static ErrorBuilder wrap(llvm::Error E) {
- if (!E) {
- llvm::consumeError(std::move(E));
- // Return builder with generic error code for success case.
- return ErrorBuilder(std::make_error_code(std::errc::invalid_argument));
- }
-
- std::error_code EC;
- bool FirstError = true;
- ErrorBuilder Builder(std::make_error_code(std::errc::invalid_argument));
-
- llvm::handleAllErrors(std::move(E), [&](const llvm::ErrorInfoBase &EI) {
- // Capture error code from the first error only.
- if (FirstError) {
- EC = EI.convertToErrorCode();
- Builder.Code = EC;
- FirstError = false;
- }
-
- // Collect messages from all errors.
- std::string ErrorMsg = EI.message();
- if (!ErrorMsg.empty()) {
- Builder.ContextStack.push_back(std::move(ErrorMsg));
- }
- });
-
- return Builder;
- }
-
- // Add context (plain string).
- ErrorBuilder &context(const char *Msg) {
- ContextStack.push_back(Msg);
- return *this;
- }
-
- // Add context (formatted string).
- template <typename... Args>
- ErrorBuilder &context(const char *Fmt, Args &&...ArgVals) {
- addFormattedContext(Fmt, std::forward<Args>(ArgVals)...);
- return *this;
- }
-
- // Build the final error.
- llvm::Error build() {
- if (ContextStack.empty())
- return llvm::Error::success();
-
- // Reverse the context stack so that the most recent context appears first
- // and the wrapped error (if any) appears last.
- return llvm::createStringError(
- llvm::join(llvm::reverse(ContextStack), "\n"), Code);
- }
-};
-
-} // namespace
-
//----------------------------------------------------------------------------
// File Format Constant
//----------------------------------------------------------------------------
diff --git a/clang/lib/Analysis/Scalable/Support/ErrorBuilder.cpp b/clang/lib/Analysis/Scalable/Support/ErrorBuilder.cpp
new file mode 100644
index 0000000000000..ad0a013bcf2f2
--- /dev/null
+++ b/clang/lib/Analysis/Scalable/Support/ErrorBuilder.cpp
@@ -0,0 +1,59 @@
+//===- ErrorBuilder.cpp ----------------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/Scalable/Support/ErrorBuilder.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringExtras.h"
+
+namespace clang::ssaf {
+
+ErrorBuilder ErrorBuilder::wrap(llvm::Error E) {
+ if (!E) {
+ llvm::consumeError(std::move(E));
+ // Return builder with generic error code for success case.
+ return ErrorBuilder(std::make_error_code(std::errc::invalid_argument));
+ }
+
+ std::error_code EC;
+ bool FirstError = true;
+ ErrorBuilder Builder(std::make_error_code(std::errc::invalid_argument));
+
+ llvm::handleAllErrors(std::move(E), [&](const llvm::ErrorInfoBase &EI) {
+ // Capture error code from the first error only.
+ if (FirstError) {
+ EC = EI.convertToErrorCode();
+ Builder.Code = EC;
+ FirstError = false;
+ }
+
+ // Collect messages from all errors.
+ std::string ErrorMsg = EI.message();
+ if (!ErrorMsg.empty()) {
+ Builder.ContextStack.push_back(std::move(ErrorMsg));
+ }
+ });
+
+ return Builder;
+}
+
+ErrorBuilder &ErrorBuilder::context(const char *Msg) {
+ ContextStack.push_back(Msg);
+ return *this;
+}
+
+llvm::Error ErrorBuilder::build() {
+ if (ContextStack.empty())
+ return llvm::Error::success();
+
+ // Reverse the context stack so that the most recent context appears first
+ // and the wrapped error (if any) appears last.
+ return llvm::createStringError(llvm::join(llvm::reverse(ContextStack), "\n"),
+ Code);
+}
+
+} // namespace clang::ssaf
>From 848d72a7612a881d518f0b49a391398aeb78cf68 Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Mon, 16 Feb 2026 17:44:17 -0800
Subject: [PATCH 2/6] Quality Fixes
---
.../Scalable/EntityLinker/EntityLinker.h | 5 ++--
.../Analysis/Scalable/Model/EntityLinkage.h | 5 ++++
clang/lib/Analysis/Scalable/CMakeLists.txt | 1 +
.../Scalable/EntityLinker/EntityLinker.cpp | 29 +++++++------------
.../Analysis/Scalable/Model/EntityLinkage.cpp | 27 +++++++++++++++++
5 files changed, 47 insertions(+), 20 deletions(-)
create mode 100644 clang/lib/Analysis/Scalable/Model/EntityLinkage.cpp
diff --git a/clang/include/clang/Analysis/Scalable/EntityLinker/EntityLinker.h b/clang/include/clang/Analysis/Scalable/EntityLinker/EntityLinker.h
index b628af9d25843..4788f27ce24a0 100644
--- a/clang/include/clang/Analysis/Scalable/EntityLinker/EntityLinker.h
+++ b/clang/include/clang/Analysis/Scalable/EntityLinker/EntityLinker.h
@@ -44,7 +44,7 @@ class EntityLinker {
private:
llvm::Expected<EntityId> resolve(const EntityName &OldName,
const EntityId OldId,
- const EntityLinkage &EL);
+ const EntityLinkage &Linkage);
llvm::Error
merge(std::map<SummaryName,
@@ -53,7 +53,8 @@ class EntityLinker {
std::map<SummaryName,
std::map<EntityId, std::unique_ptr<EntitySummaryEncoding>>>
&OutputData,
- const EntityId OldId, const EntityId NewId, const EntityLinkage &EL,
+ const EntityId OldId, const EntityId NewId,
+ const EntityLinkage &Linkage,
std::vector<EntitySummaryEncoding *> &PatchTargets);
void patch(std::vector<EntitySummaryEncoding *> &PatchTargets,
diff --git a/clang/include/clang/Analysis/Scalable/Model/EntityLinkage.h b/clang/include/clang/Analysis/Scalable/Model/EntityLinkage.h
index af775769dc1e4..a0014f0039f61 100644
--- a/clang/include/clang/Analysis/Scalable/Model/EntityLinkage.h
+++ b/clang/include/clang/Analysis/Scalable/Model/EntityLinkage.h
@@ -9,6 +9,8 @@
#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_MODEL_ENTITYLINKAGE_H
#define LLVM_CLANG_ANALYSIS_SCALABLE_MODEL_ENTITYLINKAGE_H
+#include "llvm/ADT/StringRef.h"
+
namespace clang::ssaf {
/// Represents the linkage properties of an entity in the program model.
@@ -34,6 +36,9 @@ class EntityLinkage {
friend class SerializationFormat;
};
+/// Returns a string representation of the linkage type.
+llvm::StringRef toString(EntityLinkage::LinkageType Linkage);
+
} // namespace clang::ssaf
#endif // LLVM_CLANG_ANALYSIS_SCALABLE_MODEL_ENTITYLINKAGE_H
diff --git a/clang/lib/Analysis/Scalable/CMakeLists.txt b/clang/lib/Analysis/Scalable/CMakeLists.txt
index 8ef1625cc704b..7e4404d96bbd4 100644
--- a/clang/lib/Analysis/Scalable/CMakeLists.txt
+++ b/clang/lib/Analysis/Scalable/CMakeLists.txt
@@ -7,6 +7,7 @@ add_clang_library(clangAnalysisScalable
EntityLinker/EntityLinker.cpp
Model/BuildNamespace.cpp
Model/EntityIdTable.cpp
+ Model/EntityLinkage.cpp
Model/EntityName.cpp
Serialization/JSONFormat.cpp
Serialization/SerializationFormatRegistry.cpp
diff --git a/clang/lib/Analysis/Scalable/EntityLinker/EntityLinker.cpp b/clang/lib/Analysis/Scalable/EntityLinker/EntityLinker.cpp
index 2c7344b19de55..9b15181721566 100644
--- a/clang/lib/Analysis/Scalable/EntityLinker/EntityLinker.cpp
+++ b/clang/lib/Analysis/Scalable/EntityLinker/EntityLinker.cpp
@@ -8,11 +8,9 @@
#include "clang/Analysis/Scalable/EntityLinker/EntityLinker.h"
#include "clang/Analysis/Scalable/EntityLinker/TUSummaryEncoding.h"
-#include "clang/Analysis/Scalable/Serialization/SerializationFormat.h"
#include "clang/Analysis/Scalable/Support/ErrorBuilder.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorHandling.h"
-#include "llvm/Support/FormatVariadic.h"
using namespace clang::ssaf;
@@ -37,8 +35,6 @@ constexpr const char *MissingLinkageInformation =
constexpr const char *DuplicateEntityIdInLinking =
"duplicate TU EntityId({0}) encountered during linking";
-constexpr const char *ResolvingEntity =
- "resolving entity '{0}' (TU EntityId({1}))";
constexpr const char *MergingSummaryData = "merging summary data";
constexpr const char *LinkingTUSummary = "linking TU summary";
@@ -63,9 +59,9 @@ resolveNamespace(const NestedBuildNamespace &LUNamespace,
llvm::Expected<EntityId> EntityLinker::resolve(const EntityName &OldName,
const EntityId OldId,
- const EntityLinkage &EL) {
- NestedBuildNamespace NewNamespace =
- resolveNamespace(Output.LUNamespace, OldName.Namespace, EL.getLinkage());
+ const EntityLinkage &Linkage) {
+ NestedBuildNamespace NewNamespace = resolveNamespace(
+ Output.LUNamespace, OldName.Namespace, Linkage.getLinkage());
EntityName NewName(OldName.USR, OldName.Suffix, NewNamespace);
@@ -75,7 +71,7 @@ llvm::Expected<EntityId> EntityLinker::resolve(const EntityName &OldName,
// function will return the id assigned at the first insertion.
EntityId NewId = Output.IdTable.getId(NewName);
- auto [It, Inserted] = Output.LinkageTable.try_emplace(NewId, EL);
+ auto [It, Inserted] = Output.LinkageTable.try_emplace(NewId, Linkage);
if (!Inserted) {
return ErrorBuilder::create(
llvm::inconvertibleErrorCode(),
@@ -93,7 +89,7 @@ llvm::Error EntityLinker::merge(
std::map<SummaryName,
std::map<EntityId, std::unique_ptr<EntitySummaryEncoding>>>
&OutputData,
- const EntityId OldId, const EntityId NewId, const EntityLinkage &EL,
+ const EntityId OldId, const EntityId NewId, const EntityLinkage &Linkage,
std::vector<EntitySummaryEncoding *> &PatchTargets) {
for (auto &[Name, DataMap] : InputData) {
auto Iter = DataMap.find(OldId);
@@ -101,26 +97,23 @@ llvm::Error EntityLinker::merge(
continue;
auto &OutputMap = OutputData[Name];
- auto Result = OutputMap.insert({NewId, std::move(Iter->second)});
+ auto InsertResult = OutputMap.insert({NewId, std::move(Iter->second)});
// If insertion is successful, we will have to replace OldId with NewId in
// this EntitySummaryEncoding.
- if (Result.second) {
- PatchTargets.push_back(Result.first->second.get());
+ if (InsertResult.second) {
+ PatchTargets.push_back(InsertResult.first->second.get());
} else {
- switch (EL.getLinkage()) {
+ switch (Linkage.getLinkage()) {
// Insertion should never fail for `None` and `Internal` linkage
// entities because these entities have different namespaces even if
// their names clash.
case EntityLinkage::LinkageType::None:
- return ErrorBuilder::create(llvm::inconvertibleErrorCode(),
- ErrorMessages::FailedToMergeSummaryData,
- OldId.Index, NewId.Index, "None")
- .build();
case EntityLinkage::LinkageType::Internal:
return ErrorBuilder::create(llvm::inconvertibleErrorCode(),
ErrorMessages::FailedToMergeSummaryData,
- OldId.Index, NewId.Index, "Internal")
+ OldId.Index, NewId.Index,
+ toString(Linkage.getLinkage()))
.build();
case EntityLinkage::LinkageType::External:
// Insertion is expected to fail for duplicate occurrences of `External`
diff --git a/clang/lib/Analysis/Scalable/Model/EntityLinkage.cpp b/clang/lib/Analysis/Scalable/Model/EntityLinkage.cpp
new file mode 100644
index 0000000000000..b1f50964969c3
--- /dev/null
+++ b/clang/lib/Analysis/Scalable/Model/EntityLinkage.cpp
@@ -0,0 +1,27 @@
+//===- EntityLinkage.cpp ----------------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/Scalable/Model/EntityLinkage.h"
+#include "llvm/Support/ErrorHandling.h"
+
+namespace clang::ssaf {
+
+llvm::StringRef toString(EntityLinkage::LinkageType Linkage) {
+ switch (Linkage) {
+ case EntityLinkage::LinkageType::None:
+ return "None";
+ case EntityLinkage::LinkageType::Internal:
+ return "Internal";
+ case EntityLinkage::LinkageType::External:
+ return "External";
+ }
+
+ llvm_unreachable("Unhandled EntityLinkage::LinkageType variant");
+}
+
+} // namespace clang::ssaf
>From f8ac91011576e959f3a45e1dfb1e88084dc49a4b Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Tue, 17 Feb 2026 11:38:44 -0800
Subject: [PATCH 3/6] More quality fixes
---
.../Scalable/EntityLinker/EntityLinker.h | 21 ++++++++++++---
.../Scalable/EntityLinker/LUSummary.h | 6 ++---
.../Scalable/EntityLinker/LUSummaryEncoding.h | 10 ++++---
.../Scalable/EntityLinker/TUSummaryEncoding.h | 11 +++++---
.../clang/Analysis/Scalable/Model/EntityId.h | 8 +++---
.../Analysis/Scalable/Model/EntityIdTable.h | 6 ++---
.../Analysis/Scalable/Model/EntityLinkage.h | 4 +--
.../Analysis/Scalable/Model/EntityName.h | 8 +++---
.../Scalable/EntityLinker/EntityLinker.cpp | 26 ++++++++++++-------
9 files changed, 64 insertions(+), 36 deletions(-)
diff --git a/clang/include/clang/Analysis/Scalable/EntityLinker/EntityLinker.h b/clang/include/clang/Analysis/Scalable/EntityLinker/EntityLinker.h
index 4788f27ce24a0..c362a2f87d0f7 100644
--- a/clang/include/clang/Analysis/Scalable/EntityLinker/EntityLinker.h
+++ b/clang/include/clang/Analysis/Scalable/EntityLinker/EntityLinker.h
@@ -17,8 +17,6 @@
#include "clang/Analysis/Scalable/EntityLinker/LUSummaryEncoding.h"
#include "clang/Analysis/Scalable/Model/BuildNamespace.h"
#include "clang/Analysis/Scalable/Model/EntityId.h"
-#include "clang/Analysis/Scalable/Model/EntityLinkage.h"
-#include "clang/Analysis/Scalable/Model/EntityName.h"
#include "clang/Analysis/Scalable/Model/SummaryName.h"
#include "llvm/Support/Error.h"
#include <map>
@@ -27,6 +25,8 @@
namespace clang::ssaf {
+class EntityLinkage;
+class EntityName;
class EntitySummaryEncoding;
class TUSummaryEncoding;
@@ -34,16 +34,31 @@ class EntityLinker {
LUSummaryEncoding Output;
public:
+ /// Constructs an EntityLinker for a link unit.
+ ///
+ /// \param LUNamespace The namespace identifying this link unit.
EntityLinker(NestedBuildNamespace LUNamespace)
: Output(std::move(LUNamespace)) {}
+ /// Links a translation unit summary into the link unit summary.
+ ///
+ /// Processes entity names, resolves namespace conflicts based on linkage,
+ /// deduplicates entities, and patches entity ID references in the summary
+ /// data. The provided TU summary is consumed by this operation.
+ ///
+ /// \param Summary The TU summary to link. Ownership is transferred.
+ /// \returns Error if linking fails (e.g., duplicate internal entities,
+ /// missing linkage information), success otherwise.
llvm::Error link(std::unique_ptr<TUSummaryEncoding> Summary);
+ /// Returns the accumulated link unit summary.
+ ///
+ /// \returns A const reference to the linked output containing all
+ /// deduplicated and patched entity summaries.
const LUSummaryEncoding &getOutput() const { return Output; }
private:
llvm::Expected<EntityId> resolve(const EntityName &OldName,
- const EntityId OldId,
const EntityLinkage &Linkage);
llvm::Error
diff --git a/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummary.h b/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummary.h
index 54d18d78d53bf..735e569cbd4af 100644
--- a/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummary.h
+++ b/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummary.h
@@ -34,6 +34,9 @@ class SummaryViewBuilder;
/// together. It contains deduplicated entities with their linkage information
/// and the merged entity summaries.
class LUSummary {
+ friend class SerializationFormat;
+ friend class SummaryViewBuilder;
+
NestedBuildNamespace LUNamespace;
EntityIdTable IdTable;
@@ -46,9 +49,6 @@ class LUSummary {
public:
LUSummary(NestedBuildNamespace LUNamespace)
: LUNamespace(std::move(LUNamespace)) {}
-
- friend class SerializationFormat;
- friend class SummaryViewBuilder;
};
} // namespace clang::ssaf
diff --git a/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummaryEncoding.h b/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummaryEncoding.h
index 39185990a9ea6..196e2109471cc 100644
--- a/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummaryEncoding.h
+++ b/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummaryEncoding.h
@@ -34,12 +34,19 @@ class SerializationFormat;
/// translation units in a format-specific encoding. It is produced by the
/// entity linker and contains deduplicated and patched entity summaries.
class LUSummaryEncoding {
+ friend class EntityLinker;
+ friend class SerializationFormat;
+
+ /// The namespace identifying this link unit.
NestedBuildNamespace LUNamespace;
+ /// Maps entity names to their unique identifiers within this link unit.
EntityIdTable IdTable;
+ /// Maps entity IDs to their linkage properties (None, Internal, External).
std::map<EntityId, EntityLinkage> LinkageTable;
+ /// Encoded summary data organized by summary type and entity ID.
std::map<SummaryName,
std::map<EntityId, std::unique_ptr<EntitySummaryEncoding>>>
Data;
@@ -47,9 +54,6 @@ class LUSummaryEncoding {
public:
LUSummaryEncoding(NestedBuildNamespace LUNamespace)
: LUNamespace(std::move(LUNamespace)) {}
-
- friend class EntityLinker;
- friend class SerializationFormat;
};
} // namespace clang::ssaf
diff --git a/clang/include/clang/Analysis/Scalable/EntityLinker/TUSummaryEncoding.h b/clang/include/clang/Analysis/Scalable/EntityLinker/TUSummaryEncoding.h
index 1b42eb70d09d0..b73a58b9d2984 100644
--- a/clang/include/clang/Analysis/Scalable/EntityLinker/TUSummaryEncoding.h
+++ b/clang/include/clang/Analysis/Scalable/EntityLinker/TUSummaryEncoding.h
@@ -35,13 +35,19 @@ class SerializationFormat;
/// full EntitySummary objects. This enables efficient entity ID patching
/// during the linking process.
class TUSummaryEncoding {
- /// Identifies the translation unit.
+ friend class EntityLinker;
+ friend class SerializationFormat;
+
+ /// The namespace identifying this translation unit.
BuildNamespace TUNamespace;
+ /// Maps entity names to their unique identifiers within this TU.
EntityIdTable IdTable;
+ /// Maps entity IDs to their linkage properties (None, Internal, External).
std::map<EntityId, EntityLinkage> LinkageTable;
+ /// Encoded summary data organized by summary type and entity ID.
std::map<SummaryName,
std::map<EntityId, std::unique_ptr<EntitySummaryEncoding>>>
Data;
@@ -49,9 +55,6 @@ class TUSummaryEncoding {
public:
TUSummaryEncoding(BuildNamespace TUNamespace)
: TUNamespace(std::move(TUNamespace)) {}
-
- friend class EntityLinker;
- friend class SerializationFormat;
};
} // namespace clang::ssaf
diff --git a/clang/include/clang/Analysis/Scalable/Model/EntityId.h b/clang/include/clang/Analysis/Scalable/Model/EntityId.h
index 231525b445ca0..e6f57adf24690 100644
--- a/clang/include/clang/Analysis/Scalable/Model/EntityId.h
+++ b/clang/include/clang/Analysis/Scalable/Model/EntityId.h
@@ -28,6 +28,10 @@ class EntityIdTable;
///
/// \see EntityIdTable
class EntityId {
+ friend class EntityIdTable;
+ friend class EntityLinker;
+ friend class SerializationFormat;
+
size_t Index;
explicit EntityId(size_t Index) : Index(Index) {}
@@ -38,10 +42,6 @@ class EntityId {
bool operator==(const EntityId &Other) const { return Index == Other.Index; }
bool operator<(const EntityId &Other) const { return Index < Other.Index; }
bool operator!=(const EntityId &Other) const { return !(*this == Other); }
-
- friend class EntityIdTable;
- friend class EntityLinker;
- friend class SerializationFormat;
};
} // namespace clang::ssaf
diff --git a/clang/include/clang/Analysis/Scalable/Model/EntityIdTable.h b/clang/include/clang/Analysis/Scalable/Model/EntityIdTable.h
index b12d3e0c0faec..e563cacb63ca4 100644
--- a/clang/include/clang/Analysis/Scalable/Model/EntityIdTable.h
+++ b/clang/include/clang/Analysis/Scalable/Model/EntityIdTable.h
@@ -21,6 +21,9 @@ namespace clang::ssaf {
/// The table maps each unique EntityName to exactly one EntityId.
/// Entities are never removed.
class EntityIdTable {
+ friend class EntityLinker;
+ friend class SerializationFormat;
+
std::map<EntityName, EntityId> Entities;
public:
@@ -43,9 +46,6 @@ class EntityIdTable {
/// Returns the number of unique entities in the table.
size_t count() const { return Entities.size(); }
-
- friend class EntityLinker;
- friend class SerializationFormat;
};
} // namespace clang::ssaf
diff --git a/clang/include/clang/Analysis/Scalable/Model/EntityLinkage.h b/clang/include/clang/Analysis/Scalable/Model/EntityLinkage.h
index a0014f0039f61..4e715fc12af4c 100644
--- a/clang/include/clang/Analysis/Scalable/Model/EntityLinkage.h
+++ b/clang/include/clang/Analysis/Scalable/Model/EntityLinkage.h
@@ -19,6 +19,8 @@ namespace clang::ssaf {
/// or external linkage, which determines its visibility and accessibility
/// across translation units.
class EntityLinkage {
+ friend class SerializationFormat;
+
public:
enum class LinkageType {
None, ///< local variables, function parameters
@@ -32,8 +34,6 @@ class EntityLinkage {
private:
LinkageType Linkage;
-
- friend class SerializationFormat;
};
/// Returns a string representation of the linkage type.
diff --git a/clang/include/clang/Analysis/Scalable/Model/EntityName.h b/clang/include/clang/Analysis/Scalable/Model/EntityName.h
index 6bf51844f2f5b..42021cfd6b610 100644
--- a/clang/include/clang/Analysis/Scalable/Model/EntityName.h
+++ b/clang/include/clang/Analysis/Scalable/Model/EntityName.h
@@ -25,6 +25,10 @@ namespace clang::ssaf {
/// Client code should not make assumptions about the implementation details,
/// such as USRs.
class EntityName {
+ friend class EntityLinker;
+ friend class LinkUnitResolution;
+ friend class SerializationFormat;
+
std::string USR;
llvm::SmallString<16> Suffix;
NestedBuildNamespace Namespace;
@@ -46,10 +50,6 @@ class EntityName {
///
/// \param Namespace The namespace steps to append to this entity's namespace.
EntityName makeQualified(NestedBuildNamespace Namespace) const;
-
- friend class EntityLinker;
- friend class LinkUnitResolution;
- friend class SerializationFormat;
};
} // namespace clang::ssaf
diff --git a/clang/lib/Analysis/Scalable/EntityLinker/EntityLinker.cpp b/clang/lib/Analysis/Scalable/EntityLinker/EntityLinker.cpp
index 9b15181721566..b23c19f0936b2 100644
--- a/clang/lib/Analysis/Scalable/EntityLinker/EntityLinker.cpp
+++ b/clang/lib/Analysis/Scalable/EntityLinker/EntityLinker.cpp
@@ -8,9 +8,12 @@
#include "clang/Analysis/Scalable/EntityLinker/EntityLinker.h"
#include "clang/Analysis/Scalable/EntityLinker/TUSummaryEncoding.h"
+#include "clang/Analysis/Scalable/Model/EntityLinkage.h"
+#include "clang/Analysis/Scalable/Model/EntityName.h"
#include "clang/Analysis/Scalable/Support/ErrorBuilder.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorHandling.h"
+#include <cassert>
using namespace clang::ssaf;
@@ -58,7 +61,6 @@ resolveNamespace(const NestedBuildNamespace &LUNamespace,
}
llvm::Expected<EntityId> EntityLinker::resolve(const EntityName &OldName,
- const EntityId OldId,
const EntityLinkage &Linkage) {
NestedBuildNamespace NewNamespace = resolveNamespace(
Output.LUNamespace, OldName.Namespace, Linkage.getLinkage());
@@ -71,7 +73,8 @@ llvm::Expected<EntityId> EntityLinker::resolve(const EntityName &OldName,
// function will return the id assigned at the first insertion.
EntityId NewId = Output.IdTable.getId(NewName);
- auto [It, Inserted] = Output.LinkageTable.try_emplace(NewId, Linkage);
+ [[maybe_unused]] auto [It, Inserted] =
+ Output.LinkageTable.try_emplace(NewId, Linkage);
if (!Inserted) {
return ErrorBuilder::create(
llvm::inconvertibleErrorCode(),
@@ -93,8 +96,9 @@ llvm::Error EntityLinker::merge(
std::vector<EntitySummaryEncoding *> &PatchTargets) {
for (auto &[Name, DataMap] : InputData) {
auto Iter = DataMap.find(OldId);
- if (Iter == DataMap.end())
+ if (Iter == DataMap.end()) {
continue;
+ }
auto &OutputMap = OutputData[Name];
auto InsertResult = OutputMap.insert({NewId, std::move(Iter->second)});
@@ -133,6 +137,7 @@ void EntityLinker::patch(
std::vector<EntitySummaryEncoding *> &PatchTargets,
const std::map<EntityId, EntityId> &EntityResolutionTable) {
for (auto *PatchTarget : PatchTargets) {
+ assert(PatchTarget && "Patch target cannot be null");
PatchTarget->patch(EntityResolutionTable);
}
}
@@ -142,7 +147,6 @@ llvm::Error EntityLinker::link(std::unique_ptr<TUSummaryEncoding> Summary) {
std::vector<EntitySummaryEncoding *> PatchTargets;
for (const auto &[OldName, OldId] : Summary->IdTable.Entities) {
-
auto Iter = Summary->LinkageTable.find(OldId);
if (Iter == Summary->LinkageTable.end()) {
return ErrorBuilder::create(llvm::inconvertibleErrorCode(),
@@ -152,18 +156,19 @@ llvm::Error EntityLinker::link(std::unique_ptr<TUSummaryEncoding> Summary) {
.build();
}
- EntityLinkage &Linkage = Iter->second;
+ const EntityLinkage &Linkage = Iter->second;
- llvm::Expected<EntityId> NewIdOrErr = resolve(OldName, OldId, Linkage);
- if (!NewIdOrErr)
+ auto NewIdOrErr = resolve(OldName, Linkage);
+ if (!NewIdOrErr) {
return ErrorBuilder::wrap(NewIdOrErr.takeError())
.context(ErrorMessages::LinkingTUSummary)
.build();
+ }
EntityId NewId = *NewIdOrErr;
- auto Res = EntityResolutionTable.insert({OldId, NewId});
- if (!Res.second) {
+ auto InsertResult = EntityResolutionTable.insert({OldId, NewId});
+ if (!InsertResult.second) {
return ErrorBuilder::create(llvm::inconvertibleErrorCode(),
ErrorMessages::DuplicateEntityIdInLinking,
OldId.Index)
@@ -172,11 +177,12 @@ llvm::Error EntityLinker::link(std::unique_ptr<TUSummaryEncoding> Summary) {
}
if (llvm::Error Err = merge(Summary->Data, Output.Data, OldId, NewId,
- Linkage, PatchTargets))
+ Linkage, PatchTargets)) {
return ErrorBuilder::wrap(std::move(Err))
.context(ErrorMessages::MergingSummaryData)
.context(ErrorMessages::LinkingTUSummary)
.build();
+ }
}
patch(PatchTargets, EntityResolutionTable);
>From 5d929d62deac051f72a6e093763547423a2d527f Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Tue, 17 Feb 2026 16:36:14 -0800
Subject: [PATCH 4/6] Tests
---
.../Scalable/EntityLinker/EntityLinker.h | 5 +
.../Scalable/EntityLinker/LUSummary.h | 1 +
.../Scalable/EntityLinker/LUSummaryEncoding.h | 1 +
.../Scalable/EntityLinker/TUSummaryEncoding.h | 1 +
.../Scalable/Model/PrivateFieldNames.def | 12 +
.../Serialization/SerializationFormat.h | 8 +
.../Scalable/EntityLinker/EntityLinker.cpp | 13 +-
.../Analysis/Scalable/CMakeLists.txt | 1 +
.../Analysis/Scalable/EntityLinkerTest.cpp | 713 ++++++++++++++++++
.../unittests/Analysis/Scalable/TestFixture.h | 4 +
10 files changed, 753 insertions(+), 6 deletions(-)
create mode 100644 clang/unittests/Analysis/Scalable/EntityLinkerTest.cpp
diff --git a/clang/include/clang/Analysis/Scalable/EntityLinker/EntityLinker.h b/clang/include/clang/Analysis/Scalable/EntityLinker/EntityLinker.h
index c362a2f87d0f7..2e203ee5f13d7 100644
--- a/clang/include/clang/Analysis/Scalable/EntityLinker/EntityLinker.h
+++ b/clang/include/clang/Analysis/Scalable/EntityLinker/EntityLinker.h
@@ -57,6 +57,11 @@ class EntityLinker {
/// deduplicated and patched entity summaries.
const LUSummaryEncoding &getOutput() const { return Output; }
+ /// Returns the accumulated link unit summary.
+ ///
+ /// \returns A mutable reference to the linked output.
+ LUSummaryEncoding &getOutput() { return Output; }
+
private:
llvm::Expected<EntityId> resolve(const EntityName &OldName,
const EntityLinkage &Linkage);
diff --git a/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummary.h b/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummary.h
index 735e569cbd4af..4741272fb0f36 100644
--- a/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummary.h
+++ b/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummary.h
@@ -36,6 +36,7 @@ class SummaryViewBuilder;
class LUSummary {
friend class SerializationFormat;
friend class SummaryViewBuilder;
+ friend class TestFixture;
NestedBuildNamespace LUNamespace;
diff --git a/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummaryEncoding.h b/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummaryEncoding.h
index 196e2109471cc..18762a7ebc67d 100644
--- a/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummaryEncoding.h
+++ b/clang/include/clang/Analysis/Scalable/EntityLinker/LUSummaryEncoding.h
@@ -36,6 +36,7 @@ class SerializationFormat;
class LUSummaryEncoding {
friend class EntityLinker;
friend class SerializationFormat;
+ friend class TestFixture;
/// The namespace identifying this link unit.
NestedBuildNamespace LUNamespace;
diff --git a/clang/include/clang/Analysis/Scalable/EntityLinker/TUSummaryEncoding.h b/clang/include/clang/Analysis/Scalable/EntityLinker/TUSummaryEncoding.h
index b73a58b9d2984..5df8680867bf5 100644
--- a/clang/include/clang/Analysis/Scalable/EntityLinker/TUSummaryEncoding.h
+++ b/clang/include/clang/Analysis/Scalable/EntityLinker/TUSummaryEncoding.h
@@ -37,6 +37,7 @@ class SerializationFormat;
class TUSummaryEncoding {
friend class EntityLinker;
friend class SerializationFormat;
+ friend class TestFixture;
/// The namespace identifying this translation unit.
BuildNamespace TUNamespace;
diff --git a/clang/include/clang/Analysis/Scalable/Model/PrivateFieldNames.def b/clang/include/clang/Analysis/Scalable/Model/PrivateFieldNames.def
index f4e952c04ed2f..8ef57b9a1aa19 100644
--- a/clang/include/clang/Analysis/Scalable/Model/PrivateFieldNames.def
+++ b/clang/include/clang/Analysis/Scalable/Model/PrivateFieldNames.def
@@ -25,10 +25,22 @@ FIELD(EntityLinkage, Linkage)
FIELD(EntityName, Namespace)
FIELD(EntityName, Suffix)
FIELD(EntityName, USR)
+FIELD(LUSummary, Data)
+FIELD(LUSummary, IdTable)
+FIELD(LUSummary, LinkageTable)
+FIELD(LUSummary, LUNamespace)
+FIELD(LUSummaryEncoding, Data)
+FIELD(LUSummaryEncoding, IdTable)
+FIELD(LUSummaryEncoding, LinkageTable)
+FIELD(LUSummaryEncoding, LUNamespace)
FIELD(NestedBuildNamespace, Namespaces)
FIELD(TUSummary, Data)
FIELD(TUSummary, IdTable)
FIELD(TUSummary, LinkageTable)
FIELD(TUSummary, TUNamespace)
+FIELD(TUSummaryEncoding, Data)
+FIELD(TUSummaryEncoding, IdTable)
+FIELD(TUSummaryEncoding, LinkageTable)
+FIELD(TUSummaryEncoding, TUNamespace)
#undef FIELD
diff --git a/clang/include/clang/Analysis/Scalable/Serialization/SerializationFormat.h b/clang/include/clang/Analysis/Scalable/Serialization/SerializationFormat.h
index 5686a088c72d4..d136e588e0404 100644
--- a/clang/include/clang/Analysis/Scalable/Serialization/SerializationFormat.h
+++ b/clang/include/clang/Analysis/Scalable/Serialization/SerializationFormat.h
@@ -14,6 +14,10 @@
#ifndef CLANG_ANALYSIS_SCALABLE_SERIALIZATION_SERIALIZATION_FORMAT_H
#define CLANG_ANALYSIS_SCALABLE_SERIALIZATION_SERIALIZATION_FORMAT_H
+#include "clang/Analysis/Scalable/EntityLinker/EntityLinker.h"
+#include "clang/Analysis/Scalable/EntityLinker/LUSummary.h"
+#include "clang/Analysis/Scalable/EntityLinker/LUSummaryEncoding.h"
+#include "clang/Analysis/Scalable/EntityLinker/TUSummaryEncoding.h"
#include "clang/Analysis/Scalable/Model/BuildNamespace.h"
#include "clang/Analysis/Scalable/Model/SummaryName.h"
#include "clang/Analysis/Scalable/TUSummary/TUSummary.h"
@@ -24,10 +28,14 @@ namespace clang::ssaf {
class EntityId;
class EntityIdTable;
+class EntityLinker;
class EntityName;
class EntitySummary;
+class LUSummary;
+class LUSummaryEncoding;
class SummaryName;
class TUSummary;
+class TUSummaryEncoding;
/// Abstract base class for serialization formats.
class SerializationFormat {
diff --git a/clang/lib/Analysis/Scalable/EntityLinker/EntityLinker.cpp b/clang/lib/Analysis/Scalable/EntityLinker/EntityLinker.cpp
index b23c19f0936b2..65b8adf7642ae 100644
--- a/clang/lib/Analysis/Scalable/EntityLinker/EntityLinker.cpp
+++ b/clang/lib/Analysis/Scalable/EntityLinker/EntityLinker.cpp
@@ -75,12 +75,13 @@ llvm::Expected<EntityId> EntityLinker::resolve(const EntityName &OldName,
[[maybe_unused]] auto [It, Inserted] =
Output.LinkageTable.try_emplace(NewId, Linkage);
- if (!Inserted) {
- return ErrorBuilder::create(
- llvm::inconvertibleErrorCode(),
- ErrorMessages::EntityIdAlreadyExistsInLinkageTable, NewId.Index)
- .build();
- }
+ // if (!Inserted) {
+ // return ErrorBuilder::create(
+ // llvm::inconvertibleErrorCode(),
+ // ErrorMessages::EntityIdAlreadyExistsInLinkageTable,
+ // NewId.Index)
+ // .build();
+ // }
return NewId;
}
diff --git a/clang/unittests/Analysis/Scalable/CMakeLists.txt b/clang/unittests/Analysis/Scalable/CMakeLists.txt
index 5529ca06de170..c191abfc56eac 100644
--- a/clang/unittests/Analysis/Scalable/CMakeLists.txt
+++ b/clang/unittests/Analysis/Scalable/CMakeLists.txt
@@ -4,6 +4,7 @@ add_distinct_clang_unittest(ClangScalableAnalysisTests
EntityIdTest.cpp
EntityIdTableTest.cpp
EntityLinkageTest.cpp
+ EntityLinkerTest.cpp
EntityNameTest.cpp
Registries/FancyAnalysisData.cpp
Registries/MockSerializationFormat.cpp
diff --git a/clang/unittests/Analysis/Scalable/EntityLinkerTest.cpp b/clang/unittests/Analysis/Scalable/EntityLinkerTest.cpp
new file mode 100644
index 0000000000000..5ef5d5fd4d1ca
--- /dev/null
+++ b/clang/unittests/Analysis/Scalable/EntityLinkerTest.cpp
@@ -0,0 +1,713 @@
+//===- unittests/Analysis/Scalable/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/Analysis/Scalable/EntityLinker/EntityLinker.h"
+#include "TestFixture.h"
+#include "clang/Analysis/Scalable/EntityLinker/EntitySummaryEncoding.h"
+#include "clang/Analysis/Scalable/EntityLinker/LUSummaryEncoding.h"
+#include "clang/Analysis/Scalable/EntityLinker/TUSummaryEncoding.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/EntityName.h"
+#include "clang/Analysis/Scalable/Model/SummaryName.h"
+#include "llvm/Testing/Support/Error.h"
+#include "gtest/gtest.h"
+#include <memory>
+
+namespace clang::ssaf {
+
+namespace {
+
+// Mock EntitySummaryEncoding for testing
+class MockEntitySummaryEncoding : public EntitySummaryEncoding {
+public:
+ MockEntitySummaryEncoding() : Id(++Index) {}
+
+ size_t getId() const { return Id; }
+
+ void
+ patch(const std::map<EntityId, EntityId> &EntityResolutionTable) override {
+ PatchedIds = EntityResolutionTable;
+ }
+
+ 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:
+ // Helper to create a TUSummaryEncoding with entities
+ std::unique_ptr<TUSummaryEncoding>
+ createTUSummaryEncoding(BuildNamespaceKind Kind, llvm::StringRef Name) {
+ return std::make_unique<TUSummaryEncoding>(BuildNamespace(Kind, Name));
+ }
+
+ // Helper to add an entity to a TUSummaryEncoding
+ EntityId addEntity(TUSummaryEncoding &TU, llvm::StringRef USR,
+ EntityLinkage::LinkageType Linkage) {
+ EntityName Name(USR, "", NestedBuildNamespace(getTUNamespace(TU)));
+ EntityId Id = getIdTable(TU).getId(Name);
+ getLinkageTable(TU).insert({Id, EntityLinkage(Linkage)});
+ return Id;
+ }
+
+ // Helper to add summary data to a TUSummaryEncoding
+ 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;
+ }
+};
+
+TEST_F(EntityLinkerTest, NoLink) {
+ NestedBuildNamespace LUNamespace(
+ {BuildNamespace(BuildNamespaceKind::LinkUnit, "LU")});
+
+ EntityLinker Linker(LUNamespace);
+
+ const auto &Output = Linker.getOutput();
+ EXPECT_EQ(getIdTable(Output).count(), 0u);
+ EXPECT_EQ(getLinkageTable(Output).size(), 0u);
+ EXPECT_EQ(getData(Output).size(), 0u);
+}
+
+TEST_F(EntityLinkerTest, EmptyLink) {
+ 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 = Linker.getOutput();
+ EXPECT_EQ(getIdTable(Output).count(), 0u);
+ EXPECT_EQ(getLinkageTable(Output).size(), 0u);
+ EXPECT_EQ(getData(Output).size(), 0u);
+}
+
+TEST_F(EntityLinkerTest, NonEmptyLink) {
+ NestedBuildNamespace LUNamespace(
+ {BuildNamespace(BuildNamespaceKind::LinkUnit, "LU")});
+
+ EntityLinker Linker(LUNamespace);
+
+ auto TU = createTUSummaryEncoding(BuildNamespaceKind::CompilationUnit, "TU");
+
+ const auto EIdA = addEntity(*TU, "A", EntityLinkage::LinkageType::None);
+ const auto ESIdAS1 = addSummaryData(*TU, EIdA, "S1");
+ const auto ESIdAS2 = addSummaryData(*TU, EIdA, "S2");
+
+ const auto EIdB = addEntity(*TU, "B", EntityLinkage::LinkageType::Internal);
+ const auto ESIdBS1 = addSummaryData(*TU, EIdB, "S1");
+ const auto ESIdBS2 = addSummaryData(*TU, EIdB, "S2");
+
+ const auto EIdC = addEntity(*TU, "C", EntityLinkage::LinkageType::External);
+ const auto ESIdCS1 = addSummaryData(*TU, EIdC, "S1");
+
+ const auto EIdD = addEntity(*TU, "D", EntityLinkage::LinkageType::External);
+ const auto ESIdDS2 = addSummaryData(*TU, EIdD, "S2");
+
+ const BuildNamespace TUNamespace = getTUNamespace(*TU);
+
+ EXPECT_THAT_ERROR(Linker.link(std::move(TU)), llvm::Succeeded());
+
+ const auto &Output = Linker.getOutput();
+ const auto &IdTable = getIdTable(Output);
+ const auto &LinkageTable = getLinkageTable(Output);
+ const auto &Data = getData(Output);
+
+ // Construct the nested namespace with TU inside LU
+ std::vector<BuildNamespace> NamespaceVec;
+ NamespaceVec.push_back(TUNamespace);
+ for (const auto &NS : getNamespaces(LUNamespace)) {
+ NamespaceVec.push_back(NS);
+ }
+ NestedBuildNamespace LocalNamespace(NamespaceVec);
+
+ EntityName NameA("A", "", LocalNamespace);
+ EntityName NameB("B", "", LocalNamespace);
+ EntityName NameC("C", "", LUNamespace);
+ EntityName NameD("D", "", LUNamespace);
+
+ // EntityIDTable Tests.
+ {
+ const auto &Entities = getEntities(IdTable);
+
+ EXPECT_EQ(IdTable.count(), 4u);
+
+ EXPECT_TRUE(IdTable.contains(NameA));
+ EXPECT_EQ(Entities.at(NameA), EIdA);
+
+ EXPECT_TRUE(IdTable.contains(NameB));
+ EXPECT_EQ(Entities.at(NameB), EIdB);
+
+ EXPECT_TRUE(IdTable.contains(NameC));
+ EXPECT_EQ(Entities.at(NameC), EIdC);
+
+ EXPECT_TRUE(IdTable.contains(NameD));
+ EXPECT_EQ(Entities.at(NameD), EIdD);
+ }
+
+ // LinkageTable Tests.
+ {
+ EXPECT_EQ(LinkageTable.size(), 4u);
+
+ ASSERT_NE(LinkageTable.find(EIdA), LinkageTable.end());
+ EXPECT_EQ(getLinkage(LinkageTable.at(EIdA)),
+ EntityLinkage::LinkageType::None);
+
+ ASSERT_NE(LinkageTable.find(EIdB), LinkageTable.end());
+ EXPECT_EQ(LinkageTable.at(EIdB).getLinkage(),
+ EntityLinkage::LinkageType::Internal);
+
+ ASSERT_NE(LinkageTable.find(EIdC), LinkageTable.end());
+ EXPECT_EQ(LinkageTable.at(EIdC).getLinkage(),
+ EntityLinkage::LinkageType::External);
+
+ ASSERT_NE(LinkageTable.find(EIdD), LinkageTable.end());
+ EXPECT_EQ(LinkageTable.at(EIdD).getLinkage(),
+ EntityLinkage::LinkageType::External);
+ }
+
+ // Data Tests.
+ {
+ EXPECT_EQ(Data.size(), 2u);
+
+ std::map<EntityId, EntityId> ExpectedEntityResolutionMapping = {
+ {EIdA, EIdA}, {EIdB, EIdB}, {EIdC, EIdC}, {EIdD, EIdD}};
+
+ // S1 Tests.
+ {
+ SummaryName S1("S1");
+ ASSERT_NE(Data.find(S1), Data.end());
+
+ const auto &S1Data = Data.at(S1);
+ EXPECT_EQ(S1Data.size(), 3u);
+
+ EXPECT_NE(S1Data.find(EIdA), S1Data.end());
+ auto *MockA =
+ static_cast<MockEntitySummaryEncoding *>(S1Data.at(EIdA).get());
+ EXPECT_EQ(MockA->getId(), ESIdAS1);
+ EXPECT_EQ(MockA->getPatchedIds(), ExpectedEntityResolutionMapping);
+
+ EXPECT_NE(S1Data.find(EIdB), S1Data.end());
+ auto *MockB =
+ static_cast<MockEntitySummaryEncoding *>(S1Data.at(EIdB).get());
+ EXPECT_EQ(MockB->getId(), ESIdBS1);
+ EXPECT_EQ(MockB->getPatchedIds(), ExpectedEntityResolutionMapping);
+
+ EXPECT_NE(S1Data.find(EIdC), S1Data.end());
+ auto *MockC =
+ static_cast<MockEntitySummaryEncoding *>(S1Data.at(EIdC).get());
+ EXPECT_EQ(MockC->getId(), ESIdCS1);
+ EXPECT_EQ(MockC->getPatchedIds(), ExpectedEntityResolutionMapping);
+ }
+
+ // S2 Tests.
+ {
+ SummaryName S2("S2");
+ ASSERT_NE(Data.find(S2), Data.end());
+
+ const auto &S2Data = Data.at(S2);
+ EXPECT_EQ(S2Data.size(), 3u);
+
+ EXPECT_NE(S2Data.find(EIdA), S2Data.end());
+ auto *MockA =
+ static_cast<MockEntitySummaryEncoding *>(S2Data.at(EIdA).get());
+ EXPECT_EQ(MockA->getId(), ESIdAS2);
+ EXPECT_EQ(MockA->getPatchedIds(), ExpectedEntityResolutionMapping);
+
+ EXPECT_NE(S2Data.find(EIdB), S2Data.end());
+ auto *MockB =
+ static_cast<MockEntitySummaryEncoding *>(S2Data.at(EIdB).get());
+ EXPECT_EQ(MockB->getId(), ESIdBS2);
+ EXPECT_EQ(MockB->getPatchedIds(), ExpectedEntityResolutionMapping);
+
+ EXPECT_NE(S2Data.find(EIdD), S2Data.end());
+ auto *MockD =
+ static_cast<MockEntitySummaryEncoding *>(S2Data.at(EIdD).get());
+ EXPECT_EQ(MockD->getId(), ESIdDS2);
+ EXPECT_EQ(MockD->getPatchedIds(), ExpectedEntityResolutionMapping);
+ }
+ }
+}
+
+TEST_F(EntityLinkerTest, TwoTULinkWithAllCombinations) {
+ NestedBuildNamespace LUNamespace(
+ {BuildNamespace(BuildNamespaceKind::LinkUnit, "LU")});
+
+ EntityLinker Linker(LUNamespace);
+
+ // Create TU1 with entities covering all linkage types and summary
+ // distributions
+ auto TU1 =
+ createTUSummaryEncoding(BuildNamespaceKind::CompilationUnit, "TU1");
+
+ // None linkage entities in TU1
+ const auto EIdTU1_X_None =
+ addEntity(*TU1, "X", EntityLinkage::LinkageType::None);
+ const auto ESIdTU1_X_S1 = addSummaryData(*TU1, EIdTU1_X_None, "S1");
+
+ const auto EIdTU1_Y_None =
+ addEntity(*TU1, "Y", EntityLinkage::LinkageType::None);
+ const auto ESIdTU1_Y_S2 = addSummaryData(*TU1, EIdTU1_Y_None, "S2");
+
+ const auto EIdTU1_Z_None =
+ addEntity(*TU1, "Z", EntityLinkage::LinkageType::None);
+ const auto ESIdTU1_Z_S1 = addSummaryData(*TU1, EIdTU1_Z_None, "S1");
+ const auto ESIdTU1_Z_S2 = addSummaryData(*TU1, EIdTU1_Z_None, "S2");
+
+ // Internal linkage entities in TU1
+ const auto EIdTU1_A_Internal =
+ addEntity(*TU1, "A", EntityLinkage::LinkageType::Internal);
+ const auto ESIdTU1_A_S1 = addSummaryData(*TU1, EIdTU1_A_Internal, "S1");
+
+ const auto EIdTU1_B_Internal =
+ addEntity(*TU1, "B", EntityLinkage::LinkageType::Internal);
+ const auto ESIdTU1_B_S2 = addSummaryData(*TU1, EIdTU1_B_Internal, "S2");
+
+ const auto EIdTU1_C_Internal =
+ addEntity(*TU1, "C", EntityLinkage::LinkageType::Internal);
+ const auto ESIdTU1_C_S1 = addSummaryData(*TU1, EIdTU1_C_Internal, "S1");
+ const auto ESIdTU1_C_S2 = addSummaryData(*TU1, EIdTU1_C_Internal, "S2");
+
+ // External linkage entities in TU1
+ const auto EIdTU1_P_External =
+ addEntity(*TU1, "P", EntityLinkage::LinkageType::External);
+ const auto ESIdTU1_P_S1 = addSummaryData(*TU1, EIdTU1_P_External, "S1");
+
+ const auto EIdTU1_Q_External =
+ addEntity(*TU1, "Q", EntityLinkage::LinkageType::External);
+ const auto ESIdTU1_Q_S2 = addSummaryData(*TU1, EIdTU1_Q_External, "S2");
+
+ const auto EIdTU1_R_External =
+ addEntity(*TU1, "R", EntityLinkage::LinkageType::External);
+ const auto ESIdTU1_R_S1 = addSummaryData(*TU1, EIdTU1_R_External, "S1");
+ const auto ESIdTU1_R_S2 = addSummaryData(*TU1, EIdTU1_R_External, "S2");
+
+ const BuildNamespace TU1Namespace = getTUNamespace(*TU1);
+
+ // Link TU1
+ EXPECT_THAT_ERROR(Linker.link(std::move(TU1)), llvm::Succeeded());
+
+ // Create TU2 with entities covering all combinations including duplicates
+ auto TU2 =
+ createTUSummaryEncoding(BuildNamespaceKind::CompilationUnit, "TU2");
+
+ // None linkage entities in TU2 - includes duplicates and unique
+ const auto EIdTU2_X_None =
+ addEntity(*TU2, "X", EntityLinkage::LinkageType::None);
+ const auto ESIdTU2_X_S2 = addSummaryData(*TU2, EIdTU2_X_None, "S2");
+
+ const auto EIdTU2_Y_None =
+ addEntity(*TU2, "Y", EntityLinkage::LinkageType::None);
+ const auto ESIdTU2_Y_S1 = addSummaryData(*TU2, EIdTU2_Y_None, "S1");
+
+ const auto EIdTU2_W_None =
+ addEntity(*TU2, "W", EntityLinkage::LinkageType::None);
+ const auto ESIdTU2_W_S1 = addSummaryData(*TU2, EIdTU2_W_None, "S1");
+ const auto ESIdTU2_W_S2 = addSummaryData(*TU2, EIdTU2_W_None, "S2");
+
+ // Internal linkage entities in TU2 - includes duplicates and unique
+ const auto EIdTU2_A_Internal =
+ addEntity(*TU2, "A", EntityLinkage::LinkageType::Internal);
+ const auto ESIdTU2_A_S2 = addSummaryData(*TU2, EIdTU2_A_Internal, "S2");
+
+ const auto EIdTU2_B_Internal =
+ addEntity(*TU2, "B", EntityLinkage::LinkageType::Internal);
+ const auto ESIdTU2_B_S1 = addSummaryData(*TU2, EIdTU2_B_Internal, "S1");
+
+ const auto EIdTU2_D_Internal =
+ addEntity(*TU2, "D", EntityLinkage::LinkageType::Internal);
+ const auto ESIdTU2_D_S1 = addSummaryData(*TU2, EIdTU2_D_Internal, "S1");
+ const auto ESIdTU2_D_S2 = addSummaryData(*TU2, EIdTU2_D_Internal, "S2");
+
+ // External linkage entities in TU2 - includes duplicates (will be dropped)
+ // and unique
+ const auto EIdTU2_P_External =
+ addEntity(*TU2, "P", EntityLinkage::LinkageType::External);
+ const auto ESIdTU2_P_S2 = addSummaryData(*TU2, EIdTU2_P_External, "S2");
+
+ const auto EIdTU2_Q_External =
+ addEntity(*TU2, "Q", EntityLinkage::LinkageType::External);
+ const auto ESIdTU2_Q_S1 = addSummaryData(*TU2, EIdTU2_Q_External, "S1");
+
+ const auto EIdTU2_S_External =
+ addEntity(*TU2, "S", EntityLinkage::LinkageType::External);
+ const auto ESIdTU2_S_S1 = addSummaryData(*TU2, EIdTU2_S_External, "S1");
+ const auto ESIdTU2_S_S2 = addSummaryData(*TU2, EIdTU2_S_External, "S2");
+
+ const BuildNamespace TU2Namespace = getTUNamespace(*TU2);
+
+ // Link TU2
+ EXPECT_THAT_ERROR(Linker.link(std::move(TU2)), llvm::Succeeded());
+
+ // Verify the output
+ const auto &Output = Linker.getOutput();
+ const auto &IdTable = getIdTable(Output);
+ const auto &LinkageTable = getLinkageTable(Output);
+ const auto &Data = getData(Output);
+
+ // Construct the nested namespaces
+ std::vector<BuildNamespace> TU1NamespaceVec;
+ TU1NamespaceVec.push_back(TU1Namespace);
+ for (const auto &NS : getNamespaces(LUNamespace)) {
+ TU1NamespaceVec.push_back(NS);
+ }
+ NestedBuildNamespace TU1LocalNamespace(TU1NamespaceVec);
+
+ std::vector<BuildNamespace> TU2NamespaceVec;
+ TU2NamespaceVec.push_back(TU2Namespace);
+ for (const auto &NS : getNamespaces(LUNamespace)) {
+ TU2NamespaceVec.push_back(NS);
+ }
+ NestedBuildNamespace TU2LocalNamespace(TU2NamespaceVec);
+
+ // Create expected entity names
+ // None linkage entities use local namespace (TU scoped)
+ EntityName NameTU1_X_None("X", "", TU1LocalNamespace);
+ EntityName NameTU1_Y_None("Y", "", TU1LocalNamespace);
+ EntityName NameTU1_Z_None("Z", "", TU1LocalNamespace);
+ EntityName NameTU2_X_None("X", "", TU2LocalNamespace);
+ EntityName NameTU2_Y_None("Y", "", TU2LocalNamespace);
+ EntityName NameTU2_W_None("W", "", TU2LocalNamespace);
+
+ // Internal linkage entities use local namespace (TU scoped)
+ EntityName NameTU1_A_Internal("A", "", TU1LocalNamespace);
+ EntityName NameTU1_B_Internal("B", "", TU1LocalNamespace);
+ EntityName NameTU1_C_Internal("C", "", TU1LocalNamespace);
+ EntityName NameTU2_A_Internal("A", "", TU2LocalNamespace);
+ EntityName NameTU2_B_Internal("B", "", TU2LocalNamespace);
+ EntityName NameTU2_D_Internal("D", "", TU2LocalNamespace);
+
+ // External linkage entities use LU namespace (shared across TUs)
+ EntityName NameP_External("P", "", LUNamespace);
+ EntityName NameQ_External("Q", "", LUNamespace);
+ EntityName NameR_External("R", "", LUNamespace);
+ EntityName NameS_External("S", "", LUNamespace);
+
+ // EntityIdTable Tests
+ {
+ const auto &Entities = getEntities(IdTable);
+
+ // Should have 6 None + 6 Internal + 4 External = 16 entities total
+ EXPECT_EQ(IdTable.count(), 16u);
+
+ // TU1 None linkage entities
+ EXPECT_TRUE(IdTable.contains(NameTU1_X_None));
+ ASSERT_EQ(Entities.at(NameTU1_X_None), EIdTU1_X_None);
+
+ EXPECT_TRUE(IdTable.contains(NameTU1_Y_None));
+ EXPECT_EQ(Entities.at(NameTU1_Y_None), EIdTU1_Y_None);
+
+ EXPECT_TRUE(IdTable.contains(NameTU1_Z_None));
+ EXPECT_EQ(Entities.at(NameTU1_Z_None), EIdTU1_Z_None);
+
+ // TU2 None linkage entities (different from TU1 due to namespace)
+ EXPECT_TRUE(IdTable.contains(NameTU2_X_None));
+ EXPECT_EQ(Entities.at(NameTU2_X_None), EIdTU2_X_None);
+
+ EXPECT_TRUE(IdTable.contains(NameTU2_Y_None));
+ EXPECT_EQ(Entities.at(NameTU2_Y_None), EIdTU2_Y_None);
+
+ EXPECT_TRUE(IdTable.contains(NameTU2_W_None));
+ EXPECT_EQ(Entities.at(NameTU2_W_None), EIdTU2_W_None);
+
+ // TU1 Internal linkage entities
+ EXPECT_TRUE(IdTable.contains(NameTU1_A_Internal));
+ EXPECT_EQ(Entities.at(NameTU1_A_Internal), EIdTU1_A_Internal);
+
+ EXPECT_TRUE(IdTable.contains(NameTU1_B_Internal));
+ EXPECT_EQ(Entities.at(NameTU1_B_Internal), EIdTU1_B_Internal);
+
+ EXPECT_TRUE(IdTable.contains(NameTU1_C_Internal));
+ EXPECT_EQ(Entities.at(NameTU1_C_Internal), EIdTU1_C_Internal);
+
+ // TU2 Internal linkage entities (different from TU1 due to namespace)
+ EXPECT_TRUE(IdTable.contains(NameTU2_A_Internal));
+ EXPECT_EQ(Entities.at(NameTU2_A_Internal), EIdTU2_A_Internal);
+
+ EXPECT_TRUE(IdTable.contains(NameTU2_B_Internal));
+ EXPECT_EQ(Entities.at(NameTU2_B_Internal), EIdTU2_B_Internal);
+
+ EXPECT_TRUE(IdTable.contains(NameTU2_D_Internal));
+ EXPECT_EQ(Entities.at(NameTU2_D_Internal), EIdTU2_D_Internal);
+
+ // External linkage entities (shared across TUs)
+ EXPECT_TRUE(IdTable.contains(NameP_External));
+ EXPECT_EQ(Entities.at(NameP_External), EIdTU1_P_External);
+
+ EXPECT_TRUE(IdTable.contains(NameQ_External));
+ EXPECT_EQ(Entities.at(NameQ_External), EIdTU1_Q_External);
+
+ EXPECT_TRUE(IdTable.contains(NameR_External));
+ EXPECT_EQ(Entities.at(NameR_External), EIdTU1_R_External);
+
+ EXPECT_TRUE(IdTable.contains(NameS_External));
+ EXPECT_EQ(Entities.at(NameS_External), EIdTU2_S_External);
+ }
+
+ // LinkageTable Tests
+ {
+ EXPECT_EQ(LinkageTable.size(), 16u);
+
+ // Verify None linkage entities
+ EXPECT_EQ(LinkageTable.at(EIdTU1_X_None).getLinkage(),
+ EntityLinkage::LinkageType::None);
+ EXPECT_EQ(LinkageTable.at(EIdTU1_Y_None).getLinkage(),
+ EntityLinkage::LinkageType::None);
+ EXPECT_EQ(LinkageTable.at(EIdTU1_Z_None).getLinkage(),
+ EntityLinkage::LinkageType::None);
+ EXPECT_EQ(LinkageTable.at(EIdTU2_X_None).getLinkage(),
+ EntityLinkage::LinkageType::None);
+ EXPECT_EQ(LinkageTable.at(EIdTU2_Y_None).getLinkage(),
+ EntityLinkage::LinkageType::None);
+ EXPECT_EQ(LinkageTable.at(EIdTU2_W_None).getLinkage(),
+ EntityLinkage::LinkageType::None);
+
+ // Verify Internal linkage entities
+ EXPECT_EQ(LinkageTable.at(EIdTU1_A_Internal).getLinkage(),
+ EntityLinkage::LinkageType::Internal);
+ EXPECT_EQ(LinkageTable.at(EIdTU1_B_Internal).getLinkage(),
+ EntityLinkage::LinkageType::Internal);
+ EXPECT_EQ(LinkageTable.at(EIdTU1_C_Internal).getLinkage(),
+ EntityLinkage::LinkageType::Internal);
+ EXPECT_EQ(LinkageTable.at(EIdTU2_A_Internal).getLinkage(),
+ EntityLinkage::LinkageType::Internal);
+ EXPECT_EQ(LinkageTable.at(EIdTU2_B_Internal).getLinkage(),
+ EntityLinkage::LinkageType::Internal);
+ EXPECT_EQ(LinkageTable.at(EIdTU2_D_Internal).getLinkage(),
+ EntityLinkage::LinkageType::Internal);
+
+ // Verify External linkage entities
+ EXPECT_EQ(LinkageTable.at(EIdTU1_P_External).getLinkage(),
+ EntityLinkage::LinkageType::External);
+ EXPECT_EQ(LinkageTable.at(EIdTU1_Q_External).getLinkage(),
+ EntityLinkage::LinkageType::External);
+ EXPECT_EQ(LinkageTable.at(EIdTU1_R_External).getLinkage(),
+ EntityLinkage::LinkageType::External);
+ EXPECT_EQ(LinkageTable.at(EIdTU2_S_External).getLinkage(),
+ EntityLinkage::LinkageType::External);
+ }
+
+ // Data Tests
+ {
+ EXPECT_EQ(Data.size(), 2u);
+
+ // Build entity resolution mappings for each TU
+ std::map<EntityId, EntityId> TU1EntityResolutionMapping = {
+ {EIdTU1_X_None, EIdTU1_X_None},
+ {EIdTU1_Y_None, EIdTU1_Y_None},
+ {EIdTU1_Z_None, EIdTU1_Z_None},
+ {EIdTU1_A_Internal, EIdTU1_A_Internal},
+ {EIdTU1_B_Internal, EIdTU1_B_Internal},
+ {EIdTU1_C_Internal, EIdTU1_C_Internal},
+ {EIdTU1_P_External, EIdTU1_P_External},
+ {EIdTU1_Q_External, EIdTU1_Q_External},
+ {EIdTU1_R_External, EIdTU1_R_External}};
+
+ std::map<EntityId, EntityId> TU2EntityResolutionMapping = {
+ {EIdTU2_X_None, EIdTU2_X_None},
+ {EIdTU2_Y_None, EIdTU2_Y_None},
+ {EIdTU2_W_None, EIdTU2_W_None},
+ {EIdTU2_A_Internal, EIdTU2_A_Internal},
+ {EIdTU2_B_Internal, EIdTU2_B_Internal},
+ {EIdTU2_D_Internal, EIdTU2_D_Internal},
+ // External linkage entities from TU2 resolve to TU1's IDs if duplicate
+ {EIdTU2_P_External, EIdTU1_P_External},
+ {EIdTU2_Q_External, EIdTU1_Q_External},
+ {EIdTU2_S_External, EIdTU2_S_External}};
+
+ // S1 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,S) = 11 entities
+ // Note: TU2's P and Q external entities are dropped because TU1 already
+ // has them
+ EXPECT_EQ(S1Data.size(), 11u);
+
+ // Verify TU1 entities in S1
+ EXPECT_NE(S1Data.find(EIdTU1_X_None), S1Data.end());
+ auto *MockTU1_X = static_cast<MockEntitySummaryEncoding *>(
+ S1Data.at(EIdTU1_X_None).get());
+ EXPECT_EQ(MockTU1_X->getId(), ESIdTU1_X_S1);
+ EXPECT_EQ(MockTU1_X->getPatchedIds(), TU1EntityResolutionMapping);
+
+ EXPECT_NE(S1Data.find(EIdTU1_Z_None), S1Data.end());
+ auto *MockTU1_Z = static_cast<MockEntitySummaryEncoding *>(
+ S1Data.at(EIdTU1_Z_None).get());
+ EXPECT_EQ(MockTU1_Z->getId(), ESIdTU1_Z_S1);
+ EXPECT_EQ(MockTU1_Z->getPatchedIds(), TU1EntityResolutionMapping);
+
+ EXPECT_NE(S1Data.find(EIdTU1_A_Internal), S1Data.end());
+ auto *MockTU1_A = static_cast<MockEntitySummaryEncoding *>(
+ S1Data.at(EIdTU1_A_Internal).get());
+ EXPECT_EQ(MockTU1_A->getId(), ESIdTU1_A_S1);
+ EXPECT_EQ(MockTU1_A->getPatchedIds(), TU1EntityResolutionMapping);
+
+ EXPECT_NE(S1Data.find(EIdTU1_C_Internal), S1Data.end());
+ auto *MockTU1_C = static_cast<MockEntitySummaryEncoding *>(
+ S1Data.at(EIdTU1_C_Internal).get());
+ EXPECT_EQ(MockTU1_C->getId(), ESIdTU1_C_S1);
+ EXPECT_EQ(MockTU1_C->getPatchedIds(), TU1EntityResolutionMapping);
+
+ EXPECT_NE(S1Data.find(EIdTU1_P_External), S1Data.end());
+ auto *MockTU1_P = static_cast<MockEntitySummaryEncoding *>(
+ S1Data.at(EIdTU1_P_External).get());
+ EXPECT_EQ(MockTU1_P->getId(), ESIdTU1_P_S1);
+ EXPECT_EQ(MockTU1_P->getPatchedIds(), TU1EntityResolutionMapping);
+
+ EXPECT_NE(S1Data.find(EIdTU1_R_External), S1Data.end());
+ auto *MockTU1_R = static_cast<MockEntitySummaryEncoding *>(
+ S1Data.at(EIdTU1_R_External).get());
+ EXPECT_EQ(MockTU1_R->getId(), ESIdTU1_R_S1);
+ EXPECT_EQ(MockTU1_R->getPatchedIds(), TU1EntityResolutionMapping);
+
+ // Verify TU2 entities in S1
+ EXPECT_NE(S1Data.find(EIdTU2_Y_None), S1Data.end());
+ auto *MockTU2_Y = static_cast<MockEntitySummaryEncoding *>(
+ S1Data.at(EIdTU2_Y_None).get());
+ EXPECT_EQ(MockTU2_Y->getId(), ESIdTU2_Y_S1);
+ EXPECT_EQ(MockTU2_Y->getPatchedIds(), TU2EntityResolutionMapping);
+
+ EXPECT_NE(S1Data.find(EIdTU2_W_None), S1Data.end());
+ auto *MockTU2_W = static_cast<MockEntitySummaryEncoding *>(
+ S1Data.at(EIdTU2_W_None).get());
+ EXPECT_EQ(MockTU2_W->getId(), ESIdTU2_W_S1);
+ EXPECT_EQ(MockTU2_W->getPatchedIds(), TU2EntityResolutionMapping);
+
+ EXPECT_NE(S1Data.find(EIdTU2_B_Internal), S1Data.end());
+ auto *MockTU2_B = static_cast<MockEntitySummaryEncoding *>(
+ S1Data.at(EIdTU2_B_Internal).get());
+ EXPECT_EQ(MockTU2_B->getId(), ESIdTU2_B_S1);
+ EXPECT_EQ(MockTU2_B->getPatchedIds(), TU2EntityResolutionMapping);
+
+ EXPECT_NE(S1Data.find(EIdTU2_D_Internal), S1Data.end());
+ auto *MockTU2_D = static_cast<MockEntitySummaryEncoding *>(
+ S1Data.at(EIdTU2_D_Internal).get());
+ EXPECT_EQ(MockTU2_D->getId(), ESIdTU2_D_S1);
+ EXPECT_EQ(MockTU2_D->getPatchedIds(), TU2EntityResolutionMapping);
+
+ EXPECT_NE(S1Data.find(EIdTU2_S_External), S1Data.end());
+ auto *MockTU2_S = static_cast<MockEntitySummaryEncoding *>(
+ S1Data.at(EIdTU2_S_External).get());
+ EXPECT_EQ(MockTU2_S->getId(), ESIdTU2_S_S1);
+ EXPECT_EQ(MockTU2_S->getPatchedIds(), TU2EntityResolutionMapping);
+
+ // Verify TU2's duplicate external entities are NOT in S1
+ EXPECT_EQ(S1Data.find(EIdTU2_Q_External), S1Data.end());
+ }
+
+ // S2 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,S) = 11 entities
+ // Note: TU2's P and Q external entities are dropped because TU1 already
+ // has them
+ EXPECT_EQ(S2Data.size(), 11u);
+
+ // Verify TU1 entities in S2
+ EXPECT_NE(S2Data.find(EIdTU1_Y_None), S2Data.end());
+ auto *MockTU1_Y = static_cast<MockEntitySummaryEncoding *>(
+ S2Data.at(EIdTU1_Y_None).get());
+ EXPECT_EQ(MockTU1_Y->getId(), ESIdTU1_Y_S2);
+ EXPECT_EQ(MockTU1_Y->getPatchedIds(), TU1EntityResolutionMapping);
+
+ EXPECT_NE(S2Data.find(EIdTU1_Z_None), S2Data.end());
+ auto *MockTU1_Z = static_cast<MockEntitySummaryEncoding *>(
+ S2Data.at(EIdTU1_Z_None).get());
+ EXPECT_EQ(MockTU1_Z->getId(), ESIdTU1_Z_S2);
+ EXPECT_EQ(MockTU1_Z->getPatchedIds(), TU1EntityResolutionMapping);
+
+ EXPECT_NE(S2Data.find(EIdTU1_B_Internal), S2Data.end());
+ auto *MockTU1_B = static_cast<MockEntitySummaryEncoding *>(
+ S2Data.at(EIdTU1_B_Internal).get());
+ EXPECT_EQ(MockTU1_B->getId(), ESIdTU1_B_S2);
+ EXPECT_EQ(MockTU1_B->getPatchedIds(), TU1EntityResolutionMapping);
+
+ EXPECT_NE(S2Data.find(EIdTU1_C_Internal), S2Data.end());
+ auto *MockTU1_C = static_cast<MockEntitySummaryEncoding *>(
+ S2Data.at(EIdTU1_C_Internal).get());
+ EXPECT_EQ(MockTU1_C->getId(), ESIdTU1_C_S2);
+ EXPECT_EQ(MockTU1_C->getPatchedIds(), TU1EntityResolutionMapping);
+
+ EXPECT_NE(S2Data.find(EIdTU1_Q_External), S2Data.end());
+ auto *MockTU1_Q = static_cast<MockEntitySummaryEncoding *>(
+ S2Data.at(EIdTU1_Q_External).get());
+ EXPECT_EQ(MockTU1_Q->getId(), ESIdTU1_Q_S2);
+ EXPECT_EQ(MockTU1_Q->getPatchedIds(), TU1EntityResolutionMapping);
+
+ EXPECT_NE(S2Data.find(EIdTU1_R_External), S2Data.end());
+ auto *MockTU1_R = static_cast<MockEntitySummaryEncoding *>(
+ S2Data.at(EIdTU1_R_External).get());
+ EXPECT_EQ(MockTU1_R->getId(), ESIdTU1_R_S2);
+ EXPECT_EQ(MockTU1_R->getPatchedIds(), TU1EntityResolutionMapping);
+
+ // Verify TU2 entities in S2
+ EXPECT_NE(S2Data.find(EIdTU2_X_None), S2Data.end());
+ auto *MockTU2_X = static_cast<MockEntitySummaryEncoding *>(
+ S2Data.at(EIdTU2_X_None).get());
+ EXPECT_EQ(MockTU2_X->getId(), ESIdTU2_X_S2);
+ EXPECT_EQ(MockTU2_X->getPatchedIds(), TU2EntityResolutionMapping);
+
+ EXPECT_NE(S2Data.find(EIdTU2_W_None), S2Data.end());
+ auto *MockTU2_W = static_cast<MockEntitySummaryEncoding *>(
+ S2Data.at(EIdTU2_W_None).get());
+ EXPECT_EQ(MockTU2_W->getId(), ESIdTU2_W_S2);
+ EXPECT_EQ(MockTU2_W->getPatchedIds(), TU2EntityResolutionMapping);
+
+ EXPECT_NE(S2Data.find(EIdTU2_A_Internal), S2Data.end());
+ auto *MockTU2_A = static_cast<MockEntitySummaryEncoding *>(
+ S2Data.at(EIdTU2_A_Internal).get());
+ EXPECT_EQ(MockTU2_A->getId(), ESIdTU2_A_S2);
+ EXPECT_EQ(MockTU2_A->getPatchedIds(), TU2EntityResolutionMapping);
+
+ EXPECT_NE(S2Data.find(EIdTU2_D_Internal), S2Data.end());
+ auto *MockTU2_D = static_cast<MockEntitySummaryEncoding *>(
+ S2Data.at(EIdTU2_D_Internal).get());
+ EXPECT_EQ(MockTU2_D->getId(), ESIdTU2_D_S2);
+ EXPECT_EQ(MockTU2_D->getPatchedIds(), TU2EntityResolutionMapping);
+
+ EXPECT_NE(S2Data.find(EIdTU2_S_External), S2Data.end());
+ auto *MockTU2_S = static_cast<MockEntitySummaryEncoding *>(
+ S2Data.at(EIdTU2_S_External).get());
+ EXPECT_EQ(MockTU2_S->getId(), ESIdTU2_S_S2);
+ EXPECT_EQ(MockTU2_S->getPatchedIds(), TU2EntityResolutionMapping);
+
+ // Verify TU2's duplicate external entities are NOT in S2
+ EXPECT_EQ(S2Data.find(EIdTU2_P_External), S2Data.end());
+ }
+ }
+}
+
+} // namespace
+
+} // namespace clang::ssaf
diff --git a/clang/unittests/Analysis/Scalable/TestFixture.h b/clang/unittests/Analysis/Scalable/TestFixture.h
index 427769819a703..c8936fd2cfb53 100644
--- a/clang/unittests/Analysis/Scalable/TestFixture.h
+++ b/clang/unittests/Analysis/Scalable/TestFixture.h
@@ -9,6 +9,10 @@
#ifndef LLVM_CLANG_UNITTESTS_ANALYSIS_SCALABLE_TESTFIXTURE_H
#define LLVM_CLANG_UNITTESTS_ANALYSIS_SCALABLE_TESTFIXTURE_H
+#include "clang/Analysis/Scalable/EntityLinker/EntityLinker.h"
+#include "clang/Analysis/Scalable/EntityLinker/LUSummary.h"
+#include "clang/Analysis/Scalable/EntityLinker/LUSummaryEncoding.h"
+#include "clang/Analysis/Scalable/EntityLinker/TUSummaryEncoding.h"
#include "clang/Analysis/Scalable/Model/BuildNamespace.h"
#include "clang/Analysis/Scalable/Model/EntityId.h"
#include "clang/Analysis/Scalable/Model/EntityIdTable.h"
>From 40a3657716c371b88604f68ce149d93dc45a9f69 Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Tue, 17 Feb 2026 20:54:03 -0800
Subject: [PATCH 5/6] Add tests for ErrorBuilder
---
.../Analysis/Scalable/Support/ErrorBuilder.h | 108 +++++++++-
.../Scalable/Support/ErrorBuilder.cpp | 23 ++-
.../Analysis/Scalable/CMakeLists.txt | 1 +
.../Analysis/Scalable/ErrorBuilderTest.cpp | 187 ++++++++++++++++++
4 files changed, 299 insertions(+), 20 deletions(-)
create mode 100644 clang/unittests/Analysis/Scalable/ErrorBuilderTest.cpp
diff --git a/clang/include/clang/Analysis/Scalable/Support/ErrorBuilder.h b/clang/include/clang/Analysis/Scalable/Support/ErrorBuilder.h
index 4ccda48e2391a..b0849269edd41 100644
--- a/clang/include/clang/Analysis/Scalable/Support/ErrorBuilder.h
+++ b/clang/include/clang/Analysis/Scalable/Support/ErrorBuilder.h
@@ -25,7 +25,7 @@ namespace clang::ssaf {
/// Fluent API for constructing contextual errors.
///
/// ErrorBuilder allows building error messages with layered context
-/// information. Context is added from innermost to outermost, and the final
+/// information. Context is added innermost to outermost, and the final
/// error message presents the context in reverse order (outermost first).
///
/// Example usage:
@@ -39,19 +39,35 @@ class ErrorBuilder {
std::error_code Code;
std::vector<std::string> ContextStack;
- // Private constructor - only accessible via static factories.
explicit ErrorBuilder(std::error_code EC) : Code(EC) {}
- // Helper: Format message and add to context stack.
+ void pushContext(std::string Msg) {
+ if (!Msg.empty()) {
+ ContextStack.push_back(std::move(Msg));
+ }
+ }
+
template <typename... Args>
void addFormattedContext(const char *Fmt, Args &&...ArgVals) {
std::string Message =
llvm::formatv(Fmt, std::forward<Args>(ArgVals)...).str();
- ContextStack.push_back(std::move(Message));
+ pushContext(std::move(Message));
}
public:
- // Static factory: Create new error from error code and formatted message.
+ /// Create an ErrorBuilder with an error code and formatted message.
+ ///
+ /// \param EC The error code for this error.
+ /// \param Fmt Format string for the error message (using llvm::formatv).
+ /// \param ArgVals Arguments for the format string.
+ /// \returns A new ErrorBuilder with the initial error message.
+ ///
+ /// Example:
+ /// \code
+ /// return ErrorBuilder::create(std::errc::invalid_argument,
+ /// "invalid value: {0}", 42)
+ /// .build();
+ /// \endcode
template <typename... Args>
static ErrorBuilder create(std::error_code EC, const char *Fmt,
Args &&...ArgVals) {
@@ -60,27 +76,99 @@ class ErrorBuilder {
return Builder;
}
- // Convenience overload for std::errc.
+ /// Convenience overload that accepts std::errc instead of std::error_code.
+ ///
+ /// \param EC The error condition for this error.
+ /// \param Fmt Format string for the error message.
+ /// \param ArgVals Arguments for the format string.
+ /// \returns A new ErrorBuilder with the initial error message.
template <typename... Args>
static ErrorBuilder create(std::errc EC, const char *Fmt, Args &&...ArgVals) {
return create(std::make_error_code(EC), Fmt,
std::forward<Args>(ArgVals)...);
}
- // Static factory: Wrap existing error and optionally add context.
+ /// Wrap an existing error and optionally add context.
+ ///
+ /// Extracts the error code and message(s) from the given error. If multiple
+ /// errors are joined (via llvm::joinErrors), their messages are combined
+ /// using " + " separator.
+ ///
+ /// \param E The error to wrap. Must be a failure (cannot be success).
+ /// \returns A new ErrorBuilder containing the wrapped error information.
+ ///
+ /// \pre E must evaluate to true (i.e., must be a failure). Wrapping
+ /// Error::success() is a programming error and will trigger an
+ /// assertion failure in debug builds.
+ ///
+ /// Example:
+ /// \code
+ /// if (auto Err = foo())
+ /// return ErrorBuilder::wrap(std::move(Err))
+ /// .context("while processing file")
+ /// .build();
+ /// \endcode
static ErrorBuilder wrap(llvm::Error E);
- // Add context (plain string).
+ /// Add context information as a plain string.
+ ///
+ /// Empty strings are ignored and not added to the context stack.
+ ///
+ /// \param Msg Context message to add. Must be a null-terminated string.
+ /// \returns Reference to this ErrorBuilder for method chaining.
+ ///
+ /// Example:
+ /// \code
+ /// return ErrorBuilder::create(...)
+ /// .context("reading configuration file")
+ /// .build();
+ /// \endcode
ErrorBuilder &context(const char *Msg);
- // Add context (formatted string).
+ /// Add context information with formatted string.
+ ///
+ /// Uses llvm::formatv for formatting. Empty messages (after formatting)
+ /// are ignored and not added to the context stack.
+ ///
+ /// \param Fmt Format string (using llvm::formatv syntax).
+ /// \param ArgVals Arguments for the format string.
+ /// \returns Reference to this ErrorBuilder for method chaining.
+ ///
+ /// Example:
+ /// \code
+ /// return ErrorBuilder::create(...)
+ /// .context("processing field '{0}'", fieldName)
+ /// .context("at line {0}, column {1}", line, col)
+ /// .build();
+ /// \endcode
template <typename... Args>
ErrorBuilder &context(const char *Fmt, Args &&...ArgVals) {
addFormattedContext(Fmt, std::forward<Args>(ArgVals)...);
return *this;
}
- // Build the final error.
+ /// Build and return the final error.
+ ///
+ /// Constructs an llvm::Error with all accumulated context. The context
+ /// is presented in reverse order: most recent context first, original
+ /// error message last. Each context layer is separated by a newline.
+ ///
+ /// \returns An llvm::Error containing the error code and formatted message.
+ /// Even if no context was added (empty context stack), an error
+ /// with the stored error code is returned.
+ ///
+ /// Example output:
+ /// \code
+ /// // ErrorBuilder::create(errc::invalid_argument, "value is 42")
+ /// // .context("processing field 'age'")
+ /// // .context("reading config")
+ /// // .build();
+ /// //
+ /// // Produces:
+ /// // "reading config
+ /// // processing field 'age'
+ /// // value is 42"
+ /// \endcode
llvm::Error build();
};
diff --git a/clang/lib/Analysis/Scalable/Support/ErrorBuilder.cpp b/clang/lib/Analysis/Scalable/Support/ErrorBuilder.cpp
index ad0a013bcf2f2..5876edb03997e 100644
--- a/clang/lib/Analysis/Scalable/Support/ErrorBuilder.cpp
+++ b/clang/lib/Analysis/Scalable/Support/ErrorBuilder.cpp
@@ -9,18 +9,18 @@
#include "clang/Analysis/Scalable/Support/ErrorBuilder.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
+#include <cassert>
namespace clang::ssaf {
ErrorBuilder ErrorBuilder::wrap(llvm::Error E) {
- if (!E) {
- llvm::consumeError(std::move(E));
- // Return builder with generic error code for success case.
- return ErrorBuilder(std::make_error_code(std::errc::invalid_argument));
- }
+ assert(
+ E &&
+ "Cannot wrap a success error - check for success before calling wrap()");
std::error_code EC;
bool FirstError = true;
+ std::vector<std::string> Messages;
ErrorBuilder Builder(std::make_error_code(std::errc::invalid_argument));
llvm::handleAllErrors(std::move(E), [&](const llvm::ErrorInfoBase &EI) {
@@ -34,24 +34,27 @@ ErrorBuilder ErrorBuilder::wrap(llvm::Error E) {
// Collect messages from all errors.
std::string ErrorMsg = EI.message();
if (!ErrorMsg.empty()) {
- Builder.ContextStack.push_back(std::move(ErrorMsg));
+ Messages.push_back(std::move(ErrorMsg));
}
});
+ // Combine all messages with " + " and push as a single context entry
+ std::string CombinedMsg = llvm::join(Messages, " + ");
+ Builder.pushContext(std::move(CombinedMsg));
+
return Builder;
}
ErrorBuilder &ErrorBuilder::context(const char *Msg) {
- ContextStack.push_back(Msg);
+ pushContext(std::string(Msg));
return *this;
}
llvm::Error ErrorBuilder::build() {
- if (ContextStack.empty())
- return llvm::Error::success();
-
// Reverse the context stack so that the most recent context appears first
// and the wrapped error (if any) appears last.
+ // Note: Even if ContextStack is empty, we create an error with the stored
+ // error code and an empty message (this is valid in LLVM).
return llvm::createStringError(llvm::join(llvm::reverse(ContextStack), "\n"),
Code);
}
diff --git a/clang/unittests/Analysis/Scalable/CMakeLists.txt b/clang/unittests/Analysis/Scalable/CMakeLists.txt
index c191abfc56eac..ce5219e348c11 100644
--- a/clang/unittests/Analysis/Scalable/CMakeLists.txt
+++ b/clang/unittests/Analysis/Scalable/CMakeLists.txt
@@ -6,6 +6,7 @@ add_distinct_clang_unittest(ClangScalableAnalysisTests
EntityLinkageTest.cpp
EntityLinkerTest.cpp
EntityNameTest.cpp
+ ErrorBuilderTest.cpp
Registries/FancyAnalysisData.cpp
Registries/MockSerializationFormat.cpp
Registries/MockSummaryExtractor1.cpp
diff --git a/clang/unittests/Analysis/Scalable/ErrorBuilderTest.cpp b/clang/unittests/Analysis/Scalable/ErrorBuilderTest.cpp
new file mode 100644
index 0000000000000..ad0c3bf34038a
--- /dev/null
+++ b/clang/unittests/Analysis/Scalable/ErrorBuilderTest.cpp
@@ -0,0 +1,187 @@
+//===- unittests/Analysis/Scalable/ErrorBuilderTest.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/Support/ErrorBuilder.h"
+#include "gtest/gtest.h"
+#include <system_error>
+
+using namespace llvm;
+
+namespace clang::ssaf {
+
+namespace {
+
+class ErrorBuilderTest : public ::testing::Test {
+protected:
+ struct ErrorInfo {
+ std::error_code Code;
+ std::string Message;
+ };
+
+ ErrorInfo extractErrorInfo(Error Err) {
+ ErrorInfo Info;
+
+ handleAllErrors(std::move(Err), [&](const StringError &SE) {
+ Info.Code = SE.convertToErrorCode();
+ Info.Message = SE.getMessage();
+ });
+
+ return Info;
+ }
+};
+
+TEST_F(ErrorBuilderTest, CreateSimpleError) {
+ auto Err =
+ ErrorBuilder::create(std::errc::invalid_argument, "test error").build();
+
+ auto Info = extractErrorInfo(std::move(Err));
+
+ EXPECT_EQ(Info.Code, std::errc::invalid_argument);
+ EXPECT_EQ(Info.Message, "test error");
+}
+
+TEST_F(ErrorBuilderTest, CreateWithErrorCode) {
+ auto EC = std::make_error_code(std::errc::no_such_file_or_directory);
+ auto Err = ErrorBuilder::create(EC, "file not found").build();
+
+ auto Info = extractErrorInfo(std::move(Err));
+
+ EXPECT_EQ(Info.Code, std::errc::no_such_file_or_directory);
+ EXPECT_EQ(Info.Message, "file not found");
+}
+
+TEST_F(ErrorBuilderTest, CreateWithFormattedMessage) {
+ auto Err = ErrorBuilder::create(std::errc::invalid_argument,
+ "field '{0}' has value {1}", "age", 150)
+ .build();
+
+ auto Info = extractErrorInfo(std::move(Err));
+
+ EXPECT_EQ(Info.Code, std::errc::invalid_argument);
+ EXPECT_EQ(Info.Message, "field 'age' has value 150");
+}
+
+TEST_F(ErrorBuilderTest, AddPlainContext) {
+ auto Err = ErrorBuilder::create(std::errc::invalid_argument, "inner error")
+ .context("outer context")
+ .build();
+
+ auto Info = extractErrorInfo(std::move(Err));
+
+ EXPECT_EQ(Info.Code, std::errc::invalid_argument);
+ EXPECT_EQ(Info.Message, "outer context\ninner error");
+}
+
+TEST_F(ErrorBuilderTest, AddMultipleArgumentContext) {
+ auto Err = ErrorBuilder::create(std::errc::invalid_argument,
+ "expected {0} but got {1} in field '{2}'",
+ "string", "number", "value")
+ .context("parsing line {0}", 42)
+ .context("reading file '{0}'", "config.json")
+ .build();
+
+ auto Info = extractErrorInfo(std::move(Err));
+
+ EXPECT_EQ(Info.Code, std::errc::invalid_argument);
+ EXPECT_EQ(Info.Message, "reading file 'config.json'\n"
+ "parsing line 42\n"
+ "expected string but got number in field 'value'");
+}
+
+TEST_F(ErrorBuilderTest, AddSpecialCharacterContext) {
+ auto Err = ErrorBuilder::create(std::errc::invalid_argument,
+ "special chars: {0}", "test\nwith\nnewlines")
+ .context("tab\tseparated\tvalues")
+ .build();
+
+ auto Info = extractErrorInfo(std::move(Err));
+
+ EXPECT_EQ(Info.Code, std::errc::invalid_argument);
+ EXPECT_EQ(Info.Message, "tab\tseparated\tvalues\n"
+ "special chars: test\n"
+ "with\n"
+ "newlines");
+}
+
+TEST_F(ErrorBuilderTest, WrapExistingError) {
+ auto OriginalErr =
+ createStringError(std::errc::invalid_argument, "original error message");
+
+ auto WrappedErr = ErrorBuilder::wrap(std::move(OriginalErr))
+ .context("additional context")
+ .build();
+
+ auto Info = extractErrorInfo(std::move(WrappedErr));
+
+ EXPECT_EQ(Info.Code, std::errc::invalid_argument);
+ EXPECT_EQ(Info.Message, "additional context\noriginal error message");
+}
+
+TEST_F(ErrorBuilderTest, WrapMultipleErrors) {
+ auto Err1 = createStringError(std::errc::invalid_argument, "first");
+ auto Err2 = createStringError(std::errc::argument_list_too_long, "second");
+ auto Err3 = createStringError(std::errc::filename_too_long, "third");
+
+ auto JoinedErr =
+ joinErrors(std::move(Err1), joinErrors(std::move(Err2), std::move(Err3)));
+
+ auto WrappedErr = ErrorBuilder::wrap(std::move(JoinedErr))
+ .context("wrapping three joined errors")
+ .build();
+
+ auto Info = extractErrorInfo(std::move(WrappedErr));
+
+ EXPECT_EQ(Info.Code, std::errc::invalid_argument);
+ // All three messages combined with " + " in the order they were handled
+ EXPECT_EQ(Info.Message,
+ "wrapping three joined errors\nfirst + second + third");
+}
+
+TEST_F(ErrorBuilderTest, WrapErrorWithEmptyMessage) {
+ auto EmptyErr = createStringError(std::errc::invalid_argument, "");
+
+ auto WrappedErr = ErrorBuilder::wrap(std::move(EmptyErr))
+ .context("")
+ .context("wrapping error with empty message")
+ .context("")
+ .build();
+
+ auto Info = extractErrorInfo(std::move(WrappedErr));
+
+ EXPECT_EQ(Info.Code, std::errc::invalid_argument);
+ EXPECT_EQ(Info.Message, "wrapping error with empty message");
+}
+
+TEST_F(ErrorBuilderTest, CreateErrorWithEmptyMessage) {
+ auto Err = ErrorBuilder::create(std::errc::invalid_argument, "")
+ .context("")
+ .context("creating error with empty message")
+ .context("")
+ .build();
+
+ auto Info = extractErrorInfo(std::move(Err));
+
+ EXPECT_EQ(Info.Code, std::errc::invalid_argument);
+ EXPECT_EQ(Info.Message, "creating error with empty message");
+}
+
+#ifndef NDEBUG
+// Death test only works in debug builds where assertions are enabled
+TEST_F(ErrorBuilderTest, WrapSuccessErrorTriggersAssertion) {
+ EXPECT_DEATH(
+ {
+ auto SuccessErr = Error::success();
+ ErrorBuilder::wrap(std::move(SuccessErr));
+ },
+ "Cannot wrap a success error - check for success before calling wrap()");
+}
+#endif // !NDEBUG
+
+} // namespace
+
+} // namespace clang::ssaf
>From 7fb6e6e75892bdad74b40a28e33855122979a58d Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Wed, 18 Feb 2026 13:59:16 -0800
Subject: [PATCH 6/6] Fix tests
---
.../Analysis/Scalable/EntityLinkerTest.cpp | 911 +++++++++---------
.../Analysis/Scalable/TestFixture.cpp | 14 +
.../unittests/Analysis/Scalable/TestFixture.h | 1 +
3 files changed, 445 insertions(+), 481 deletions(-)
diff --git a/clang/unittests/Analysis/Scalable/EntityLinkerTest.cpp b/clang/unittests/Analysis/Scalable/EntityLinkerTest.cpp
index 5ef5d5fd4d1ca..5c77dbc367cc6 100644
--- a/clang/unittests/Analysis/Scalable/EntityLinkerTest.cpp
+++ b/clang/unittests/Analysis/Scalable/EntityLinkerTest.cpp
@@ -21,11 +21,11 @@
#include "gtest/gtest.h"
#include <memory>
-namespace clang::ssaf {
+using namespace clang::ssaf;
+using namespace llvm;
namespace {
-// Mock EntitySummaryEncoding for testing
class MockEntitySummaryEncoding : public EntitySummaryEncoding {
public:
MockEntitySummaryEncoding() : Id(++Index) {}
@@ -52,13 +52,20 @@ size_t MockEntitySummaryEncoding::Index = 0;
class EntityLinkerTest : public TestFixture {
protected:
- // Helper to create a TUSummaryEncoding with entities
std::unique_ptr<TUSummaryEncoding>
createTUSummaryEncoding(BuildNamespaceKind Kind, llvm::StringRef Name) {
return std::make_unique<TUSummaryEncoding>(BuildNamespace(Kind, Name));
}
- // Helper to add an entity to a TUSummaryEncoding
+ 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::LinkageType Linkage) {
EntityName Name(USR, "", NestedBuildNamespace(getTUNamespace(TU)));
@@ -67,17 +74,122 @@ class EntityLinkerTest : public TestFixture {
return Id;
}
- // Helper to add summary data to a TUSummaryEncoding
- 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;
+ NestedBuildNamespace
+ makeLocalNamespace(const BuildNamespace &TUNamespace,
+ const NestedBuildNamespace &LUNamespace) {
+ return NestedBuildNamespace(TUNamespace).makeQualified(LUNamespace);
}
};
+// ============================================================================
+// Entity ID Table Matchers
+// ============================================================================
+
+MATCHER_P(ContainsEntity, entityName,
+ std::string(negation ? "does not contain" : "contains") +
+ " entity with name '" + ::testing::PrintToString(entityName) +
+ "'") {
+ return arg.contains(entityName);
+}
+
+MATCHER_P(IdTableHasSize, expectedCount,
+ std::string("has ") + ::testing::PrintToString(expectedCount) +
+ " entities") {
+ if (arg.count() != expectedCount) {
+ *result_listener << "has " << arg.count() << " entities";
+ return false;
+ }
+ return true;
+}
+
+// ============================================================================
+// Linkage Table Matchers
+// ============================================================================
+
+MATCHER_P2(EntityHasLinkage, entityId, expectedLinkage,
+ std::string("entity has ") +
+ ::testing::PrintToString(expectedLinkage) + " linkage") {
+ auto it = arg.find(entityId);
+ if (it == arg.end()) {
+ *result_listener << "entity " << ::testing::PrintToString(entityId)
+ << " not found in linkage table";
+ return false;
+ }
+
+ auto actualLinkage = it->second.getLinkage();
+ if (actualLinkage != expectedLinkage) {
+ *result_listener << "entity " << ::testing::PrintToString(entityId)
+ << " has linkage "
+ << ::testing::PrintToString(actualLinkage);
+ return false;
+ }
+
+ return true;
+}
+
+MATCHER_P(LinkageTableHasSize, expectedSize,
+ std::string("linkage table has size ") +
+ ::testing::PrintToString(expectedSize)) {
+ if (arg.size() != expectedSize) {
+ *result_listener << "has size " << arg.size();
+ return false;
+ }
+ return true;
+}
+
+// ============================================================================
+// Summary Data Matchers
+// ============================================================================
+
+MATCHER_P3(HasSummaryData, entityId, expectedMockId, expectedResolutionMapping,
+ std::string("has summary data for entity with expected mock ID ") +
+ ::testing::PrintToString(expectedMockId)) {
+
+ auto it = arg.find(entityId);
+ if (it == arg.end()) {
+ *result_listener << "entity " << ::testing::PrintToString(entityId)
+ << " not found in summary data";
+ return false;
+ }
+
+ auto *mock = static_cast<const MockEntitySummaryEncoding *>(it->second.get());
+
+ if (mock->getId() != expectedMockId) {
+ *result_listener << "entity " << ::testing::PrintToString(entityId)
+ << " has mock ID " << mock->getId() << " (expected "
+ << expectedMockId << ")";
+ return false;
+ }
+
+ if (mock->getPatchedIds() != expectedResolutionMapping) {
+ *result_listener << "entity " << ::testing::PrintToString(entityId)
+ << " has different resolution mapping";
+ return false;
+ }
+
+ return true;
+}
+
+MATCHER_P(DoesNotContainSummaryFor, entityId,
+ std::string("does not contain summary data for entity")) {
+ if (arg.find(entityId) != arg.end()) {
+ *result_listener << "unexpectedly contains entity "
+ << ::testing::PrintToString(entityId);
+ return false;
+ }
+ return true;
+}
+
+MATCHER_P(SummaryDataHasSize, expectedSize,
+ std::string("summary data has size ") +
+ ::testing::PrintToString(expectedSize)) {
+ if (arg.size() != expectedSize) {
+ *result_listener << "has size " << arg.size();
+ return false;
+ }
+ return true;
+}
+
TEST_F(EntityLinkerTest, NoLink) {
NestedBuildNamespace LUNamespace(
{BuildNamespace(BuildNamespaceKind::LinkUnit, "LU")});
@@ -115,141 +227,104 @@ TEST_F(EntityLinkerTest, NonEmptyLink) {
auto TU = createTUSummaryEncoding(BuildNamespaceKind::CompilationUnit, "TU");
- const auto EIdA = addEntity(*TU, "A", EntityLinkage::LinkageType::None);
- const auto ESIdAS1 = addSummaryData(*TU, EIdA, "S1");
- const auto ESIdAS2 = addSummaryData(*TU, EIdA, "S2");
+ const auto TU_A_Id = addEntity(*TU, "A", EntityLinkage::LinkageType::None);
+ 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 EIdB = addEntity(*TU, "B", EntityLinkage::LinkageType::Internal);
- const auto ESIdBS1 = addSummaryData(*TU, EIdB, "S1");
- const auto ESIdBS2 = addSummaryData(*TU, EIdB, "S2");
+ const auto TU_B_Id =
+ addEntity(*TU, "B", EntityLinkage::LinkageType::Internal);
+ 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 EIdC = addEntity(*TU, "C", EntityLinkage::LinkageType::External);
- const auto ESIdCS1 = addSummaryData(*TU, EIdC, "S1");
+ const auto TU_C_Id =
+ addEntity(*TU, "C", EntityLinkage::LinkageType::External);
+ const auto TU_C_S1_Data = addSummaryData(*TU, TU_C_Id, "S1");
- const auto EIdD = addEntity(*TU, "D", EntityLinkage::LinkageType::External);
- const auto ESIdDS2 = addSummaryData(*TU, EIdD, "S2");
+ const auto TU_D_Id =
+ addEntity(*TU, "D", EntityLinkage::LinkageType::External);
+ const auto TU_D_S2_Data = addSummaryData(*TU, TU_D_Id, "S2");
const BuildNamespace TUNamespace = getTUNamespace(*TU);
- EXPECT_THAT_ERROR(Linker.link(std::move(TU)), llvm::Succeeded());
+ ASSERT_THAT_ERROR(Linker.link(std::move(TU)), llvm::Succeeded());
const auto &Output = Linker.getOutput();
const auto &IdTable = getIdTable(Output);
+ const auto &Entities = getEntities(IdTable);
const auto &LinkageTable = getLinkageTable(Output);
const auto &Data = getData(Output);
- // Construct the nested namespace with TU inside LU
- std::vector<BuildNamespace> NamespaceVec;
- NamespaceVec.push_back(TUNamespace);
- for (const auto &NS : getNamespaces(LUNamespace)) {
- NamespaceVec.push_back(NS);
- }
- NestedBuildNamespace LocalNamespace(NamespaceVec);
+ NestedBuildNamespace LocalNamespace =
+ NestedBuildNamespace(TUNamespace).makeQualified(LUNamespace);
- EntityName NameA("A", "", LocalNamespace);
- EntityName NameB("B", "", LocalNamespace);
- EntityName NameC("C", "", LUNamespace);
- EntityName NameD("D", "", 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.
+ // EntityIdTable Tests.
{
- const auto &Entities = getEntities(IdTable);
-
- EXPECT_EQ(IdTable.count(), 4u);
-
- EXPECT_TRUE(IdTable.contains(NameA));
- EXPECT_EQ(Entities.at(NameA), EIdA);
-
- EXPECT_TRUE(IdTable.contains(NameB));
- EXPECT_EQ(Entities.at(NameB), EIdB);
-
- EXPECT_TRUE(IdTable.contains(NameC));
- EXPECT_EQ(Entities.at(NameC), EIdC);
-
- EXPECT_TRUE(IdTable.contains(NameD));
- EXPECT_EQ(Entities.at(NameD), EIdD);
+ 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.
{
- EXPECT_EQ(LinkageTable.size(), 4u);
-
- ASSERT_NE(LinkageTable.find(EIdA), LinkageTable.end());
- EXPECT_EQ(getLinkage(LinkageTable.at(EIdA)),
- EntityLinkage::LinkageType::None);
-
- ASSERT_NE(LinkageTable.find(EIdB), LinkageTable.end());
- EXPECT_EQ(LinkageTable.at(EIdB).getLinkage(),
- EntityLinkage::LinkageType::Internal);
-
- ASSERT_NE(LinkageTable.find(EIdC), LinkageTable.end());
- EXPECT_EQ(LinkageTable.at(EIdC).getLinkage(),
- EntityLinkage::LinkageType::External);
-
- ASSERT_NE(LinkageTable.find(EIdD), LinkageTable.end());
- EXPECT_EQ(LinkageTable.at(EIdD).getLinkage(),
- EntityLinkage::LinkageType::External);
+ ASSERT_THAT(LinkageTable, LinkageTableHasSize(4u));
+ ASSERT_THAT(LinkageTable,
+ EntityHasLinkage(LU_A_Id, EntityLinkage::LinkageType::None));
+ ASSERT_THAT(
+ LinkageTable,
+ EntityHasLinkage(LU_B_Id, EntityLinkage::LinkageType::Internal));
+ ASSERT_THAT(
+ LinkageTable,
+ EntityHasLinkage(LU_C_Id, EntityLinkage::LinkageType::External));
+ ASSERT_THAT(
+ LinkageTable,
+ EntityHasLinkage(LU_D_Id, EntityLinkage::LinkageType::External));
}
+ 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.
{
- EXPECT_EQ(Data.size(), 2u);
-
- std::map<EntityId, EntityId> ExpectedEntityResolutionMapping = {
- {EIdA, EIdA}, {EIdB, EIdB}, {EIdC, EIdC}, {EIdD, EIdD}};
+ ASSERT_EQ(Data.size(), 2u);
- // S1 Tests.
+ // S1 Data Tests.
{
SummaryName S1("S1");
ASSERT_NE(Data.find(S1), Data.end());
-
const auto &S1Data = Data.at(S1);
- EXPECT_EQ(S1Data.size(), 3u);
-
- EXPECT_NE(S1Data.find(EIdA), S1Data.end());
- auto *MockA =
- static_cast<MockEntitySummaryEncoding *>(S1Data.at(EIdA).get());
- EXPECT_EQ(MockA->getId(), ESIdAS1);
- EXPECT_EQ(MockA->getPatchedIds(), ExpectedEntityResolutionMapping);
-
- EXPECT_NE(S1Data.find(EIdB), S1Data.end());
- auto *MockB =
- static_cast<MockEntitySummaryEncoding *>(S1Data.at(EIdB).get());
- EXPECT_EQ(MockB->getId(), ESIdBS1);
- EXPECT_EQ(MockB->getPatchedIds(), ExpectedEntityResolutionMapping);
-
- EXPECT_NE(S1Data.find(EIdC), S1Data.end());
- auto *MockC =
- static_cast<MockEntitySummaryEncoding *>(S1Data.at(EIdC).get());
- EXPECT_EQ(MockC->getId(), ESIdCS1);
- EXPECT_EQ(MockC->getPatchedIds(), ExpectedEntityResolutionMapping);
+
+ 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(TU_C_Id, TU_C_S1_Data, Resolution));
}
- // S2 Tests.
+ // S2 Data Tests.
{
SummaryName S2("S2");
ASSERT_NE(Data.find(S2), Data.end());
-
const auto &S2Data = Data.at(S2);
- EXPECT_EQ(S2Data.size(), 3u);
-
- EXPECT_NE(S2Data.find(EIdA), S2Data.end());
- auto *MockA =
- static_cast<MockEntitySummaryEncoding *>(S2Data.at(EIdA).get());
- EXPECT_EQ(MockA->getId(), ESIdAS2);
- EXPECT_EQ(MockA->getPatchedIds(), ExpectedEntityResolutionMapping);
-
- EXPECT_NE(S2Data.find(EIdB), S2Data.end());
- auto *MockB =
- static_cast<MockEntitySummaryEncoding *>(S2Data.at(EIdB).get());
- EXPECT_EQ(MockB->getId(), ESIdBS2);
- EXPECT_EQ(MockB->getPatchedIds(), ExpectedEntityResolutionMapping);
-
- EXPECT_NE(S2Data.find(EIdD), S2Data.end());
- auto *MockD =
- static_cast<MockEntitySummaryEncoding *>(S2Data.at(EIdD).get());
- EXPECT_EQ(MockD->getId(), ESIdDS2);
- EXPECT_EQ(MockD->getPatchedIds(), ExpectedEntityResolutionMapping);
+
+ 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(TU_D_Id, TU_D_S2_Data, Resolution));
}
}
}
@@ -260,454 +335,328 @@ TEST_F(EntityLinkerTest, TwoTULinkWithAllCombinations) {
EntityLinker Linker(LUNamespace);
- // Create TU1 with entities covering all linkage types and summary
- // distributions
auto TU1 =
createTUSummaryEncoding(BuildNamespaceKind::CompilationUnit, "TU1");
// None linkage entities in TU1
- const auto EIdTU1_X_None =
- addEntity(*TU1, "X", EntityLinkage::LinkageType::None);
- const auto ESIdTU1_X_S1 = addSummaryData(*TU1, EIdTU1_X_None, "S1");
+ const auto TU1_X_Id = addEntity(*TU1, "X", EntityLinkage::LinkageType::None);
+ const auto TU1_X_S1_Data = addSummaryData(*TU1, TU1_X_Id, "S1");
- const auto EIdTU1_Y_None =
- addEntity(*TU1, "Y", EntityLinkage::LinkageType::None);
- const auto ESIdTU1_Y_S2 = addSummaryData(*TU1, EIdTU1_Y_None, "S2");
+ const auto TU1_Y_Id = addEntity(*TU1, "Y", EntityLinkage::LinkageType::None);
+ const auto TU1_Y_S2_Data = addSummaryData(*TU1, TU1_Y_Id, "S2");
- const auto EIdTU1_Z_None =
- addEntity(*TU1, "Z", EntityLinkage::LinkageType::None);
- const auto ESIdTU1_Z_S1 = addSummaryData(*TU1, EIdTU1_Z_None, "S1");
- const auto ESIdTU1_Z_S2 = addSummaryData(*TU1, EIdTU1_Z_None, "S2");
+ const auto TU1_Z_Id = addEntity(*TU1, "Z", EntityLinkage::LinkageType::None);
+ 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 EIdTU1_A_Internal =
+ const auto TU1_A_Id =
addEntity(*TU1, "A", EntityLinkage::LinkageType::Internal);
- const auto ESIdTU1_A_S1 = addSummaryData(*TU1, EIdTU1_A_Internal, "S1");
+ const auto TU1_A_S1_Data = addSummaryData(*TU1, TU1_A_Id, "S1");
- const auto EIdTU1_B_Internal =
+ const auto TU1_B_Id =
addEntity(*TU1, "B", EntityLinkage::LinkageType::Internal);
- const auto ESIdTU1_B_S2 = addSummaryData(*TU1, EIdTU1_B_Internal, "S2");
+ const auto TU1_B_S2_Data = addSummaryData(*TU1, TU1_B_Id, "S2");
- const auto EIdTU1_C_Internal =
+ const auto TU1_C_Id =
addEntity(*TU1, "C", EntityLinkage::LinkageType::Internal);
- const auto ESIdTU1_C_S1 = addSummaryData(*TU1, EIdTU1_C_Internal, "S1");
- const auto ESIdTU1_C_S2 = addSummaryData(*TU1, EIdTU1_C_Internal, "S2");
+ 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 EIdTU1_P_External =
+ const auto TU1_P_Id =
addEntity(*TU1, "P", EntityLinkage::LinkageType::External);
- const auto ESIdTU1_P_S1 = addSummaryData(*TU1, EIdTU1_P_External, "S1");
+ const auto TU1_P_S1_Data = addSummaryData(*TU1, TU1_P_Id, "S1");
- const auto EIdTU1_Q_External =
+ const auto TU1_Q_Id =
addEntity(*TU1, "Q", EntityLinkage::LinkageType::External);
- const auto ESIdTU1_Q_S2 = addSummaryData(*TU1, EIdTU1_Q_External, "S2");
+ const auto TU1_Q_S2_Data = addSummaryData(*TU1, TU1_Q_Id, "S2");
- const auto EIdTU1_R_External =
+ const auto TU1_R_Id =
addEntity(*TU1, "R", EntityLinkage::LinkageType::External);
- const auto ESIdTU1_R_S1 = addSummaryData(*TU1, EIdTU1_R_External, "S1");
- const auto ESIdTU1_R_S2 = addSummaryData(*TU1, EIdTU1_R_External, "S2");
+ 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);
- // Link TU1
- EXPECT_THAT_ERROR(Linker.link(std::move(TU1)), llvm::Succeeded());
+ ASSERT_THAT_ERROR(Linker.link(std::move(TU1)), llvm::Succeeded());
- // Create TU2 with entities covering all combinations including duplicates
auto TU2 =
createTUSummaryEncoding(BuildNamespaceKind::CompilationUnit, "TU2");
- // None linkage entities in TU2 - includes duplicates and unique
- const auto EIdTU2_X_None =
- addEntity(*TU2, "X", EntityLinkage::LinkageType::None);
- const auto ESIdTU2_X_S2 = addSummaryData(*TU2, EIdTU2_X_None, "S2");
+ // None linkage entities in TU2 - includes duplicates and uniques
+ const auto TU2_X_Id = addEntity(*TU2, "X", EntityLinkage::LinkageType::None);
+ const auto TU2_X_S2_Data = addSummaryData(*TU2, TU2_X_Id, "S2");
- const auto EIdTU2_Y_None =
- addEntity(*TU2, "Y", EntityLinkage::LinkageType::None);
- const auto ESIdTU2_Y_S1 = addSummaryData(*TU2, EIdTU2_Y_None, "S1");
+ const auto TU2_Y_Id = addEntity(*TU2, "Y", EntityLinkage::LinkageType::None);
+ const auto TU2_Y_S1_Data = addSummaryData(*TU2, TU2_Y_Id, "S1");
- const auto EIdTU2_W_None =
- addEntity(*TU2, "W", EntityLinkage::LinkageType::None);
- const auto ESIdTU2_W_S1 = addSummaryData(*TU2, EIdTU2_W_None, "S1");
- const auto ESIdTU2_W_S2 = addSummaryData(*TU2, EIdTU2_W_None, "S2");
+ const auto TU2_W_Id = addEntity(*TU2, "W", EntityLinkage::LinkageType::None);
+ 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 EIdTU2_A_Internal =
+ const auto TU2_A_Id =
addEntity(*TU2, "A", EntityLinkage::LinkageType::Internal);
- const auto ESIdTU2_A_S2 = addSummaryData(*TU2, EIdTU2_A_Internal, "S2");
+ const auto TU2_A_S2_Data = addSummaryData(*TU2, TU2_A_Id, "S2");
- const auto EIdTU2_B_Internal =
+ const auto TU2_B_Id =
addEntity(*TU2, "B", EntityLinkage::LinkageType::Internal);
- const auto ESIdTU2_B_S1 = addSummaryData(*TU2, EIdTU2_B_Internal, "S1");
+ const auto TU2_B_S1_Data = addSummaryData(*TU2, TU2_B_Id, "S1");
- const auto EIdTU2_D_Internal =
+ const auto TU2_D_Id =
addEntity(*TU2, "D", EntityLinkage::LinkageType::Internal);
- const auto ESIdTU2_D_S1 = addSummaryData(*TU2, EIdTU2_D_Internal, "S1");
- const auto ESIdTU2_D_S2 = addSummaryData(*TU2, EIdTU2_D_Internal, "S2");
+ 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 unique
- const auto EIdTU2_P_External =
+ // and uniques
+ const auto TU2_P_Id =
addEntity(*TU2, "P", EntityLinkage::LinkageType::External);
- const auto ESIdTU2_P_S2 = addSummaryData(*TU2, EIdTU2_P_External, "S2");
+ const auto TU2_P_S2_Data = addSummaryData(*TU2, TU2_P_Id, "S2");
- const auto EIdTU2_Q_External =
+ const auto TU2_Q_Id =
addEntity(*TU2, "Q", EntityLinkage::LinkageType::External);
- const auto ESIdTU2_Q_S1 = addSummaryData(*TU2, EIdTU2_Q_External, "S1");
+ const auto TU2_Q_S1_Data = addSummaryData(*TU2, TU2_Q_Id, "S1");
- const auto EIdTU2_S_External =
+ const auto TU2_S_Id =
addEntity(*TU2, "S", EntityLinkage::LinkageType::External);
- const auto ESIdTU2_S_S1 = addSummaryData(*TU2, EIdTU2_S_External, "S1");
- const auto ESIdTU2_S_S2 = addSummaryData(*TU2, EIdTU2_S_External, "S2");
+ 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);
- // Link TU2
- EXPECT_THAT_ERROR(Linker.link(std::move(TU2)), llvm::Succeeded());
+ ASSERT_THAT_ERROR(Linker.link(std::move(TU2)), llvm::Succeeded());
- // Verify the output
const auto &Output = Linker.getOutput();
const auto &IdTable = getIdTable(Output);
+ const auto &Entities = getEntities(IdTable);
const auto &LinkageTable = getLinkageTable(Output);
const auto &Data = getData(Output);
- // Construct the nested namespaces
- std::vector<BuildNamespace> TU1NamespaceVec;
- TU1NamespaceVec.push_back(TU1Namespace);
- for (const auto &NS : getNamespaces(LUNamespace)) {
- TU1NamespaceVec.push_back(NS);
- }
- NestedBuildNamespace TU1LocalNamespace(TU1NamespaceVec);
+ NestedBuildNamespace TU1LocalNamespace =
+ NestedBuildNamespace(TU1Namespace).makeQualified(LUNamespace);
- std::vector<BuildNamespace> TU2NamespaceVec;
- TU2NamespaceVec.push_back(TU2Namespace);
- for (const auto &NS : getNamespaces(LUNamespace)) {
- TU2NamespaceVec.push_back(NS);
- }
- NestedBuildNamespace TU2LocalNamespace(TU2NamespaceVec);
+ NestedBuildNamespace TU2LocalNamespace =
+ NestedBuildNamespace(TU2Namespace).makeQualified(LUNamespace);
- // Create expected entity names
// None linkage entities use local namespace (TU scoped)
- EntityName NameTU1_X_None("X", "", TU1LocalNamespace);
- EntityName NameTU1_Y_None("Y", "", TU1LocalNamespace);
- EntityName NameTU1_Z_None("Z", "", TU1LocalNamespace);
- EntityName NameTU2_X_None("X", "", TU2LocalNamespace);
- EntityName NameTU2_Y_None("Y", "", TU2LocalNamespace);
- EntityName NameTU2_W_None("W", "", TU2LocalNamespace);
+ 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 NameTU1_A_Internal("A", "", TU1LocalNamespace);
- EntityName NameTU1_B_Internal("B", "", TU1LocalNamespace);
- EntityName NameTU1_C_Internal("C", "", TU1LocalNamespace);
- EntityName NameTU2_A_Internal("A", "", TU2LocalNamespace);
- EntityName NameTU2_B_Internal("B", "", TU2LocalNamespace);
- EntityName NameTU2_D_Internal("D", "", TU2LocalNamespace);
+ 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 NameP_External("P", "", LUNamespace);
- EntityName NameQ_External("Q", "", LUNamespace);
- EntityName NameR_External("R", "", LUNamespace);
- EntityName NameS_External("S", "", LUNamespace);
+ 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
+ // EntityIdTable Tests.
{
- const auto &Entities = getEntities(IdTable);
-
// Should have 6 None + 6 Internal + 4 External = 16 entities total
- EXPECT_EQ(IdTable.count(), 16u);
-
- // TU1 None linkage entities
- EXPECT_TRUE(IdTable.contains(NameTU1_X_None));
- ASSERT_EQ(Entities.at(NameTU1_X_None), EIdTU1_X_None);
-
- EXPECT_TRUE(IdTable.contains(NameTU1_Y_None));
- EXPECT_EQ(Entities.at(NameTU1_Y_None), EIdTU1_Y_None);
-
- EXPECT_TRUE(IdTable.contains(NameTU1_Z_None));
- EXPECT_EQ(Entities.at(NameTU1_Z_None), EIdTU1_Z_None);
-
- // TU2 None linkage entities (different from TU1 due to namespace)
- EXPECT_TRUE(IdTable.contains(NameTU2_X_None));
- EXPECT_EQ(Entities.at(NameTU2_X_None), EIdTU2_X_None);
-
- EXPECT_TRUE(IdTable.contains(NameTU2_Y_None));
- EXPECT_EQ(Entities.at(NameTU2_Y_None), EIdTU2_Y_None);
-
- EXPECT_TRUE(IdTable.contains(NameTU2_W_None));
- EXPECT_EQ(Entities.at(NameTU2_W_None), EIdTU2_W_None);
-
- // TU1 Internal linkage entities
- EXPECT_TRUE(IdTable.contains(NameTU1_A_Internal));
- EXPECT_EQ(Entities.at(NameTU1_A_Internal), EIdTU1_A_Internal);
-
- EXPECT_TRUE(IdTable.contains(NameTU1_B_Internal));
- EXPECT_EQ(Entities.at(NameTU1_B_Internal), EIdTU1_B_Internal);
-
- EXPECT_TRUE(IdTable.contains(NameTU1_C_Internal));
- EXPECT_EQ(Entities.at(NameTU1_C_Internal), EIdTU1_C_Internal);
-
- // TU2 Internal linkage entities (different from TU1 due to namespace)
- EXPECT_TRUE(IdTable.contains(NameTU2_A_Internal));
- EXPECT_EQ(Entities.at(NameTU2_A_Internal), EIdTU2_A_Internal);
-
- EXPECT_TRUE(IdTable.contains(NameTU2_B_Internal));
- EXPECT_EQ(Entities.at(NameTU2_B_Internal), EIdTU2_B_Internal);
-
- EXPECT_TRUE(IdTable.contains(NameTU2_D_Internal));
- EXPECT_EQ(Entities.at(NameTU2_D_Internal), EIdTU2_D_Internal);
-
- // External linkage entities (shared across TUs)
- EXPECT_TRUE(IdTable.contains(NameP_External));
- EXPECT_EQ(Entities.at(NameP_External), EIdTU1_P_External);
-
- EXPECT_TRUE(IdTable.contains(NameQ_External));
- EXPECT_EQ(Entities.at(NameQ_External), EIdTU1_Q_External);
-
- EXPECT_TRUE(IdTable.contains(NameR_External));
- EXPECT_EQ(Entities.at(NameR_External), EIdTU1_R_External);
-
- EXPECT_TRUE(IdTable.contains(NameS_External));
- EXPECT_EQ(Entities.at(NameS_External), EIdTU2_S_External);
+ 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));
}
- // LinkageTable Tests
+ // 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.
{
- EXPECT_EQ(LinkageTable.size(), 16u);
-
- // Verify None linkage entities
- EXPECT_EQ(LinkageTable.at(EIdTU1_X_None).getLinkage(),
- EntityLinkage::LinkageType::None);
- EXPECT_EQ(LinkageTable.at(EIdTU1_Y_None).getLinkage(),
- EntityLinkage::LinkageType::None);
- EXPECT_EQ(LinkageTable.at(EIdTU1_Z_None).getLinkage(),
- EntityLinkage::LinkageType::None);
- EXPECT_EQ(LinkageTable.at(EIdTU2_X_None).getLinkage(),
- EntityLinkage::LinkageType::None);
- EXPECT_EQ(LinkageTable.at(EIdTU2_Y_None).getLinkage(),
- EntityLinkage::LinkageType::None);
- EXPECT_EQ(LinkageTable.at(EIdTU2_W_None).getLinkage(),
- EntityLinkage::LinkageType::None);
-
- // Verify Internal linkage entities
- EXPECT_EQ(LinkageTable.at(EIdTU1_A_Internal).getLinkage(),
- EntityLinkage::LinkageType::Internal);
- EXPECT_EQ(LinkageTable.at(EIdTU1_B_Internal).getLinkage(),
- EntityLinkage::LinkageType::Internal);
- EXPECT_EQ(LinkageTable.at(EIdTU1_C_Internal).getLinkage(),
- EntityLinkage::LinkageType::Internal);
- EXPECT_EQ(LinkageTable.at(EIdTU2_A_Internal).getLinkage(),
- EntityLinkage::LinkageType::Internal);
- EXPECT_EQ(LinkageTable.at(EIdTU2_B_Internal).getLinkage(),
- EntityLinkage::LinkageType::Internal);
- EXPECT_EQ(LinkageTable.at(EIdTU2_D_Internal).getLinkage(),
- EntityLinkage::LinkageType::Internal);
-
- // Verify External linkage entities
- EXPECT_EQ(LinkageTable.at(EIdTU1_P_External).getLinkage(),
- EntityLinkage::LinkageType::External);
- EXPECT_EQ(LinkageTable.at(EIdTU1_Q_External).getLinkage(),
- EntityLinkage::LinkageType::External);
- EXPECT_EQ(LinkageTable.at(EIdTU1_R_External).getLinkage(),
- EntityLinkage::LinkageType::External);
- EXPECT_EQ(LinkageTable.at(EIdTU2_S_External).getLinkage(),
- EntityLinkage::LinkageType::External);
+ ASSERT_THAT(LinkageTable, LinkageTableHasSize(16u));
+
+ ASSERT_THAT(
+ LinkageTable,
+ EntityHasLinkage(LU_TU1_X_Id, EntityLinkage::LinkageType::None));
+ ASSERT_THAT(
+ LinkageTable,
+ EntityHasLinkage(LU_TU1_Y_Id, EntityLinkage::LinkageType::None));
+ ASSERT_THAT(
+ LinkageTable,
+ EntityHasLinkage(LU_TU1_Z_Id, EntityLinkage::LinkageType::None));
+ ASSERT_THAT(
+ LinkageTable,
+ EntityHasLinkage(LU_TU2_X_Id, EntityLinkage::LinkageType::None));
+ ASSERT_THAT(
+ LinkageTable,
+ EntityHasLinkage(LU_TU2_Y_Id, EntityLinkage::LinkageType::None));
+ ASSERT_THAT(
+ LinkageTable,
+ EntityHasLinkage(LU_TU2_W_Id, EntityLinkage::LinkageType::None));
+
+ ASSERT_THAT(
+ LinkageTable,
+ EntityHasLinkage(LU_TU1_A_Id, EntityLinkage::LinkageType::Internal));
+ ASSERT_THAT(
+ LinkageTable,
+ EntityHasLinkage(LU_TU1_B_Id, EntityLinkage::LinkageType::Internal));
+ ASSERT_THAT(
+ LinkageTable,
+ EntityHasLinkage(LU_TU1_C_Id, EntityLinkage::LinkageType::Internal));
+ ASSERT_THAT(
+ LinkageTable,
+ EntityHasLinkage(LU_TU2_A_Id, EntityLinkage::LinkageType::Internal));
+ ASSERT_THAT(
+ LinkageTable,
+ EntityHasLinkage(LU_TU2_B_Id, EntityLinkage::LinkageType::Internal));
+ ASSERT_THAT(
+ LinkageTable,
+ EntityHasLinkage(LU_TU2_D_Id, EntityLinkage::LinkageType::Internal));
+
+ ASSERT_THAT(
+ LinkageTable,
+ EntityHasLinkage(LU_P_Id, EntityLinkage::LinkageType::External));
+ ASSERT_THAT(
+ LinkageTable,
+ EntityHasLinkage(LU_Q_Id, EntityLinkage::LinkageType::External));
+ ASSERT_THAT(
+ LinkageTable,
+ EntityHasLinkage(LU_R_Id, EntityLinkage::LinkageType::External));
+ ASSERT_THAT(
+ LinkageTable,
+ EntityHasLinkage(LU_S_Id, EntityLinkage::LinkageType::External));
}
- // Data Tests
+ // Data Tests.
{
- EXPECT_EQ(Data.size(), 2u);
-
- // Build entity resolution mappings for each TU
- std::map<EntityId, EntityId> TU1EntityResolutionMapping = {
- {EIdTU1_X_None, EIdTU1_X_None},
- {EIdTU1_Y_None, EIdTU1_Y_None},
- {EIdTU1_Z_None, EIdTU1_Z_None},
- {EIdTU1_A_Internal, EIdTU1_A_Internal},
- {EIdTU1_B_Internal, EIdTU1_B_Internal},
- {EIdTU1_C_Internal, EIdTU1_C_Internal},
- {EIdTU1_P_External, EIdTU1_P_External},
- {EIdTU1_Q_External, EIdTU1_Q_External},
- {EIdTU1_R_External, EIdTU1_R_External}};
-
- std::map<EntityId, EntityId> TU2EntityResolutionMapping = {
- {EIdTU2_X_None, EIdTU2_X_None},
- {EIdTU2_Y_None, EIdTU2_Y_None},
- {EIdTU2_W_None, EIdTU2_W_None},
- {EIdTU2_A_Internal, EIdTU2_A_Internal},
- {EIdTU2_B_Internal, EIdTU2_B_Internal},
- {EIdTU2_D_Internal, EIdTU2_D_Internal},
- // External linkage entities from TU2 resolve to TU1's IDs if duplicate
- {EIdTU2_P_External, EIdTU1_P_External},
- {EIdTU2_Q_External, EIdTU1_Q_External},
- {EIdTU2_S_External, EIdTU2_S_External}};
-
- // S1 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_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,S) = 11 entities
- // Note: TU2's P and Q external entities are dropped because TU1 already
- // has them
- EXPECT_EQ(S1Data.size(), 11u);
-
- // Verify TU1 entities in S1
- EXPECT_NE(S1Data.find(EIdTU1_X_None), S1Data.end());
- auto *MockTU1_X = static_cast<MockEntitySummaryEncoding *>(
- S1Data.at(EIdTU1_X_None).get());
- EXPECT_EQ(MockTU1_X->getId(), ESIdTU1_X_S1);
- EXPECT_EQ(MockTU1_X->getPatchedIds(), TU1EntityResolutionMapping);
-
- EXPECT_NE(S1Data.find(EIdTU1_Z_None), S1Data.end());
- auto *MockTU1_Z = static_cast<MockEntitySummaryEncoding *>(
- S1Data.at(EIdTU1_Z_None).get());
- EXPECT_EQ(MockTU1_Z->getId(), ESIdTU1_Z_S1);
- EXPECT_EQ(MockTU1_Z->getPatchedIds(), TU1EntityResolutionMapping);
-
- EXPECT_NE(S1Data.find(EIdTU1_A_Internal), S1Data.end());
- auto *MockTU1_A = static_cast<MockEntitySummaryEncoding *>(
- S1Data.at(EIdTU1_A_Internal).get());
- EXPECT_EQ(MockTU1_A->getId(), ESIdTU1_A_S1);
- EXPECT_EQ(MockTU1_A->getPatchedIds(), TU1EntityResolutionMapping);
-
- EXPECT_NE(S1Data.find(EIdTU1_C_Internal), S1Data.end());
- auto *MockTU1_C = static_cast<MockEntitySummaryEncoding *>(
- S1Data.at(EIdTU1_C_Internal).get());
- EXPECT_EQ(MockTU1_C->getId(), ESIdTU1_C_S1);
- EXPECT_EQ(MockTU1_C->getPatchedIds(), TU1EntityResolutionMapping);
-
- EXPECT_NE(S1Data.find(EIdTU1_P_External), S1Data.end());
- auto *MockTU1_P = static_cast<MockEntitySummaryEncoding *>(
- S1Data.at(EIdTU1_P_External).get());
- EXPECT_EQ(MockTU1_P->getId(), ESIdTU1_P_S1);
- EXPECT_EQ(MockTU1_P->getPatchedIds(), TU1EntityResolutionMapping);
-
- EXPECT_NE(S1Data.find(EIdTU1_R_External), S1Data.end());
- auto *MockTU1_R = static_cast<MockEntitySummaryEncoding *>(
- S1Data.at(EIdTU1_R_External).get());
- EXPECT_EQ(MockTU1_R->getId(), ESIdTU1_R_S1);
- EXPECT_EQ(MockTU1_R->getPatchedIds(), TU1EntityResolutionMapping);
-
- // Verify TU2 entities in S1
- EXPECT_NE(S1Data.find(EIdTU2_Y_None), S1Data.end());
- auto *MockTU2_Y = static_cast<MockEntitySummaryEncoding *>(
- S1Data.at(EIdTU2_Y_None).get());
- EXPECT_EQ(MockTU2_Y->getId(), ESIdTU2_Y_S1);
- EXPECT_EQ(MockTU2_Y->getPatchedIds(), TU2EntityResolutionMapping);
-
- EXPECT_NE(S1Data.find(EIdTU2_W_None), S1Data.end());
- auto *MockTU2_W = static_cast<MockEntitySummaryEncoding *>(
- S1Data.at(EIdTU2_W_None).get());
- EXPECT_EQ(MockTU2_W->getId(), ESIdTU2_W_S1);
- EXPECT_EQ(MockTU2_W->getPatchedIds(), TU2EntityResolutionMapping);
-
- EXPECT_NE(S1Data.find(EIdTU2_B_Internal), S1Data.end());
- auto *MockTU2_B = static_cast<MockEntitySummaryEncoding *>(
- S1Data.at(EIdTU2_B_Internal).get());
- EXPECT_EQ(MockTU2_B->getId(), ESIdTU2_B_S1);
- EXPECT_EQ(MockTU2_B->getPatchedIds(), TU2EntityResolutionMapping);
-
- EXPECT_NE(S1Data.find(EIdTU2_D_Internal), S1Data.end());
- auto *MockTU2_D = static_cast<MockEntitySummaryEncoding *>(
- S1Data.at(EIdTU2_D_Internal).get());
- EXPECT_EQ(MockTU2_D->getId(), ESIdTU2_D_S1);
- EXPECT_EQ(MockTU2_D->getPatchedIds(), TU2EntityResolutionMapping);
-
- EXPECT_NE(S1Data.find(EIdTU2_S_External), S1Data.end());
- auto *MockTU2_S = static_cast<MockEntitySummaryEncoding *>(
- S1Data.at(EIdTU2_S_External).get());
- EXPECT_EQ(MockTU2_S->getId(), ESIdTU2_S_S1);
- EXPECT_EQ(MockTU2_S->getPatchedIds(), TU2EntityResolutionMapping);
-
- // Verify TU2's duplicate external entities are NOT in S1
- EXPECT_EQ(S1Data.find(EIdTU2_Q_External), S1Data.end());
+
+ // 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,
+ HasSummaryData(LU_S_Id, TU2_S_S1_Data, TU2Resolution));
}
- // S2 Tests
+ // 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,S) = 11 entities
- // Note: TU2's P and Q external entities are dropped because TU1 already
- // has them
- EXPECT_EQ(S2Data.size(), 11u);
-
- // Verify TU1 entities in S2
- EXPECT_NE(S2Data.find(EIdTU1_Y_None), S2Data.end());
- auto *MockTU1_Y = static_cast<MockEntitySummaryEncoding *>(
- S2Data.at(EIdTU1_Y_None).get());
- EXPECT_EQ(MockTU1_Y->getId(), ESIdTU1_Y_S2);
- EXPECT_EQ(MockTU1_Y->getPatchedIds(), TU1EntityResolutionMapping);
-
- EXPECT_NE(S2Data.find(EIdTU1_Z_None), S2Data.end());
- auto *MockTU1_Z = static_cast<MockEntitySummaryEncoding *>(
- S2Data.at(EIdTU1_Z_None).get());
- EXPECT_EQ(MockTU1_Z->getId(), ESIdTU1_Z_S2);
- EXPECT_EQ(MockTU1_Z->getPatchedIds(), TU1EntityResolutionMapping);
-
- EXPECT_NE(S2Data.find(EIdTU1_B_Internal), S2Data.end());
- auto *MockTU1_B = static_cast<MockEntitySummaryEncoding *>(
- S2Data.at(EIdTU1_B_Internal).get());
- EXPECT_EQ(MockTU1_B->getId(), ESIdTU1_B_S2);
- EXPECT_EQ(MockTU1_B->getPatchedIds(), TU1EntityResolutionMapping);
-
- EXPECT_NE(S2Data.find(EIdTU1_C_Internal), S2Data.end());
- auto *MockTU1_C = static_cast<MockEntitySummaryEncoding *>(
- S2Data.at(EIdTU1_C_Internal).get());
- EXPECT_EQ(MockTU1_C->getId(), ESIdTU1_C_S2);
- EXPECT_EQ(MockTU1_C->getPatchedIds(), TU1EntityResolutionMapping);
-
- EXPECT_NE(S2Data.find(EIdTU1_Q_External), S2Data.end());
- auto *MockTU1_Q = static_cast<MockEntitySummaryEncoding *>(
- S2Data.at(EIdTU1_Q_External).get());
- EXPECT_EQ(MockTU1_Q->getId(), ESIdTU1_Q_S2);
- EXPECT_EQ(MockTU1_Q->getPatchedIds(), TU1EntityResolutionMapping);
-
- EXPECT_NE(S2Data.find(EIdTU1_R_External), S2Data.end());
- auto *MockTU1_R = static_cast<MockEntitySummaryEncoding *>(
- S2Data.at(EIdTU1_R_External).get());
- EXPECT_EQ(MockTU1_R->getId(), ESIdTU1_R_S2);
- EXPECT_EQ(MockTU1_R->getPatchedIds(), TU1EntityResolutionMapping);
-
- // Verify TU2 entities in S2
- EXPECT_NE(S2Data.find(EIdTU2_X_None), S2Data.end());
- auto *MockTU2_X = static_cast<MockEntitySummaryEncoding *>(
- S2Data.at(EIdTU2_X_None).get());
- EXPECT_EQ(MockTU2_X->getId(), ESIdTU2_X_S2);
- EXPECT_EQ(MockTU2_X->getPatchedIds(), TU2EntityResolutionMapping);
-
- EXPECT_NE(S2Data.find(EIdTU2_W_None), S2Data.end());
- auto *MockTU2_W = static_cast<MockEntitySummaryEncoding *>(
- S2Data.at(EIdTU2_W_None).get());
- EXPECT_EQ(MockTU2_W->getId(), ESIdTU2_W_S2);
- EXPECT_EQ(MockTU2_W->getPatchedIds(), TU2EntityResolutionMapping);
-
- EXPECT_NE(S2Data.find(EIdTU2_A_Internal), S2Data.end());
- auto *MockTU2_A = static_cast<MockEntitySummaryEncoding *>(
- S2Data.at(EIdTU2_A_Internal).get());
- EXPECT_EQ(MockTU2_A->getId(), ESIdTU2_A_S2);
- EXPECT_EQ(MockTU2_A->getPatchedIds(), TU2EntityResolutionMapping);
-
- EXPECT_NE(S2Data.find(EIdTU2_D_Internal), S2Data.end());
- auto *MockTU2_D = static_cast<MockEntitySummaryEncoding *>(
- S2Data.at(EIdTU2_D_Internal).get());
- EXPECT_EQ(MockTU2_D->getId(), ESIdTU2_D_S2);
- EXPECT_EQ(MockTU2_D->getPatchedIds(), TU2EntityResolutionMapping);
-
- EXPECT_NE(S2Data.find(EIdTU2_S_External), S2Data.end());
- auto *MockTU2_S = static_cast<MockEntitySummaryEncoding *>(
- S2Data.at(EIdTU2_S_External).get());
- EXPECT_EQ(MockTU2_S->getId(), ESIdTU2_S_S2);
- EXPECT_EQ(MockTU2_S->getPatchedIds(), TU2EntityResolutionMapping);
-
- // Verify TU2's duplicate external entities are NOT in S2
- EXPECT_EQ(S2Data.find(EIdTU2_P_External), S2Data.end());
+
+ // 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,
+ HasSummaryData(LU_S_Id, TU2_S_S2_Data, TU2Resolution));
}
}
}
} // namespace
-
-} // namespace clang::ssaf
diff --git a/clang/unittests/Analysis/Scalable/TestFixture.cpp b/clang/unittests/Analysis/Scalable/TestFixture.cpp
index c3c7027c1dc3f..56bacb811472d 100644
--- a/clang/unittests/Analysis/Scalable/TestFixture.cpp
+++ b/clang/unittests/Analysis/Scalable/TestFixture.cpp
@@ -24,6 +24,20 @@ template <class T> static std::string asString(const T &Obj) {
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 << ")";
}
+
+// Free functions for Google Test matchers
+namespace clang::ssaf {
+
+void PrintTo(const EntityId &E, std::ostream *OS) {
+ TestFixture::PrintTo(E, OS);
+}
+
+void PrintTo(const SummaryName &N, std::ostream *OS) {
+ TestFixture::PrintTo(N, OS);
+}
+
+} // namespace clang::ssaf
diff --git a/clang/unittests/Analysis/Scalable/TestFixture.h b/clang/unittests/Analysis/Scalable/TestFixture.h
index c8936fd2cfb53..7f2622f195cb7 100644
--- a/clang/unittests/Analysis/Scalable/TestFixture.h
+++ b/clang/unittests/Analysis/Scalable/TestFixture.h
@@ -31,6 +31,7 @@ class TestFixture : public ::testing::Test {
static auto &get##FIELD_NAME(CLASS &X) { return X.FIELD_NAME; }
#include "clang/Analysis/Scalable/Model/PrivateFieldNames.def"
+public:
static void PrintTo(const EntityId &, std::ostream *);
static void PrintTo(const SummaryName &, std::ostream *);
};
More information about the cfe-commits
mailing list