[llvm-branch-commits] [clang] [SSAF][PointerAssignments] Add PointerAssignments summary and extractor (PR #188654)

Ziqing Luo via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Wed Apr 8 15:17:24 PDT 2026


https://github.com/ziqingluo-90 updated https://github.com/llvm/llvm-project/pull/188654

>From cd394f79d9dd95fc43bb417d4d87722625f6f79c Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Wed, 25 Mar 2026 17:44:15 -0700
Subject: [PATCH 1/5] [SSAF][PointerAssignments] Add PointerAssignments summary
 and extractor

Implement PointerAssignments summary and extractor, which uses
EntityPointerLevel. An assignment is extracted as a pair of
EntityPointerLevels. Extracted assignments form a directed graph
encoding abstracted pointer flow information.

rdar://172429193
---
 .../Analyses/EntityPointerLevel.h             |   20 +-
 .../Analyses/PointerAssignments.h             |   64 +
 .../SSAFBuiltinForceLinker.h                  |   19 +-
 clang/lib/Analysis/UnsafeBufferUsage.cpp      |   20 +-
 .../Analyses/CMakeLists.txt                   |    2 +
 .../Analyses/EntityPointerLevel.cpp           |   17 +-
 .../PointerAssignments/PointerAssignments.cpp |  105 ++
 .../PointerAssignmentsExtractor.cpp           |  383 ++++++
 .../Analyses/SSAFAnalysesCommon.h             |    7 +-
 .../UnsafeBufferUsage/tu-summary.json         |   40 +
 .../Analyses/PointerAssignmentsTest.cpp       | 1150 +++++++++++++++++
 .../CMakeLists.txt                            |    1 +
 .../SummaryExtractorRegistryTest.cpp          |    1 +
 13 files changed, 1806 insertions(+), 23 deletions(-)
 create mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerAssignments.h
 create mode 100644 clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerAssignments/PointerAssignments.cpp
 create mode 100644 clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerAssignments/PointerAssignmentsExtractor.cpp
 create mode 100644 clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerAssignmentsTest.cpp

diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
index f2a2c73a29a7a..1ccb78066786b 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
@@ -101,24 +101,24 @@ llvm::Expected<EntityPointerLevelSet>
 translateEntityPointerLevel(const Expr *E, ASTContext &Ctx,
                             std::function<EntityId(EntityName EN)> AddEntity);
 
-/// Create an EntityPointerLevel (EPL) from a NamedDecl of a pointer/array type.
+/// Create EntityPointerLevel(s) (EPLs) from a NamedDecl of a pointer/array type.
 ///
-/// \param E the pointer expression to be translated
+/// \param ND the pointer type Decl to be translated
 /// \param Ctx the AST context of `E`
 /// \param AddEntity the callback provided by the caller to convert EntityNames
 /// to EntityIds.
-/// \param IsFunRet true iff the created EPL is associated with the return type
-/// of a function entity.
-llvm::Expected<EntityPointerLevel>
+/// \param IsFunRet true iff the created EPL(s) is associated with the return
+/// type of a function entity.
+llvm::Expected<EntityPointerLevelSet>
 creatEntityPointerLevel(const NamedDecl *ND, ASTContext &Ctx,
                         std::function<EntityId(EntityName EN)> AddEntity,
                         bool IsFunRet = false);
 
-/// Creates a new EntityPointerLevel (EPL) from `E` by incrementing `E`'s
-/// pointer level.
-/// \return the EPL that is associated with the pointee (or array element) type
-/// of `E`'s associated pointer/array tyoe of the same entity.
-EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E);
+/// Creates new EntityPointerLevel(s) (EPLs) from the provided
+/// one(s) by incrementing their pointer levels.
+/// \return the EPL(s) that is associated with the pointee (or array element)
+/// type of `E`'s associated pointer/array tyoe of the same entity.
+EntityPointerLevelSet incrementPointerLevel(const EntityPointerLevelSet &E);
 
 llvm::json::Value
 entityPointerLevelToJSON(const EntityPointerLevel &EPL,
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerAssignments.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerAssignments.h
new file mode 100644
index 0000000000000..cc82f2479a47d
--- /dev/null
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerAssignments.h
@@ -0,0 +1,64 @@
+//===---------------- PointerAssignments.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 an analysis that builds directed graphs where nodes
+//  are pointers and edges are assignment operations, each of which bridges two
+//  nodes.
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_POINTERASSIGNMENTS_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_POINTERASSIGNMENTS_H
+
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h"
+
+namespace clang::ssaf {
+
+/// Maps LHSs to their RHS sets:
+using EdgeSet = std::map<EntityPointerLevel, EntityPointerLevelSet>;
+
+class PointerAssignmentsEntitySummary final : public EntitySummary {
+  EdgeSet Edges;
+
+  friend class PointerAssignmentsTUSummaryExtractor;
+
+  PointerAssignmentsEntitySummary(EdgeSet Edges)
+      : EntitySummary(), Edges(std::move(Edges)) {}
+
+public:
+  SummaryName getSummaryName() const override { return summaryName(); };
+
+  bool operator==(const EdgeSet &Other) const { return Edges == Other; }
+
+  bool operator==(const PointerAssignmentsEntitySummary &Other) const {
+    return Edges == Other.Edges;
+  }
+
+  bool empty() const { return Edges.empty() && Edges.empty(); }
+
+  static llvm::json::Object
+  summaryToJSON(const EntitySummary &ES,
+                JSONFormat::EntityIdToJSONFn EntityId2JSON);
+
+  static llvm::Expected<std::unique_ptr<EntitySummary>>
+  summaryFromJSON(const llvm::json::Object &Data, EntityIdTable &,
+                  JSONFormat::EntityIdFromJSONFn EntityIdFromJSON);
+
+  static SummaryName summaryName() { return SummaryName{"PointerAssignments"}; }
+};
+
+struct PointerAssignmentsJSONFormatInfo : JSONFormat::FormatInfo {
+  PointerAssignmentsJSONFormatInfo()
+      : JSONFormat::FormatInfo(
+            PointerAssignmentsEntitySummary::summaryName(),
+            PointerAssignmentsEntitySummary::summaryToJSON,
+            PointerAssignmentsEntitySummary::summaryFromJSON) {}
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_POINTERASSIGNMENTS_H
\ No newline at end of file
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h b/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
index 670b80e49eadc..c7d9444b07bed 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
@@ -25,13 +25,30 @@ extern volatile int SSAFJSONFormatAnchorSource;
 [[maybe_unused]] static int SSAFJSONFormatAnchorDestination =
     SSAFJSONFormatAnchorSource;
 
+// This anchor is used to force the linker to link the UnsafeBufferUsage
+// JSONFormat registration:
 extern volatile int UnsafeBufferUsageSSAFJSONFormatAnchorSource;
 [[maybe_unused]] static int UnsafeBufferUsageSSAFJSONFormatAnchorDestination =
     UnsafeBufferUsageSSAFJSONFormatAnchorSource;
 
+// This anchor is used to force the linker to link the UnsafeBufferUsage
+// JSONFormat registration:
 extern volatile int UnsafeBufferUsageTUSummaryExtractorAnchorSource;
 [[maybe_unused]] static int
     UnsafeBufferUsageTUSummaryExtractorAnchorDestination =
-        UnsafeBufferUsageSSAFJSONFormatAnchorSource;
+        UnsafeBufferUsageTUSummaryExtractorAnchorSource;
+
+// This anchor is used to force the linker to link the PointerAssignments
+// JSONFormat registration:
+extern volatile int PointerAssignmentsSSAFJSONFormatAnchorSource;
+[[maybe_unused]] static int PointerAssignmentsSSAFJSONFormatAnchorDestination =
+    PointerAssignmentsSSAFJSONFormatAnchorSource;
+
+// This anchor is used to force the linker to link the PointerAssignments
+// TUSummaryExtractor registration.
+extern volatile int PointerAssignmentsTUSummaryExtractorAnchorSource;
+[[maybe_unused]] static int
+    PointerAssignmentsTUSummaryExtractorAnchorDestination =
+        PointerAssignmentsTUSummaryExtractorAnchorSource;
 
 #endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_SSAFBUILTINFORCELINKER_H
diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 5369774db1ba5..2b11af5e4e458 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -2951,7 +2951,9 @@ template <typename NodeTy> struct CompareNode {
 //   FunctionDecl
 //   BlockDecl
 //   ObjCMethodDecl
-static void populateStmtsForFindingGadgets(SmallVector<const Stmt *> &Stmts,
+//
+// return false iff `D` is not any one above.
+static bool populateStmtsForFindingGadgets(SmallVector<const Stmt *> &Stmts,
                                            const Decl *D) {
   auto AddStmt = [&Stmts](const Stmt *S) {
     if (S)
@@ -2965,13 +2967,21 @@ static void populateStmtsForFindingGadgets(SmallVector<const Stmt *> &Stmts,
       llvm::append_range(
           Stmts, llvm::map_range(CtorD->inits(),
                                  std::mem_fn(&CXXCtorInitializer::getInit)));
-  } else if (isa<BlockDecl>(D) || isa<ObjCMethodDecl>(D)) {
+    return true;
+  }
+  if (isa<BlockDecl>(D) || isa<ObjCMethodDecl>(D)) {
     AddStmt(D->getBody());
-  } else if (const auto *VD = dyn_cast<VarDecl>(D)) {
-    AddStmt(VD->getInit()); // FIXME: default arg for ParmVarDecl?
-  } else if (const auto *FD = dyn_cast<FieldDecl>(D)) {
+    return true;
+  }
+  if (const auto *VD = dyn_cast<VarDecl>(D)) {
+    AddStmt(VD->getInit());
+    return true;
+  }
+  if (const auto *FD = dyn_cast<FieldDecl>(D)) {
     AddStmt(FD->getInClassInitializer());
+    return true;
   }
+  return false;
 }
 
 struct WarningGadgetSets {
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
index c8544d073d276..9197d8689620e 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
@@ -6,6 +6,8 @@ add_clang_library(clangScalableStaticAnalysisFrameworkAnalyses
   EntityPointerLevel.cpp
   UnsafeBufferUsage/UnsafeBufferUsage.cpp
   UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
+  PointerAssignments/PointerAssignments.cpp
+  PointerAssignments/PointerAssignmentsExtractor.cpp
 
   LINK_LIBS
   clangAST
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp
index ddbc45301ac4b..e28e0a130a702 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp
@@ -12,6 +12,8 @@
 #include "clang/AST/DeclCXX.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
+#include "llvm/ADT/STLExtras.h"
+#include <functional>
 #include <optional>
 
 namespace clang::ssaf {
@@ -238,17 +240,24 @@ translateEntityPointerLevel(const Expr *E, ASTContext &Ctx,
 }
 
 /// Create an EntityPointerLevel from a ValueDecl of a pointer type.
-Expected<EntityPointerLevel>
+Expected<EntityPointerLevelSet>
 creatEntityPointerLevel(const NamedDecl *D, ASTContext &Ctx,
                         std::function<EntityId(EntityName EN)> AddEntity,
                         bool IsFunRet) {
   EntityPointerLevelTranslator Translator(AddEntity, Ctx);
+  auto EPL = Translator.translate(D, IsFunRet);
 
-  return Translator.translate(D, IsFunRet);
+  if (!EPL)
+    return EPL.takeError();
+  return EntityPointerLevelSet{*EPL};
 }
 
-EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E) {
-  return EntityPointerLevelTranslator::incrementPointerLevel(E);
+EntityPointerLevelSet incrementPointerLevel(const EntityPointerLevelSet &EPLs) {
+  EntityPointerLevelSet Result;
+
+  for (auto &E : EPLs)
+    Result.insert(EntityPointerLevelTranslator::incrementPointerLevel(E));
+  return Result;
 }
 
 // Writes an EntityPointerLevel as
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerAssignments/PointerAssignments.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerAssignments/PointerAssignments.cpp
new file mode 100644
index 0000000000000..8f998ec35abc2
--- /dev/null
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerAssignments/PointerAssignments.cpp
@@ -0,0 +1,105 @@
+//===---------- PointerAssignments.cpp -----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+
+#include "clang/ScalableStaticAnalysisFramework/Analyses/PointerAssignments.h"
+#include "SSAFAnalysesCommon.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
+#include "clang/ScalableStaticAnalysisFramework/SSAFForceLinker.h" // IWYU pragma: keep
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/JSON.h"
+
+namespace {
+constexpr const char *const PointerAssignmentsKey = "PointerAssignments";
+} // namespace
+
+namespace clang::ssaf {
+using Object = llvm::json::Object;
+using Array = llvm::json::Array;
+using Value = llvm::json::Value;
+
+// Writes the 'Edges' map as an array of array of EntityPointerLevels:
+// Array [
+//    Array [ [lhs-node], [rhs-node], [rhs-node], ...]
+//    Array [ [lhs-node], [rhs-node], [rhs-node], ...]
+//    ...
+// ]
+llvm::json::Object PointerAssignmentsEntitySummary::summaryToJSON(
+    const EntitySummary &ES, JSONFormat::EntityIdToJSONFn EntityId2JSON) {
+  Array EdgesData;
+
+  for (const auto &Entry :
+       static_cast<const PointerAssignmentsEntitySummary &>(ES).Edges) {
+    Array EdgesEntryData;
+    EntityPointerLevel LHS = Entry.first;
+
+    EdgesEntryData.push_back(entityPointerLevelToJSON(LHS, EntityId2JSON));
+    // Add to nodes:
+    for (const auto &RHS : Entry.second)
+      EdgesEntryData.push_back(entityPointerLevelToJSON(RHS, EntityId2JSON));
+    EdgesData.push_back(Value(std::move(EdgesEntryData)));
+  }
+
+  Object Data;
+
+  Data[PointerAssignmentsKey] = Value(std::move(EdgesData));
+  return Data;
+}
+
+llvm::Expected<std::unique_ptr<EntitySummary>>
+PointerAssignmentsEntitySummary::summaryFromJSON(
+    const Object &Data, EntityIdTable &,
+    JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) {
+  const Value *EdgesData = Data.get(PointerAssignmentsKey);
+
+  if (!EdgesData)
+    return makeErrorSawButExpected(
+        Object(Data), "a JSON object with the key: %s", PointerAssignmentsKey);
+
+  EdgeSet Edges;
+  const auto *EdgesDataAsArr = EdgesData->getAsArray();
+
+  if (!EdgesDataAsArr)
+    return makeErrorSawButExpected(
+        *EdgesData, "a JSON array of arary of EntityPointerLevels");
+  for (const auto &EdgesEntryData : *EdgesDataAsArr) {
+    const auto *EPLArray = EdgesEntryData.getAsArray();
+
+    if (!EPLArray || EPLArray->size() <= 1)
+      return makeErrorSawButExpected(
+          EdgesEntryData, "a JSON array of EntityPointerLevels with a size "
+                          "greater than 1: [lhs, rhs, rhs, ...]");
+
+    llvm::Error Err = llvm::Error::success();
+    auto EPLs = llvm::map_range(
+        *EPLArray, [&Err, &EntityIdFromJSON](const auto &EPLData) {
+          auto EPL = entityPointerLevelFromJSON(EPLData, EntityIdFromJSON);
+
+          if (!EPL)
+            Err = llvm::joinErrors(std::move(Err), EPL.takeError());
+          return *EPL;
+        });
+
+    if (Err)
+      return Err;
+    Edges[*EPLs.begin()].insert(EPLs.begin() + 1, EPLs.end());
+  }
+  return std::make_unique<PointerAssignmentsEntitySummary>(
+      PointerAssignmentsEntitySummary(std::move(Edges)));
+}
+
+static llvm::Registry<JSONFormat::FormatInfo>::Add<
+    PointerAssignmentsJSONFormatInfo>
+    RegisterPointerAssignmentsJSONFormatInfo(
+        "PointerAssignments",
+        "JSON Format info for PointerAssignmentsEntitySummary");
+
+} // namespace clang::ssaf
+
+// NOLINTNEXTLINE(misc-use-internal-linkage)
+volatile int PointerAssignmentsSSAFJSONFormatAnchorSource = 0;
\ No newline at end of file
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerAssignments/PointerAssignmentsExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerAssignments/PointerAssignmentsExtractor.cpp
new file mode 100644
index 0000000000000..a49e2ffdab441
--- /dev/null
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerAssignments/PointerAssignmentsExtractor.cpp
@@ -0,0 +1,383 @@
+//===---- PointerAssignmentsExtractor.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 "SSAFAnalysesCommon.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTTypeTraits.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/TypeBase.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/PointerAssignments.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.h"
+#include "clang/ScalableStaticAnalysisFramework/SSAFForceLinker.h" // IWYU pragma: keep
+#include "llvm/Support/Error.h"
+#include <functional>
+#include <memory>
+#include <optional>
+
+namespace {
+using namespace clang;
+using namespace ssaf;
+
+class PointerAssignmentMatcher {
+  ASTContext &Ctx;
+  std::function<EntityId(const EntityName &)> AddEntity;
+
+  // Convert a Expr/NamedDecl to an EntityPointerLevel(Set):
+  Expected<EntityPointerLevelSet> toEPL(const NamedDecl *N,
+                                        bool IsRet = false) {
+    auto Ret = creatEntityPointerLevel(N, Ctx, AddEntity, IsRet);
+
+    if (Ret)
+      return EntityPointerLevelSet{*Ret};
+    return Ret.takeError();
+  }
+
+  Expected<EntityPointerLevelSet> toEPL(const Expr *N, bool IsRet = false) {
+    return translateEntityPointerLevel(N, Ctx, AddEntity);
+  }
+
+  template <typename T1, typename T2>
+  bool addEdges(const T1 *LHS, const T2 *RHS) {
+    return addEdges(toEPL(LHS), RHS, false);
+  }
+
+  template <typename T>
+  bool addEdges(Expected<EntityPointerLevelSet> &&LHS, const T *RHS,
+                bool IsRHSRet = false) {
+    auto Rs = toEPL(RHS, IsRHSRet);
+    bool Error = false;
+
+    if (!Rs) {
+      addError(Rs.takeError());
+      Error = true;
+    }
+    if (!LHS) {
+      addError(LHS.takeError());
+      Error = true;
+    }
+    if (Error)
+      return false;
+    for (auto L : *LHS)
+      Results[L].insert(Rs->begin(), Rs->end());
+    return true;
+  }
+
+  template <typename ParmsProvider, typename ArgsProvider>
+  bool matchArgsWithParams(unsigned ArgIdxStart, ParmsProvider *PP,
+                           ArgsProvider *AP) {
+    bool Matched = false;
+    unsigned ArgIdx = ArgIdxStart;
+
+    for (unsigned ParmIdx = 0; ParmIdx < PP->getNumParams();
+         ++ArgIdx, ++ParmIdx) {
+      if (const ParmVarDecl *PD = PP->getParamDecl(ParmIdx);
+          PD && hasPtrOrArrType(*PD)) {
+        addEdges(PD, AP->getArg(ArgIdx));
+        Matched = true;
+      }
+    }
+    return Matched;
+  }
+
+  void addError(llvm::Error &&E) {
+    Error = llvm::joinErrors(std::move(Error), std::move(E));
+  }
+
+  // Match initializer lists of the form 'Var = {a, b, c, ...}':
+  //
+  //   If 'Var' is a struct/union:
+  //     'Var = {a, b, c, ...}' => 'Var.field_1 = a'
+  //                               'Var.field_2 = b'
+  //                               ...
+  //   If 'Var' is an array:
+  //     'Var = {a, b, c, ...}' => '*Var = a'
+  //                               '*Var = b'
+  //                               ...
+  //
+  // The process is recursive: 'a', 'b', 'c', etc. may themselves be
+  // initializer lists.  We therefore use `ArrayIndirectLevel` to keep track.
+  bool matchInitializerList(const ValueDecl *Base, const Expr *InitExpr,
+                            unsigned ArrayElementIndirectLevel = 0) {
+    const InitListExpr *ILE = dyn_cast<InitListExpr>(InitExpr);
+
+    if (!ILE) {
+      if (!hasPtrOrArrType(*InitExpr))
+        return false;
+
+      auto Expected = toEPL(Base);
+
+      if (!Expected) {
+        addError(Expected.takeError());
+        return false;
+      }
+
+      EntityPointerLevelSet LHS = *Expected;
+
+      for (unsigned I = 0; I < ArrayElementIndirectLevel; ++I)
+        LHS = incrementPointerLevel(LHS);
+      return addEdges(LHS, InitExpr);
+    }
+    // Note that `Base`'s type is NOT the real LHS type when
+    // ArrayElementIndirectLevel > 0:
+    QualType Type = InitExpr->getType();
+
+    if (auto *RD = Type->getAsRecordDecl())
+      return matchInitializerListForRecordDecl(RD, ILE);
+    if (Type->isArrayType())
+      return matchInitializerListForArray(Base, ILE, ArrayElementIndirectLevel);
+    // Must be the case of using a initializer-list for a scalar:
+    return matchInitializerList(Base, ILE->getInit(0));
+  }
+
+  // Helper function for matchInitializerList that handles record:
+  bool matchInitializerListForRecordDecl(const RecordDecl *RecordTy,
+                                         const InitListExpr *ILE) {
+    if (auto *CXXRD = dyn_cast<CXXRecordDecl>(RecordTy))
+      if (CXXRD->getNumBases() != 0) {
+        // FIXME: support this:
+        addError(strErrAtNode(
+            Ctx, *ILE,
+            "attempt to create pointer assignment edges between "
+            "CXXRecordDecls with base classes and initializer-lists"));
+        return false;
+      }
+    // Handle union:
+    if (RecordTy->isUnion()) {
+      auto *InitField = ILE->getInitializedFieldInUnion();
+
+      if (!InitField)
+        return false;
+      assert(!ILE->inits().empty());
+      return matchInitializerList(InitField, ILE->getInit(0));
+    }
+    // Handle struct/class:
+    ILE = ILE->getSemanticForm() ? ILE->getSemanticForm() : ILE;
+
+    auto FieldIter = RecordTy->field_begin();
+    bool Matched = false;
+
+    assert(RecordTy->getNumFields() >= ILE->getNumInits());
+    for (auto *Init : ILE->inits()) {
+      if (matchInitializerList(*(FieldIter++), Init))
+        Matched = true;
+    }
+    return Matched;
+  }
+
+  // Helper function for matchInitializerList that handles array:
+  bool matchInitializerListForArray(const ValueDecl *Array,
+                                    const InitListExpr *ILE,
+                                    unsigned ArrayIndirectLevel = 0) {
+    bool Matched = false;
+
+    for (auto *E : ILE->inits())
+      if (matchInitializerList(Array, E, ArrayIndirectLevel + 1))
+        Matched = true;
+    return Matched;
+  }
+
+public:
+  llvm::Error Error;
+  EdgeSet Results;
+
+  PointerAssignmentMatcher(
+      ASTContext &Ctx, std::function<EntityId(const EntityName &)> AddEntity)
+      : Ctx(Ctx), AddEntity(AddEntity), Error(llvm::Error::success()) {}
+
+  // Match and extract assignments.
+  // The extraction function 'XF' can be described by the following rules:
+  //
+  // XF(l = r)               => '(toEPL(l), toEPL(r))'
+  // XF(foo(a, b, ...))      => XF(Param_1 = a), XF(Param_2 = b), ...
+  // XF(return e;)           => XF(Fun = e), where 'Fun' is the enclosing
+  //                                         function
+  // XF(ctor(a, ...) : x1(y1), ... {...})
+  //                         => XF(Param_1 = a), ...,
+  //                            XF(x1 = y1), ...,
+  //                            ctor's body will be visited later.
+  // XF(T var = e)           => XF(Var = e)
+  // XF(T var = init-list)   => see `matchInitializerList`
+  bool matches(const DynTypedNode &DynNode, ASTContext &Ctx,
+               const NamedDecl *RootDecl) {
+    if (const Stmt *S = DynNode.get<Stmt>()) {
+      // Match 'p = q' whenever it has pointer or array type:
+      if (const auto *BO = dyn_cast<BinaryOperator>(S);
+          BO && BO->getOpcode() == BO_Assign && hasPtrOrArrType(*BO)) {
+        return addEdges(BO->getLHS(), BO->getRHS());
+      }
+
+      // Match arg-to-param passing (in CallExpr) for any pointer type argument:
+      if (const auto *CE = dyn_cast<CallExpr>(S)) {
+        const FunctionDecl *FD = CE->getDirectCallee();
+
+        if (!FD)
+          return false;
+
+        unsigned ArgIdx = 0;
+
+        if (isa<CXXOperatorCallExpr>(CE))
+          if (auto *MD = dyn_cast<CXXMethodDecl>(FD);
+              MD && !MD->isExplicitObjectMemberFunction())
+            ArgIdx = 1;
+        return matchArgsWithParams(ArgIdx, FD, CE);
+      }
+      // Match arg-to-param passing (in CXXConstructExpr) for any pointer type
+      // argument:
+      if (const auto *CCE = dyn_cast<CXXConstructExpr>(S)) {
+        return matchArgsWithParams(/*ArgIdxStart=*/0, CCE->getConstructor(),
+                                   CCE);
+      }
+      if (const auto *RS = dyn_cast<ReturnStmt>(S)) {
+        const Expr *RetExpr = RS->getRetValue();
+        if (!hasPtrOrArrType(*RetExpr))
+          return false;
+        return addEdges(toEPL(RootDecl, true), RetExpr, false);
+      }
+    }
+
+    if (const Decl *D = DynNode.get<Decl>()) {
+      const Expr *InitExpr = nullptr;
+
+      if (const auto *VD = dyn_cast<ValueDecl>(D)) {
+        if (const auto *Var = dyn_cast<VarDecl>(VD))
+          InitExpr = Var->getInit();
+        if (const auto *Fd = dyn_cast<FieldDecl>(VD))
+          InitExpr = Fd->getInClassInitializer();
+
+        // Match initializer-list:
+        if (auto *InitLst = dyn_cast_or_null<InitListExpr>(InitExpr))
+          return matchInitializerList(VD, InitLst);
+
+        // Match initializers to variables/fields of a pointer type:
+        if (InitExpr && hasPtrOrArrType(*VD))
+          return addEdges(VD, InitExpr);
+      }
+
+      // Match C++ constructor member-initializers:
+      if (const auto *CtorD = dyn_cast<CXXConstructorDecl>(D)) {
+        bool Matched = false;
+
+        for (auto *E : CtorD->inits()) {
+          if (E->isDelegatingInitializer())
+            return matches(DynTypedNode::create(*E->getInit()), Ctx, RootDecl);
+          if (const FieldDecl *FD = E->getMember();
+              FD && hasPtrOrArrType(*FD)) {
+            addEdges(E->getMember(), E->getInit());
+            Matched = true;
+          }
+        }
+        return Matched;
+      }
+    }
+    return false;
+  }
+};
+} // namespace
+
+namespace clang::ssaf {
+class PointerAssignmentsTUSummaryExtractor : public TUSummaryExtractor {
+public:
+  PointerAssignmentsTUSummaryExtractor(TUSummaryBuilder &Builder)
+      : TUSummaryExtractor(Builder) {}
+
+  EntityId addEntity(const EntityName &EN) {
+    return SummaryBuilder.addEntity(EN);
+  }
+
+  Expected<std::unique_ptr<PointerAssignmentsEntitySummary>>
+  extractEntitySummary(const NamedDecl *Contributor, ASTContext &Ctx) {
+    PointerAssignmentMatcher Matcher(
+        Ctx, [this](const EntityName &EN) { return addEntity(EN); });
+    ContributorFactFinder<PointerAssignmentMatcher> Finder(Ctx, Matcher);
+
+    Finder.findMatches(const_cast<NamedDecl *>(Contributor));
+    if (Matcher.Error)
+      return std::move(Matcher.Error);
+    return std::make_unique<PointerAssignmentsEntitySummary>(
+        PointerAssignmentsEntitySummary(std::move(Matcher.Results)));
+  }
+
+  void HandleTranslationUnit(ASTContext &Ctx) override;
+};
+
+void PointerAssignmentsTUSummaryExtractor::HandleTranslationUnit(
+    ASTContext &Ctx) {
+  llvm::Error Errors = llvm::ErrorSuccess();
+  auto addError = [&Errors](llvm::Error Err) {
+    Errors = llvm::joinErrors(std::move(Errors), std::move(Err));
+  };
+  ContributorFinder ContributorFinder;
+
+  ContributorFinder.VisitTranslationUnitDecl(Ctx.getTranslationUnitDecl());
+  for (auto *CD : ContributorFinder.Contributors) {
+    auto EntitySummary = extractEntitySummary(CD, Ctx);
+
+    if (!EntitySummary) {
+      addError(EntitySummary.takeError());
+      continue;
+    }
+    assert(*EntitySummary &&
+           "std::unique_ptr<EntitySummary> should not be null");
+    if ((*EntitySummary)->empty())
+      continue;
+
+    auto ContributorName = getEntityName(CD);
+
+    if (!ContributorName) {
+      addError(entityNameErrFor(Ctx, *CD));
+      continue;
+    }
+
+    auto [EntitySummaryPtr, Success] = SummaryBuilder.addSummary(
+        addEntity(*ContributorName), std::move(*EntitySummary));
+
+    if (!Success)
+      addError(failedToAddEntitySummaryFor(Ctx, CD));
+  }
+  // FIXME: handle errors!
+  llvm::consumeError(std::move(Errors));
+}
+
+// Proxy functions for unit test:
+extern Expected<std::unique_ptr<PointerAssignmentsEntitySummary>>
+extractEntitySummary(PointerAssignmentsTUSummaryExtractor &Extractor,
+                     const NamedDecl *Contributor, ASTContext &Ctx) {
+  return Extractor.extractEntitySummary(Contributor, Ctx);
+}
+
+extern PointerAssignmentsTUSummaryExtractor *
+createPointerAssignmentsTUSummaryExtractor(TUSummaryBuilder &Builder) {
+  return new PointerAssignmentsTUSummaryExtractor(
+      PointerAssignmentsTUSummaryExtractor(Builder));
+}
+
+extern void deletePointerAssignmentsTUSummaryExtractor(
+    PointerAssignmentsTUSummaryExtractor *Ptr) {
+  delete Ptr;
+}
+
+extern EntityId addEntity(PointerAssignmentsTUSummaryExtractor &Extractor,
+                          EntityName &EN) {
+  return Extractor.addEntity(EN);
+}
+} // namespace clang::ssaf
+
+volatile int PointerAssignmentsTUSummaryExtractorAnchorSource = 0;
+static TUSummaryExtractorRegistry::Add<PointerAssignmentsTUSummaryExtractor>
+    RegisterExtractor("PointerAssignmentsTUSummaryExtractor",
+                      "The TUSummaryExtractor for pointer assignments");
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
index 61e104bc8a0eb..d600c540996cd 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
@@ -14,6 +14,7 @@
 
 #include "clang/AST/Decl.h"
 #include "clang/AST/ParentMapContext.h"
+#include "clang/AST/TypeBase.h"
 #include "clang/Basic/SourceLocation.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/Twine.h"
@@ -56,8 +57,7 @@ static inline llvm::Error makeErrorSawButExpected(const llvm::json::Value &Saw,
       ExpectedArgs...);
 }
 
-template <typename DeclOrExpr>
-static bool hasPtrOrArrType(const DeclOrExpr &E) {
+template <typename TypedObj> static bool hasPtrOrArrType(const TypedObj &E) {
   return llvm::isa<PointerType>(E.getType().getCanonicalType()) ||
          llvm::isa<ArrayType>(E.getType().getCanonicalType());
 }
@@ -143,4 +143,5 @@ class ContributorFactFinder : public DynamicRecursiveASTVisitor {
   }
 };
 } // namespace
-#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_SSAFANALYSESCOMMON_H
\ No newline at end of file
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_SSAFANALYSESCOMMON_H
+
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary.json b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary.json
index 382b294ccc7bd..d4c2aa93373c5 100644
--- a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary.json
+++ b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary.json
@@ -1,5 +1,45 @@
 {
   "data": [
+    {
+      "summary_data": [
+        {
+          "entity_id": 2,
+          "entity_summary": {
+            "PointerAssignments": [
+              [
+                [
+                  {
+                    "@": 0
+                  },
+                  2
+                ],
+                [
+                  {
+                    "@": 1
+                  },
+                  1
+                ]
+              ],
+              [
+                [
+                  {
+                    "@": 2
+                  },
+                  1
+                ],
+                [
+                  {
+                    "@": 0
+                  },
+                  1
+                ]
+              ]
+            ]
+          }
+        }
+      ],
+      "summary_name": "PointerAssignments"
+    },
     {
       "summary_data": [
         {
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerAssignmentsTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerAssignmentsTest.cpp
new file mode 100644
index 0000000000000..790a344986f5e
--- /dev/null
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerAssignmentsTest.cpp
@@ -0,0 +1,1150 @@
+//===- PointerAssignmentsTest.cpp
+//------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/ScalableStaticAnalysisFramework/Analyses/PointerAssignments.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DynamicRecursiveASTVisitor.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummary.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Testing/Support/Error.h"
+#include "gtest/gtest.h"
+#include <memory>
+#include <type_traits>
+#include <variant>
+
+using namespace clang;
+using namespace ssaf;
+
+namespace clang::ssaf {
+/////////////////////////////////////////////////////
+// Declare Proxy functions
+/////////////////////////////////////////////////////
+class PointerAssignmentsTUSummaryExtractor;
+
+extern Expected<std::unique_ptr<PointerAssignmentsEntitySummary>>
+extractEntitySummary(PointerAssignmentsTUSummaryExtractor &Extractor,
+                     const NamedDecl *Contributor, ASTContext &Ctx);
+
+extern PointerAssignmentsTUSummaryExtractor *
+createPointerAssignmentsTUSummaryExtractor(TUSummaryBuilder &Builder);
+
+extern void deletePointerAssignmentsTUSummaryExtractor(
+    PointerAssignmentsTUSummaryExtractor *);
+
+extern EntityId addEntity(PointerAssignmentsTUSummaryExtractor &Extractor,
+                          EntityName &EN);
+
+class PointerAssignmentsTUSummaryExtractorProxy {
+  PointerAssignmentsTUSummaryExtractor *Ptr;
+
+public:
+  explicit PointerAssignmentsTUSummaryExtractorProxy(TUSummaryBuilder &Builder)
+      : Ptr(createPointerAssignmentsTUSummaryExtractor(Builder)) {}
+  ~PointerAssignmentsTUSummaryExtractorProxy() {
+    deletePointerAssignmentsTUSummaryExtractor(Ptr);
+  }
+
+  PointerAssignmentsTUSummaryExtractor &operator*() const { return *Ptr; }
+};
+} // namespace clang::ssaf
+
+namespace {
+// Use FindEntityByName to identify entities in unit tests.
+// Unit tests are simple enough to meet the following assumptions:
+// - Named declarations should have unique names, they can be found by comparing
+//   names with strings;
+// - Lambdas should initialize a variable named "X", they can be found using
+//   "LambdaOfVar("X")";
+// - CXX Ctors should have unique combination of names and number of parameters,
+//   they can be found using "CXXCtorOfNumParms(name, numParms)".
+struct LambdaOfVar {
+  StringRef VarName;
+};
+
+struct CXXCtorOfNumParms {
+  StringRef CXXCtorName;
+  unsigned NumParms;
+};
+
+using FindEntityByName =
+    std::variant<StringRef, CXXCtorOfNumParms, LambdaOfVar>;
+
+template <typename... Ts> struct Overloaded : Ts... {
+  using Ts::operator()...;
+};
+template <typename... Ts> Overloaded(Ts...) -> Overloaded<Ts...>;
+
+StringRef toStringRef(const FindEntityByName &N) {
+  return std::visit(
+      Overloaded{
+          [](StringRef S) -> StringRef { return S; },
+          [](const CXXCtorOfNumParms &L) -> StringRef { return L.CXXCtorName; },
+          [](const LambdaOfVar &L) -> StringRef { return L.VarName; },
+      },
+      N);
+}
+
+const NamedDecl *matchNamedDeclByFindEntityByName(const FindEntityByName &N,
+                                                  const NamedDecl *D) {
+  if (std::holds_alternative<LambdaOfVar>(N))
+    D->dump();
+  return std::visit(
+      Overloaded{
+          [&D](StringRef S) -> const NamedDecl * {
+            if (D->getNameAsString() == S)
+              return D;
+            return nullptr;
+          },
+          [&D](const CXXCtorOfNumParms &L) -> const NamedDecl * {
+            if (auto *CD = dyn_cast<CXXConstructorDecl>(D)) {
+              if (CD->getNameAsString() == L.CXXCtorName &&
+                  CD->getNumParams() == L.NumParms)
+                return D;
+            }
+            return nullptr;
+          },
+          [&D](const LambdaOfVar &L) -> const NamedDecl * {
+            if (const auto *VD = dyn_cast<VarDecl>(D); VD && VD->getInit()) {
+              VD->dump();
+              if (isa<LambdaExpr>(VD->getInit()) &&
+                  VD->getNameAsString() == L.VarName)
+                return cast<LambdaExpr>(VD->getInit())->getCallOperator();
+            }
+            return nullptr;
+          },
+      },
+      N);
+}
+
+template <typename SomeDecl = NamedDecl,
+          typename = std::enable_if_t<std::is_base_of_v<NamedDecl, SomeDecl>>>
+const SomeDecl *findEntityByName(FindEntityByName Name, ASTContext &Ctx) {
+  class NamedDeclFinder : public DynamicRecursiveASTVisitor {
+  public:
+    FindEntityByName SearchingName;
+    const SomeDecl *FoundDecl = nullptr;
+
+    NamedDeclFinder(FindEntityByName SearchingName)
+        : SearchingName(SearchingName) {}
+
+    bool VisitDecl(Decl *D) override {
+      if (auto *ND = dyn_cast<NamedDecl>(D)) {
+        FoundDecl = llvm::dyn_cast_or_null<SomeDecl>(
+            matchNamedDeclByFindEntityByName(SearchingName, ND));
+        if (FoundDecl)
+          return false;
+      }
+      return true;
+    }
+  };
+
+  NamedDeclFinder Finder(Name);
+
+  Finder.TraverseDecl(Ctx.getTranslationUnitDecl());
+  return dyn_cast_or_null<SomeDecl>(Finder.FoundDecl);
+}
+
+const FunctionDecl *findFnByName(FindEntityByName Name, ASTContext &Ctx) {
+  return findEntityByName<FunctionDecl>(Name, Ctx);
+}
+
+// Same as `std::pair<StringName, unsigned>` for a pair of entity declaration
+// name and a pointer level with an extra optional flag for whether the entity
+// represents a function return value. This structure is used to explicitly
+// spell out components of an EPL such as "{"p", 1}" or "{"foo_fn", 2, true}".
+struct EPLPair {
+  EPLPair(FindEntityByName Name, unsigned Lv, bool isFunRet = false)
+      : Name(Name), Lv(Lv), isFunRet(isFunRet) {}
+
+  FindEntityByName Name;
+  unsigned Lv;
+  bool isFunRet;
+};
+
+class PointerAssignmentsTest : public testing::Test {
+protected:
+  TUSummary TUSum;
+  TUSummaryBuilder Builder;
+  PointerAssignmentsTUSummaryExtractorProxy ExtractorProxy;
+  std::unique_ptr<ASTUnit> AST;
+
+  PointerAssignmentsTest()
+      : TUSum(BuildNamespace(BuildNamespaceKind::CompilationUnit, "Mock.cpp")),
+        Builder(TUSum), ExtractorProxy(Builder) {}
+
+  template <typename ContributorDecl = NamedDecl,
+            typename =
+                std::enable_if_t<std::is_base_of_v<NamedDecl, ContributorDecl>>>
+  std::unique_ptr<PointerAssignmentsEntitySummary>
+  setUpTest(StringRef Code, FindEntityByName ContributorName) {
+    AST = tooling::buildASTFromCodeWithArgs(
+        Code, {"-Wno-unused-value", "-Wno-int-to-pointer-cast"});
+
+    const auto *ContributorDefn = findEntityByName<ContributorDecl>(
+        ContributorName, AST->getASTContext());
+
+    if (!ContributorDefn)
+      return nullptr;
+
+    std::optional<EntityName> EN = getEntityName(ContributorDefn);
+
+    if (!EN)
+      return nullptr;
+
+    auto Sum = extractEntitySummary(*ExtractorProxy, ContributorDefn,
+                                    AST->getASTContext());
+    if (!Sum) {
+      llvm::consumeError(std::move(Sum.takeError()));
+      return nullptr;
+    }
+    assert(*Sum);
+    return std::move(*Sum);
+  }
+
+public:
+  std::optional<EntityId> getEntityId(FindEntityByName Name) {
+    if (const auto *D = findEntityByName(Name, AST->getASTContext())) {
+      if (auto EntityName = getEntityName(D))
+        return addEntity(*ExtractorProxy, *EntityName);
+    }
+    return std::nullopt;
+  }
+
+  std::optional<EntityId> getEntityIdForReturn(FindEntityByName FunName) {
+    if (const auto *D = findFnByName(FunName, AST->getASTContext())) {
+      if (auto EntityName = getEntityNameForReturn(D))
+        return addEntity(*ExtractorProxy, *EntityName);
+    }
+    return std::nullopt;
+  }
+
+  EdgeSet makeEdges(unsigned Line, ArrayRef<std::pair<EPLPair, EPLPair>> Edges);
+};
+
+// 'ToEPL(Test, Line)' is a lambda that converts a 'EPLPair' to a
+// 'EntityPointerLevel':
+static constexpr auto ToEPL =
+    [](PointerAssignmentsTest *Test,
+       unsigned Line) -> std::function<EntityPointerLevel(const EPLPair &)> {
+  return [Test, Line](const EPLPair &Pair) -> EntityPointerLevel {
+    std::optional<EntityId> Entity = Pair.isFunRet
+                                         ? Test->getEntityIdForReturn(Pair.Name)
+                                         : Test->getEntityId(Pair.Name);
+    if (!Entity) {
+      ADD_FAILURE_AT(__FILE__, Line)
+          << "Entity not found: " << toStringRef(Pair.Name);
+    }
+    return buildEntityPointerLevel(*Entity, Pair.Lv);
+  };
+};
+
+EdgeSet
+PointerAssignmentsTest::makeEdges(unsigned Line,
+                                  ArrayRef<std::pair<EPLPair, EPLPair>> Edges) {
+  EdgeSet Result;
+  for (auto Edge : Edges)
+    Result[ToEPL(this, Line)(Edge.first)].insert(
+        ToEPL(this, Line)(Edge.second));
+  return Result;
+}
+
+TEST_F(PointerAssignmentsTest, IsExtractorRegisteredTest) {
+  EXPECT_TRUE(
+      isTUSummaryExtractorRegistered("PointerAssignmentsTUSummaryExtractor"));
+}
+
+TEST_F(PointerAssignmentsTest, IsJSONFormatRegistered) {
+  std::set<llvm::StringRef> ActualNames;
+  for (const auto &Entry :
+       llvm::Registry<clang::ssaf::JSONFormat::FormatInfo>::entries()) {
+    bool Inserted = ActualNames.insert(Entry.getName()).second;
+    EXPECT_TRUE(Inserted);
+  }
+
+  EXPECT_TRUE(ActualNames.count("PointerAssignments") == 1);
+}
+
+//////////////////////////////////////////////////////////////
+//                     JSON Tests                           //
+//////////////////////////////////////////////////////////////
+// Oracle JSON output for the example:
+// void foo(int ***p, int ****q, int x, int ****r) {
+//   p[5][5][5];
+//   q[5][5][5][5];
+//   q[x] = p;
+//   r = q;
+// }
+constexpr const char *const SerilizationTestOracle = R"cpp({
+  "PointerAssignments": [
+    [
+      [
+        {
+          "@": 108
+        },
+        2
+      ],
+      [
+        {
+          "@": 42
+        },
+        1
+      ]
+    ],
+    [
+      [
+        {
+          "@": 9
+        },
+        1
+      ],
+      [
+        {
+          "@": 108
+        },
+        1
+      ]
+    ]
+  ]
+})cpp";
+
+TEST_F(PointerAssignmentsTest, SerializeTest) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int ***p, int ****q, int x, int ****r) {
+      p[5][5][5];
+      q[5][5][5][5];
+      q[x] = p;
+      r = q;
+    }
+  )cpp",
+                       "foo");
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+                                          /*[0]=*/{{"q", 2U}, {"p", 1U}},
+                                          /*[1]=*/{{"r", 1U}, {"q", 1U}},
+                                      }));
+
+  using Object = llvm::json::Object;
+  using Value = llvm::json::Value;
+  std::map<EntityId, uint64_t> DummyTable{{*getEntityId("p"), 42},
+                                          {*getEntityId("q"), 108},
+                                          {*getEntityId("r"), 9}};
+
+  Object JData = PointerAssignmentsEntitySummary::summaryToJSON(
+      *Sum, [&DummyTable](EntityId Id) {
+        return Object{{"@", Value(DummyTable[Id])}};
+      });
+
+  EXPECT_EQ(llvm::formatv("{0:2}", llvm::json::Value(std::move(JData))).str(),
+            SerilizationTestOracle);
+}
+
+TEST_F(PointerAssignmentsTest, DeserializeTest) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int ***p, int ****q, int x, int ****r) {
+      p[5][5][5];
+      q[5][5][5][5];
+      q[x] = p;
+      r = q;
+    }
+  )cpp",
+                       "foo");
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+                                          /*[0]=*/{{"q", 2U}, {"p", 1U}},
+                                          /*[1]=*/{{"r", 1U}, {"q", 1U}},
+                                      }));
+
+  using Object = llvm::json::Object;
+  using Value = llvm::json::Value;
+  std::map<uint64_t, EntityId> DummyTable{{42, *getEntityId("p")},
+                                          {108, *getEntityId("q")},
+                                          {9, *getEntityId("r")}};
+  Expected<Value> ParsedJSON = llvm::json::parse(SerilizationTestOracle);
+
+  ASSERT_THAT_EXPECTED(ParsedJSON, llvm::Succeeded());
+  ASSERT_NE(ParsedJSON->getAsObject(), nullptr);
+
+  EntityIdTable Ignored;
+  auto ParsedSum = PointerAssignmentsEntitySummary::summaryFromJSON(
+      *ParsedJSON->getAsObject(), Ignored,
+      [&DummyTable](const Object &O) -> Expected<EntityId> {
+        return DummyTable.at(O.getInteger("@").value());
+      });
+
+  ASSERT_THAT_EXPECTED(ParsedSum, llvm::Succeeded());
+  EXPECT_EQ(*static_cast<PointerAssignmentsEntitySummary *>(ParsedSum->get()),
+            *Sum);
+}
+
+//////////////////////////////////////////////////////////////
+//          Simple Assign Tests                             //
+//////////////////////////////////////////////////////////////
+TEST_F(PointerAssignmentsTest, SimpleAssign) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int *p, int *q) {
+      q = p;
+    }
+  )cpp",
+                       "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, AssignWithSubscriptLHS) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int **q, int *p, int x) {
+      q[x] = p;
+    }
+  )cpp",
+                       "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 2U}, {"p", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, AssignWithPtrArithRHS) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int *p, int *q) {
+      q = p + 5;
+    }
+  )cpp",
+                       "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, AssignInSubscript) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int *p, int *q) {
+      (q = p)[5];
+    }
+  )cpp",
+                       "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, MultipleAssign) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int *p, int *q, int *r) {
+      q = p;
+      r = q;
+    }
+  )cpp",
+                       "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+                                          {{"q", 1U}, {"p", 1U}},
+                                          {{"r", 1U}, {"q", 1U}},
+                                      }));
+}
+
+TEST_F(PointerAssignmentsTest, ChainedAssign) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int *p, int *q, int *r) {
+      r = q = p;
+    }
+  )cpp",
+                       "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+                                          {{"q", 1U}, {"p", 1U}},
+                                          {{"r", 1U}, {"q", 1U}},
+                                      }));
+}
+
+TEST_F(PointerAssignmentsTest, CastToRValue) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int *p, int *q) {
+      q = static_cast<int *&&>(p);
+    }
+  )cpp",
+                       "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, AssignToMember) {
+  auto Sum = setUpTest(R"cpp(
+    struct S { int *field; };
+    void foo(S s, int *p) {
+      s.field = p;
+    }
+  )cpp",
+                       "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"field", 1U}, {"p", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, AssignToMember2) {
+  auto Sum = setUpTest(R"cpp(
+    struct S { int *field; };
+    void foo(S *s, int *p) {
+      s->field = p;
+    }
+  )cpp",
+                       "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"field", 1U}, {"p", 1U}}}));
+}
+
+//////////////////////////////////////////////////////////////
+//          Call Expr Tests.                                //
+//////////////////////////////////////////////////////////////
+TEST_F(PointerAssignmentsTest, CallArg) {
+  auto Sum = setUpTest(R"cpp(
+    void bar(int *param);
+    void foo(int *p) {
+      bar(p);
+    }
+  )cpp",
+                       "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"param", 1U}, {"p", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, CallMultiArgs) {
+  auto Sum = setUpTest(R"cpp(
+    void bar(int *param1, int y, int *param2);
+    void foo(int *p, int x, int *q) {
+      bar(p, x, q);
+    }
+  )cpp",
+                       "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+                                          {{"param1", 1U}, {"p", 1U}},
+                                          {{"param2", 1U}, {"q", 1U}},
+                                      }));
+}
+
+TEST_F(PointerAssignmentsTest, CallAsCallArg) {
+  auto Sum = setUpTest(R"cpp(
+
+    int *bar(int * w);
+    void foo(int * p) {
+      foo(bar(p));
+    }
+  )cpp",
+                       "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"w", 1U}, {"p", 1U}},
+                                       {{"p", 1U}, {"bar", 1U, true}}}));
+}
+
+TEST_F(PointerAssignmentsTest, CXXOperatorCallMultiArgs) {
+  auto Sum = setUpTest(R"cpp(
+    struct S {
+      int* operator()(int *a, int *b);
+    };
+    void foo(S obj, int *p, int *q) {
+      foo(obj, obj(p, q), obj(p, q));
+    }
+  )cpp",
+                       "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+                                          {{"a", 1U}, {"p", 1U}},
+                                          {{"b", 1U}, {"q", 1U}},
+                                          {{"p", 1U}, {"operator()", 1U, true}},
+                                          {{"q", 1U}, {"operator()", 1U, true}},
+                                      }));
+}
+
+TEST_F(PointerAssignmentsTest, CXXMemberCall) {
+  auto Sum = setUpTest(R"cpp(
+    struct S {
+      int* method(int *a, int *b);
+    };
+    void foo(S obj, int *p, int *q) {
+      foo(obj, obj.method(p, q), obj.method(p, q));
+    }
+  )cpp",
+                       "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"a", 1U}, {"p", 1U}},
+                                       {{"b", 1U}, {"q", 1U}},
+                                       {{"p", 1U}, {"method", 1U, true}},
+                                       {{"q", 1U}, {"method", 1U, true}}}));
+}
+
+TEST_F(PointerAssignmentsTest, VirtualMethodCall) {
+  auto Sum = setUpTest(R"cpp(
+    struct Base {
+      virtual void method(int *a);
+    };
+    void foo(Base &obj, int *p) {
+      obj.method(p);
+    }
+  )cpp",
+                       "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"a", 1U}, {"p", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, StaticMethodCall) {
+  auto Sum = setUpTest(R"cpp(
+    struct S {
+      static void method(int *a, int *b);
+    };
+    void foo(int *p, int *q) {
+      S::method(p, q);
+    }
+  )cpp",
+                       "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+                                          {{"a", 1U}, {"p", 1U}},
+                                          {{"b", 1U}, {"q", 1U}},
+                                      }));
+}
+
+TEST_F(PointerAssignmentsTest, DefaultArg) {
+  auto Sum = setUpTest(R"cpp(
+    int *g;
+    void bar(int *a, int *b = g);
+    void foo(int *p) {
+      bar(p);
+    }
+  )cpp",
+                       "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__,
+                            {{{"a", 1U}, {"p", 1U}}, {{"b", 1U}, {"g", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, DefaultArg2) {
+  auto Sum = setUpTest(R"cpp(
+    int *g;
+    void bar(int *a, int *b = g);
+    void foo(int *p) {
+      bar(p, p);
+    }
+  )cpp",
+                       "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+                                          {{"a", 1U}, {"p", 1U}},
+                                          {{"b", 1U}, {"p", 1U}},
+                                      }));
+}
+
+//////////////////////////////////////////////////////////////
+//          CXX Ctor Tests.                                 //
+//////////////////////////////////////////////////////////////
+TEST_F(PointerAssignmentsTest, CXXCtorCallMultiArgs) {
+  auto Sum = setUpTest(R"cpp(
+    struct S {
+      S(int *a, int *b) {}
+    };
+    void foo(int *p, int *q) {
+      S s{p, q};
+    }
+  )cpp",
+                       "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+                                          {{"a", 1U}, {"p", 1U}},
+                                          {{"b", 1U}, {"q", 1U}},
+                                      }));
+}
+
+TEST_F(PointerAssignmentsTest, CXXCtorCallMultiArgs2) {
+  auto Sum = setUpTest(R"cpp(
+    struct S {
+      S(int *a, int x, int *b) {}
+    };
+    void foo(int *p, int x, int *q) {
+      S s{p, x, q};
+    }
+  )cpp",
+                       "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+                                          {{"a", 1U}, {"p", 1U}},
+                                          {{"b", 1U}, {"q", 1U}},
+                                      }));
+}
+
+TEST_F(PointerAssignmentsTest, CXXCtorCallAsCallArg) {
+  auto Sum = setUpTest(R"cpp(
+    struct Wrapper {
+      Wrapper(int *q) {}
+    };
+    void bar(Wrapper w);
+    void foo(int *p) {
+      bar(Wrapper{p});
+    }
+  )cpp",
+                       "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, DelegatingCXXCtorCall) {
+  auto Sum = setUpTest<CXXConstructorDecl>(R"cpp(
+    struct S {
+      S(int *a, int *b) {}
+      S(int *p) : S(p, p) {}
+    };
+  )cpp",
+                                           CXXCtorOfNumParms{"S", 1});
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+                                          {{"a", 1U}, {"p", 1U}},
+                                          {{"b", 1U}, {"p", 1U}},
+                                      }));
+}
+
+TEST_F(PointerAssignmentsTest, CXXCtorBaseInit) {
+  auto Sum = setUpTest<CXXConstructorDecl>(R"cpp(
+    struct Base {
+      Base(int *a) {}
+    };
+    struct Derived : Base {
+      Derived(int *p) : Base(p) {}
+    };
+  )cpp",
+                                           "Derived");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"a", 1U}, {"p", 1U}}}));
+}
+
+//////////////////////////////////////////////////////////////
+//          Initializers Tests.                             //
+//////////////////////////////////////////////////////////////
+TEST_F(PointerAssignmentsTest, LocalVarDeclInit) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int *p) {
+      int *q = p;
+    }
+  )cpp",
+                       "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, LocalVarDeclInit2) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int (*arr)[10]) {
+      int (*p)[10] = arr;
+    }
+  )cpp",
+                       "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"p", 1U}, {"arr", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, FieldInit) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int *p) {
+      struct Bar {
+        int *field = p;
+      };
+    }
+  )cpp",
+                       "Bar");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"field", 1U}, {"p", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, CXXCtorMemberInit) {
+  StringRef Code = R"cpp(
+    void foo(int *p) {
+      struct Bar {
+        int *member;
+        Bar(int *q) : member(q) {}
+      };
+      Bar B{p};
+    }
+  )cpp";
+
+  auto Sum = setUpTest<CXXConstructorDecl>(Code, "Bar");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"member", 1U}, {"q", 1U}}}));
+
+  Sum = setUpTest(Code, "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, GlobalVarInit) {
+  auto Sum = setUpTest<VarDecl>(R"cpp(
+    int *q;
+    int *g = q;
+  )cpp",
+                                "g");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"g", 1U}, {"q", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, StaticLocalInit) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int *p) {
+      static int *s = p;
+    }
+  )cpp",
+                       "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"s", 1U}, {"p", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, StaticMemberInit) {
+  auto Sum = setUpTest<VarDecl>(R"cpp(
+    int *g;
+    struct S { static int *member = g; };   
+  )cpp",
+                                "member");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"member", 1U}, {"g", 1U}}}));
+}
+
+//////////////////////////////////////////////////////////////
+//              InitList Tests.                               //
+//////////////////////////////////////////////////////////////
+
+TEST_F(PointerAssignmentsTest, ArrayInitList) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int *p, int *q) {
+      int *arr[] = {p, q};
+    }
+  )cpp",
+                       "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+                                          {{"arr", 2U}, {"p", 1U}},
+                                          {{"arr", 2U}, {"q", 1U}},
+                                      }));
+}
+
+TEST_F(PointerAssignmentsTest, StructInitList) {
+  auto Sum = setUpTest(R"cpp(
+    struct S { int *a; int *b; };
+    void foo(int *p, int *q) {
+      S s = {p, q};
+    }
+  )cpp",
+                       "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+                                          {{"a", 1U}, {"p", 1U}},
+                                          {{"b", 1U}, {"q", 1U}},
+                                      }));
+}
+
+// A union initialized with a brace-enclosed initializer:
+TEST_F(PointerAssignmentsTest, UnionInitList) {
+  auto Sum = setUpTest(R"cpp(
+    union U { int *x; int y; };
+    void foo(int *p) {
+      U u = {p};
+    }
+  )cpp",
+                       "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"x", 1U}, {"p", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, NestedInitList) {
+  auto Sum = setUpTest(R"cpp(
+    struct Inner { int * a; int * b; };
+    struct S { Inner c; int * d; };
+    void foo(int *p, int *q, int *r) {
+      S s = {{p, q}, r};
+    }
+  )cpp",
+                       "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+                                          {{"a", 1U}, {"p", 1U}},
+                                          {{"b", 1U}, {"q", 1U}},
+                                          {{"d", 1U}, {"r", 1U}},
+                                      }));
+}
+
+TEST_F(PointerAssignmentsTest, NestedInitList2) {
+  auto Sum = setUpTest(R"cpp(
+    union Inner { int * a; int b; };
+    struct S { Inner c; int * d; };
+    void foo(int *p, int *q) {
+      S s = {{p}, q};
+    }
+  )cpp",
+                       "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+                                          {{"a", 1U}, {"p", 1U}},
+                                          {{"d", 1U}, {"q", 1U}},
+                                      }));
+}
+
+TEST_F(PointerAssignmentsTest, NestedInitList3) {
+  auto Sum = setUpTest(R"cpp(
+    struct Inner { int * a; int * b; };
+    union S { Inner c; int * d; };
+    void foo(int *p, int *q) {
+      S s = {{p, q}};
+    }
+  )cpp",
+                       "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+                                          {{"a", 1U}, {"p", 1U}},
+                                          {{"b", 1U}, {"q", 1U}},
+                                      }));
+}
+
+TEST_F(PointerAssignmentsTest, NestedArrayInitList) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int *p, int *q, int *r, int *s) {
+      int *arr[][2] = {{p, q}, {r, s}};
+    }
+  )cpp",
+                       "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+                                          {{"arr", 3U}, {"p", 1U}},
+                                          {{"arr", 3U}, {"q", 1U}},
+                                          {{"arr", 3U}, {"r", 1U}},
+                                          {{"arr", 3U}, {"s", 1U}},
+                                      }));
+}
+
+TEST_F(PointerAssignmentsTest, MixedNestedArrayStructInitList) {
+  auto Sum = setUpTest(R"cpp(
+    struct T { int *arr[2]; };
+    void foo(int *p, int *q, int *r, int *s) {
+      T t[2] = {{p, q}, {r, s}};
+    }
+  )cpp",
+                       "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+                                          {{"arr", 2U}, {"p", 1U}},
+                                          {{"arr", 2U}, {"q", 1U}},
+                                          {{"arr", 2U}, {"r", 1U}},
+                                          {{"arr", 2U}, {"s", 1U}},
+                                      }));
+}
+
+TEST_F(PointerAssignmentsTest, ArrayOfStructInitList) {
+  auto Sum = setUpTest(R"cpp(
+    struct S { int *a; int *b; };
+    void foo(int *p, int *q, int *r, int *s) {
+      S arr[] = {{p, q}, {r, s}};
+    }
+  )cpp",
+                       "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+                                          {{"a", 1U}, {"p", 1U}},
+                                          {{"b", 1U}, {"q", 1U}},
+                                          {{"a", 1U}, {"r", 1U}},
+                                          {{"b", 1U}, {"s", 1U}},
+                                      }));
+}
+
+//////////////////////////////////////////////////////////////
+//              Return Tests.                               //
+//////////////////////////////////////////////////////////////
+
+TEST_F(PointerAssignmentsTest, ReturnEdge) {
+  auto Sum = setUpTest(R"cpp(
+    int *foo(int *p) {
+      return p;
+    }
+  )cpp",
+                       "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"foo", 1U, true}, {"p", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, MultipleReturnEdges) {
+  auto Sum = setUpTest(R"cpp(
+    int *foo(int *p, int *q, bool cond) {
+      if (cond)
+        return p;
+      return q;
+    }
+  )cpp",
+                       "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+                                          {{"foo", 1U, true}, {"p", 1U}},
+                                          {{"foo", 1U, true}, {"q", 1U}},
+                                      }));
+}
+
+TEST_F(PointerAssignmentsTest, NoReturnEdgeForNonPointerReturnType) {
+  auto Sum = setUpTest(R"cpp(
+    int foo(int *p, int x) {
+      return x;
+    }
+  )cpp",
+                       "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {}));
+}
+
+TEST_F(PointerAssignmentsTest, ReturnEdgeNotFromNestedFunction) {
+  auto *Code = R"cpp(
+    int *foo(int *p) {
+      struct Inner {
+        int *bar(int *q) { return q; }
+      };
+      return p;
+    }
+  )cpp";
+  auto Sum = setUpTest(Code, "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"foo", 1U, true}, {"p", 1U}}}));
+
+  Sum = setUpTest(Code, "bar");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"bar", 1U, true}, {"q", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, ReturnEdgeInClassMethod) {
+  auto Sum = setUpTest(R"cpp(
+  void foo() {
+    struct S {
+      int *method(int *p, int *q) { return p; }
+    };
+  }
+  )cpp",
+                       "method");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"method", 1U, true}, {"p", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, NoEdgeFromIndirectCall) {
+  auto Sum = setUpTest(R"cpp(
+    void bar(int *param1);
+    void baz(int *param2);
+    
+    void foo(int *p, void (*fp)(int *)) {
+      fp(p);
+    }
+
+    void main() {
+      int *q;
+      foo(q, bar);
+      foo(q, baz);
+    }
+  )cpp",
+                       "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(
+      *Sum,
+      makeEdges(
+          __LINE__,
+          /* FIXME or TBD: Currently indirect calls produce no edge: */ {}));
+}
+
+//////////////////////////////////////////////////////////////
+//          Lambda Tests.                                   //
+//////////////////////////////////////////////////////////////
+
+TEST_F(PointerAssignmentsTest, ReturnInLambda) {
+  StringRef Code = R"cpp(
+    int* foo(int *p) {
+      auto local = [](int *r) { return r; };
+      return local(p);
+    }
+  )cpp";
+
+  auto Sum = setUpTest(Code, "foo");
+
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"r", 1U}, {"p", 1U}},
+                                       {{"foo", 1U, true},
+                                        {LambdaOfVar{"local"}, 1U, true}}}));
+
+  Sum = setUpTest(Code, LambdaOfVar{"local"});
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__,
+                            {{{LambdaOfVar{"local"}, 1U, true}, {"r", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, NestedLambdaAssign) {
+  StringRef Code = R"cpp(
+    void foo() {
+      auto outer_lambda = [](int *r, int *s) {
+        s = r;
+        auto inner_lambda = [](int *x, int *y) { y = x; };
+      };
+    }
+  )cpp";
+
+  auto Sum = setUpTest(Code, LambdaOfVar{"outer_lambda"});
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"s", 1U}, {"r", 1U}}}));
+
+  Sum = setUpTest(Code, LambdaOfVar{"inner_lambda"});
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"y", 1U}, {"x", 1U}}}));
+}
+} // namespace
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt b/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
index 5da36d071be32..e8c8918c7732a 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
+++ b/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
@@ -1,5 +1,6 @@
 add_distinct_clang_unittest(ClangScalableAnalysisTests
   Analyses/UnsafeBufferUsageTest.cpp
+  Analyses/PointerAssignmentsTest.cpp
   ASTEntityMappingTest.cpp
   BuildNamespaceTest.cpp
   EntityIdTableTest.cpp
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Registries/SummaryExtractorRegistryTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Registries/SummaryExtractorRegistryTest.cpp
index 28f4d089b5764..e6e21d3610c7f 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Registries/SummaryExtractorRegistryTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Registries/SummaryExtractorRegistryTest.cpp
@@ -42,6 +42,7 @@ TEST(SummaryExtractorRegistryTest, EnumeratingRegistryEntries) {
                              "MockSummaryExtractor1",
                              "MockSummaryExtractor2",
                              "NoOpExtractor",
+                             "PointerAssignmentsTUSummaryExtractor",
                              "UnsafeBufferUsageTUSummaryExtractor",
                          }));
 }

>From 8b954b935685f75679ec6a595592db2f4cb177d2 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Tue, 7 Apr 2026 17:06:18 -0700
Subject: [PATCH 2/5] rename to PointerFlow

---
 .../Analyses/EntityPointerLevel.h             | 134 ---------------
 .../EntityPointerLevel/EntityPointerLevel.h   |   2 +-
 .../PointerFlow.h}                            |  32 ++--
 .../SSAFBuiltinForceLinker.h                  |  17 +-
 .../Analyses/CMakeLists.txt                   |   4 +-
 .../EntityPointerLevel/EntityPointerLevel.cpp |   4 +-
 .../PointerFlow.cpp}                          |  39 +++--
 .../PointerFlowExtractor.cpp}                 |  66 +++----
 .../UnsafeBufferUsage/Inputs/tu-summary.json  |   4 +-
 .../PointerFlowTest.cpp}                      | 161 +++++++++---------
 .../CMakeLists.txt                            |   2 +-
 11 files changed, 167 insertions(+), 298 deletions(-)
 delete mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
 rename clang/include/clang/ScalableStaticAnalysisFramework/Analyses/{PointerAssignments.h => PointerFlow/PointerFlow.h} (67%)
 rename clang/lib/ScalableStaticAnalysisFramework/Analyses/{PointerAssignments/PointerAssignments.cpp => PointerFlow/PointerFlow.cpp} (71%)
 rename clang/lib/ScalableStaticAnalysisFramework/Analyses/{PointerAssignments/PointerAssignmentsExtractor.cpp => PointerFlow/PointerFlowExtractor.cpp} (86%)
 rename clang/unittests/ScalableStaticAnalysisFramework/Analyses/{PointerAssignmentsTest.cpp => PointerFlow/PointerFlowTest.cpp} (87%)

diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
deleted file mode 100644
index 012fdf20aa61e..0000000000000
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
+++ /dev/null
@@ -1,134 +0,0 @@
-//===---------------- EntityPointerLevel.h ----------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_H
-#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_H
-
-#include "clang/AST/Decl.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
-#include <set>
-
-namespace clang::ssaf {
-
-/// An EntityPointerLevel is associated with a level of the declared
-/// pointer/array type of an entity.  In the fully-expanded spelling of the
-/// declared type, a EntityPointerLevel is associated with a '*' (or a '[]`) in
-/// that declaration.
-///
-/// For example, for 'int *p[10];', there are two EntityPointerLevels. One
-/// is associated with 'int *[10]' of 'p' and the other is associated with 'int
-/// *' of 'p'.
-///
-/// An EntityPointerLevel can be identified by an EntityId and an unsigned
-/// integer indicating the pointer level: '(EntityId, PointerLevel)'.
-/// An EntityPointerLevel 'P' is valid iff 'P.EntityId' has a pointer type with
-/// at least 'P.PointerLevel' levels (This implies 'P.PointerLevel > 0').
-///
-/// For the same example 'int *p[10];', the EntityPointerLevels below are valid:
-/// - '(p, 2)' is associated with the 'int *' part of the declared type of 'p';
-/// - '(p, 1)' is associated with the 'int *[10]' part of the declared type of
-/// 'p'.
-class EntityPointerLevel {
-  EntityId Entity;
-  unsigned PointerLevel;
-
-  friend class EntityPointerLevelTranslator;
-  friend llvm::Expected<EntityPointerLevel>
-  entityPointerLevelFromJSON(const llvm::json::Value &EPLData,
-                             JSONFormat::EntityIdFromJSONFn EntityIdFromJSON);
-  // For unittests:
-  friend EntityPointerLevel buildEntityPointerLevel(EntityId, unsigned);
-
-  // EntityPointerLevel(EntityId Entity, unsigned PointerLevel)
-  //     : Entity(Entity), PointerLevel(PointerLevel) {}
-  EntityPointerLevel(std::pair<EntityId, unsigned> Pair)
-      : Entity(Pair.first), PointerLevel(Pair.second) {}
-
-public:
-  EntityId getEntity() const { return Entity; }
-  unsigned getPointerLevel() const { return PointerLevel; }
-
-  bool operator==(const EntityPointerLevel &Other) const {
-    return std::tie(Entity, PointerLevel) ==
-           std::tie(Other.Entity, Other.PointerLevel);
-  }
-
-  bool operator!=(const EntityPointerLevel &Other) const {
-    return !(*this == Other);
-  }
-
-  bool operator<(const EntityPointerLevel &Other) const {
-    return std::tie(Entity, PointerLevel) <
-           std::tie(Other.Entity, Other.PointerLevel);
-  }
-
-  /// Compares `EntityPointerLevel`s; additionally, partially compares
-  /// `EntityPointerLevel` with `EntityId`.
-  struct Comparator {
-    using is_transparent = void;
-    bool operator()(const EntityPointerLevel &L,
-                    const EntityPointerLevel &R) const {
-      return L < R;
-    }
-    bool operator()(const EntityId &L, const EntityPointerLevel &R) const {
-      return L < R.getEntity();
-    }
-    bool operator()(const EntityPointerLevel &L, const EntityId &R) const {
-      return L.getEntity() < R;
-    }
-  };
-};
-
-using EntityPointerLevelSet =
-    std::set<EntityPointerLevel, EntityPointerLevel::Comparator>;
-
-/// Translate a pointer/array type expression 'E' to a (set of)
-/// EntityPointerLevel(s) associated with the declared type of the base address
-/// of `E`. If the base address of `E` is not associated with an entity, the
-/// translation result is an empty set.
-///
-/// \param E the pointer expression to be translated
-/// \param Ctx the AST context of `E`
-/// \param AddEntity the callback provided by the caller to convert EntityNames
-/// to EntityIds.
-llvm::Expected<EntityPointerLevelSet>
-translateEntityPointerLevel(const Expr *E, ASTContext &Ctx,
-                            std::function<EntityId(EntityName EN)> AddEntity);
-
-/// Create EntityPointerLevel(s) (EPLs) from a NamedDecl of a pointer/array type.
-///
-/// \param ND the pointer type Decl to be translated
-/// \param Ctx the AST context of `E`
-/// \param AddEntity the callback provided by the caller to convert EntityNames
-/// to EntityIds.
-/// \param IsFunRet true iff the created EPL is associated with the return type
-/// of a function entity.
-llvm::Expected<EntityPointerLevel>
-createEntityPointerLevel(const NamedDecl *ND, ASTContext &Ctx,
-                         std::function<EntityId(EntityName EN)> AddEntity,
-                         bool IsFunRet = false);
-
-/// Creates new EntityPointerLevel(s) (EPLs) from the provided
-/// one(s) by incrementing their pointer levels.
-/// \return the EPL(s) that is associated with the pointee (or array element)
-/// type of `E`'s associated pointer/array tyoe of the same entity.
-EntityPointerLevelSet incrementPointerLevel(const EntityPointerLevelSet &E);
-
-llvm::json::Value
-entityPointerLevelToJSON(const EntityPointerLevel &EPL,
-                         JSONFormat::EntityIdToJSONFn EntityId2JSON);
-
-llvm::Expected<EntityPointerLevel>
-entityPointerLevelFromJSON(const llvm::json::Value &EPLData,
-                           JSONFormat::EntityIdFromJSONFn EntityIdFromJSON);
-
-/// Proxy function creating EPLs for unit tests:
-EntityPointerLevel buildEntityPointerLevel(EntityId, unsigned);
-} // namespace clang::ssaf
-#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_H
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h
index 752b3caaad29d..56ae2e4fd5f36 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h
@@ -107,7 +107,7 @@ translateEntityPointerLevel(const Expr *E, ASTContext &Ctx,
 /// \param IsFunRet true iff the created EPL is associated with the return type
 /// of a function entity.
 llvm::Expected<EntityPointerLevel>
-creatEntityPointerLevel(const NamedDecl *ND, ASTContext &Ctx,
+createEntityPointerLevel(const NamedDecl *ND, ASTContext &Ctx,
                         std::function<EntityId(EntityName EN)> AddEntity,
                         bool IsFunRet = false);
 
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerAssignments.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h
similarity index 67%
rename from clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerAssignments.h
rename to clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h
index cc82f2479a47d..b95c42328ee69 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerAssignments.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h
@@ -1,4 +1,4 @@
-//===---------------- PointerAssignments.h ----------------------*- C++ -*-===//
+//===- PointerFlow.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.
@@ -11,22 +11,24 @@
 //  nodes.
 //
 //===----------------------------------------------------------------------===//
-#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_POINTERASSIGNMENTS_H
-#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_POINTERASSIGNMENTS_H
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_POINTERFLOW_POINTERASSIGNMENTS_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_POINTERFLOW_POINTERASSIGNMENTS_H
 
-#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
 
 namespace clang::ssaf {
 
 /// Maps LHSs to their RHS sets:
 using EdgeSet = std::map<EntityPointerLevel, EntityPointerLevelSet>;
 
-class PointerAssignmentsEntitySummary final : public EntitySummary {
+class PointerFlowEntitySummary final : public EntitySummary {
   EdgeSet Edges;
 
-  friend class PointerAssignmentsTUSummaryExtractor;
+  friend class PointerFlowTUSummaryExtractor;
 
-  PointerAssignmentsEntitySummary(EdgeSet Edges)
+  PointerFlowEntitySummary(EdgeSet Edges)
       : EntitySummary(), Edges(std::move(Edges)) {}
 
 public:
@@ -34,7 +36,7 @@ class PointerAssignmentsEntitySummary final : public EntitySummary {
 
   bool operator==(const EdgeSet &Other) const { return Edges == Other; }
 
-  bool operator==(const PointerAssignmentsEntitySummary &Other) const {
+  bool operator==(const PointerFlowEntitySummary &Other) const {
     return Edges == Other.Edges;
   }
 
@@ -48,17 +50,17 @@ class PointerAssignmentsEntitySummary final : public EntitySummary {
   summaryFromJSON(const llvm::json::Object &Data, EntityIdTable &,
                   JSONFormat::EntityIdFromJSONFn EntityIdFromJSON);
 
-  static SummaryName summaryName() { return SummaryName{"PointerAssignments"}; }
+  static SummaryName summaryName() { return SummaryName{"PointerFlow"}; }
 };
 
-struct PointerAssignmentsJSONFormatInfo : JSONFormat::FormatInfo {
-  PointerAssignmentsJSONFormatInfo()
+struct PointerFlowJSONFormatInfo : JSONFormat::FormatInfo {
+  PointerFlowJSONFormatInfo()
       : JSONFormat::FormatInfo(
-            PointerAssignmentsEntitySummary::summaryName(),
-            PointerAssignmentsEntitySummary::summaryToJSON,
-            PointerAssignmentsEntitySummary::summaryFromJSON) {}
+            PointerFlowEntitySummary::summaryName(),
+            PointerFlowEntitySummary::summaryToJSON,
+            PointerFlowEntitySummary::summaryFromJSON) {}
 };
 
 } // namespace clang::ssaf
 
-#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_POINTERASSIGNMENTS_H
\ No newline at end of file
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_POINTERFLOW_POINTERASSIGNMENTS_H
\ No newline at end of file
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h b/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
index e6af437160ed9..6b98d4428f8dd 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
@@ -45,18 +45,17 @@ extern volatile int UnsafeBufferUsageTUSummaryExtractorAnchorSource;
     UnsafeBufferUsageTUSummaryExtractorAnchorDestination =
         UnsafeBufferUsageTUSummaryExtractorAnchorSource;
 
-// This anchor is used to force the linker to link the PointerAssignments
+// This anchor is used to force the linker to link the PointerFlow
 // JSONFormat registration:
-extern volatile int PointerAssignmentsSSAFJSONFormatAnchorSource;
-[[maybe_unused]] static int PointerAssignmentsSSAFJSONFormatAnchorDestination =
-    PointerAssignmentsSSAFJSONFormatAnchorSource;
+extern volatile int PointerFlowSSAFJSONFormatAnchorSource;
+[[maybe_unused]] static int PointerFlowSSAFJSONFormatAnchorDestination =
+    PointerFlowSSAFJSONFormatAnchorSource;
 
-// This anchor is used to force the linker to link the PointerAssignments
+// This anchor is used to force the linker to link the PointerFlow
 // TUSummaryExtractor registration.
-extern volatile int PointerAssignmentsTUSummaryExtractorAnchorSource;
-[[maybe_unused]] static int
-    PointerAssignmentsTUSummaryExtractorAnchorDestination =
-        PointerAssignmentsTUSummaryExtractorAnchorSource;
+extern volatile int PointerFlowTUSummaryExtractorAnchorSource;
+[[maybe_unused]] static int PointerFlowTUSummaryExtractorAnchorDestination =
+    PointerFlowTUSummaryExtractorAnchorSource;
 
 // This anchor is used to force the linker to link the CallGraphExtractor.
 extern volatile int CallGraphExtractorAnchorSource;
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
index 3fc281f397dba..22c4443366a61 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
@@ -6,10 +6,10 @@ add_clang_library(clangScalableStaticAnalysisFrameworkAnalyses
   CallGraph/CallGraphExtractor.cpp
   CallGraph/CallGraphJSONFormat.cpp
   EntityPointerLevel/EntityPointerLevel.cpp
+  PointerFlow/PointerFlow.cpp
+  PointerFlow/PointerFlowExtractor.cpp  
   UnsafeBufferUsage/UnsafeBufferUsage.cpp
   UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
-  PointerAssignments/PointerAssignments.cpp
-  PointerAssignments/PointerAssignmentsExtractor.cpp
 
   LINK_LIBS
   clangAST
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
index 4151492f40fca..69aaf0a76be8c 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
@@ -244,7 +244,7 @@ Expected<EntityPointerLevelSet> clang::ssaf::translateEntityPointerLevel(
 }
 
 /// Create an EntityPointerLevel from a ValueDecl of a pointer type.
-Expected<EntityPointerLevel> clang::ssaf::creatEntityPointerLevel(
+Expected<EntityPointerLevel> clang::ssaf::createEntityPointerLevel(
     const NamedDecl *D, ASTContext &Ctx,
     std::function<EntityId(EntityName EN)> AddEntity, bool IsFunRet) {
   EntityPointerLevelTranslator Translator(AddEntity, Ctx);
@@ -252,7 +252,7 @@ Expected<EntityPointerLevel> clang::ssaf::creatEntityPointerLevel(
 
   if (!EPL)
     return EPL.takeError();
-  return EntityPointerLevelSet{*EPL};
+  return EPL;
 }
 
 EntityPointerLevel
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerAssignments/PointerAssignments.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.cpp
similarity index 71%
rename from clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerAssignments/PointerAssignments.cpp
rename to clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.cpp
index 8f998ec35abc2..58aea3461c9c2 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerAssignments/PointerAssignments.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.cpp
@@ -1,4 +1,4 @@
-//===---------- PointerAssignments.cpp -----------------------------------===//
+//===- PointerFlow.cpp ---------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -6,8 +6,9 @@
 //
 //===---------------------------------------------------------------------===//
 
-#include "clang/ScalableStaticAnalysisFramework/Analyses/PointerAssignments.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h"
 #include "SSAFAnalysesCommon.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
 #include "clang/ScalableStaticAnalysisFramework/SSAFForceLinker.h" // IWYU pragma: keep
 #include "llvm/ADT/STLExtras.h"
@@ -15,7 +16,7 @@
 #include "llvm/Support/JSON.h"
 
 namespace {
-constexpr const char *const PointerAssignmentsKey = "PointerAssignments";
+constexpr const char *const PointerFlowKey = "PointerFlow";
 } // namespace
 
 namespace clang::ssaf {
@@ -29,12 +30,12 @@ using Value = llvm::json::Value;
 //    Array [ [lhs-node], [rhs-node], [rhs-node], ...]
 //    ...
 // ]
-llvm::json::Object PointerAssignmentsEntitySummary::summaryToJSON(
+llvm::json::Object PointerFlowEntitySummary::summaryToJSON(
     const EntitySummary &ES, JSONFormat::EntityIdToJSONFn EntityId2JSON) {
   Array EdgesData;
 
   for (const auto &Entry :
-       static_cast<const PointerAssignmentsEntitySummary &>(ES).Edges) {
+       static_cast<const PointerFlowEntitySummary &>(ES).Edges) {
     Array EdgesEntryData;
     EntityPointerLevel LHS = Entry.first;
 
@@ -47,31 +48,31 @@ llvm::json::Object PointerAssignmentsEntitySummary::summaryToJSON(
 
   Object Data;
 
-  Data[PointerAssignmentsKey] = Value(std::move(EdgesData));
+  Data[PointerFlowKey] = Value(std::move(EdgesData));
   return Data;
 }
 
 llvm::Expected<std::unique_ptr<EntitySummary>>
-PointerAssignmentsEntitySummary::summaryFromJSON(
+PointerFlowEntitySummary::summaryFromJSON(
     const Object &Data, EntityIdTable &,
     JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) {
-  const Value *EdgesData = Data.get(PointerAssignmentsKey);
+  const Value *EdgesData = Data.get(PointerFlowKey);
 
   if (!EdgesData)
-    return makeErrorSawButExpected(
-        Object(Data), "a JSON object with the key: %s", PointerAssignmentsKey);
+    return makeSawButExpectedError(
+        Object(Data), "a JSON object with the key: %s", PointerFlowKey);
 
   EdgeSet Edges;
   const auto *EdgesDataAsArr = EdgesData->getAsArray();
 
   if (!EdgesDataAsArr)
-    return makeErrorSawButExpected(
+    return makeSawButExpectedError(
         *EdgesData, "a JSON array of arary of EntityPointerLevels");
   for (const auto &EdgesEntryData : *EdgesDataAsArr) {
     const auto *EPLArray = EdgesEntryData.getAsArray();
 
     if (!EPLArray || EPLArray->size() <= 1)
-      return makeErrorSawButExpected(
+      return makeSawButExpectedError(
           EdgesEntryData, "a JSON array of EntityPointerLevels with a size "
                           "greater than 1: [lhs, rhs, rhs, ...]");
 
@@ -89,17 +90,15 @@ PointerAssignmentsEntitySummary::summaryFromJSON(
       return Err;
     Edges[*EPLs.begin()].insert(EPLs.begin() + 1, EPLs.end());
   }
-  return std::make_unique<PointerAssignmentsEntitySummary>(
-      PointerAssignmentsEntitySummary(std::move(Edges)));
+  return std::make_unique<PointerFlowEntitySummary>(
+      PointerFlowEntitySummary(std::move(Edges)));
 }
 
-static llvm::Registry<JSONFormat::FormatInfo>::Add<
-    PointerAssignmentsJSONFormatInfo>
-    RegisterPointerAssignmentsJSONFormatInfo(
-        "PointerAssignments",
-        "JSON Format info for PointerAssignmentsEntitySummary");
+static llvm::Registry<JSONFormat::FormatInfo>::Add<PointerFlowJSONFormatInfo>
+    RegisterPointerFlowJSONFormatInfo(
+        "PointerFlow", "JSON Format info for PointerFlowEntitySummary");
 
 } // namespace clang::ssaf
 
 // NOLINTNEXTLINE(misc-use-internal-linkage)
-volatile int PointerAssignmentsSSAFJSONFormatAnchorSource = 0;
\ No newline at end of file
+volatile int PointerFlowSSAFJSONFormatAnchorSource = 0;
\ No newline at end of file
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerAssignments/PointerAssignmentsExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowExtractor.cpp
similarity index 86%
rename from clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerAssignments/PointerAssignmentsExtractor.cpp
rename to clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowExtractor.cpp
index a49e2ffdab441..49e682a199309 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerAssignments/PointerAssignmentsExtractor.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowExtractor.cpp
@@ -1,4 +1,4 @@
-//===---- PointerAssignmentsExtractor.cpp --------------------------------===//
+//===- PointerFlowExtractor.cpp -----------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -15,15 +15,15 @@
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/Stmt.h"
 #include "clang/AST/TypeBase.h"
-#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h"
-#include "clang/ScalableStaticAnalysisFramework/Analyses/PointerAssignments.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.h"
-#include "clang/ScalableStaticAnalysisFramework/SSAFForceLinker.h" // IWYU pragma: keep
+#include "llvm/ADT/STLExtras.h"
 #include "llvm/Support/Error.h"
 #include <functional>
 #include <memory>
@@ -40,7 +40,7 @@ class PointerAssignmentMatcher {
   // Convert a Expr/NamedDecl to an EntityPointerLevel(Set):
   Expected<EntityPointerLevelSet> toEPL(const NamedDecl *N,
                                         bool IsRet = false) {
-    auto Ret = creatEntityPointerLevel(N, Ctx, AddEntity, IsRet);
+    auto Ret = createEntityPointerLevel(N, Ctx, AddEntity, IsRet);
 
     if (Ret)
       return EntityPointerLevelSet{*Ret};
@@ -126,11 +126,14 @@ class PointerAssignmentMatcher {
         return false;
       }
 
-      EntityPointerLevelSet LHS = *Expected;
-
-      for (unsigned I = 0; I < ArrayElementIndirectLevel; ++I)
-        LHS = incrementPointerLevel(LHS);
-      return addEdges(LHS, InitExpr);
+      auto R = llvm::map_range(*Expected, [&ArrayElementIndirectLevel](
+                                              const EntityPointerLevel &EPL) {
+        EntityPointerLevel Result = EPL;
+        for (unsigned I = 0; I < ArrayElementIndirectLevel; ++I)
+          Result = incrementPointerLevel(Result);
+        return Result;
+      });
+      return addEdges(EntityPointerLevelSet{R.begin(), R.end()}, InitExpr);
     }
     // Note that `Base`'s type is NOT the real LHS type when
     // ArrayElementIndirectLevel > 0:
@@ -150,7 +153,7 @@ class PointerAssignmentMatcher {
     if (auto *CXXRD = dyn_cast<CXXRecordDecl>(RecordTy))
       if (CXXRD->getNumBases() != 0) {
         // FIXME: support this:
-        addError(strErrAtNode(
+        addError(makeErrAtNode(
             Ctx, *ILE,
             "attempt to create pointer assignment edges between "
             "CXXRecordDecls with base classes and initializer-lists"));
@@ -290,16 +293,16 @@ class PointerAssignmentMatcher {
 } // namespace
 
 namespace clang::ssaf {
-class PointerAssignmentsTUSummaryExtractor : public TUSummaryExtractor {
+class PointerFlowTUSummaryExtractor : public TUSummaryExtractor {
 public:
-  PointerAssignmentsTUSummaryExtractor(TUSummaryBuilder &Builder)
+  PointerFlowTUSummaryExtractor(TUSummaryBuilder &Builder)
       : TUSummaryExtractor(Builder) {}
 
   EntityId addEntity(const EntityName &EN) {
     return SummaryBuilder.addEntity(EN);
   }
 
-  Expected<std::unique_ptr<PointerAssignmentsEntitySummary>>
+  Expected<std::unique_ptr<PointerFlowEntitySummary>>
   extractEntitySummary(const NamedDecl *Contributor, ASTContext &Ctx) {
     PointerAssignmentMatcher Matcher(
         Ctx, [this](const EntityName &EN) { return addEntity(EN); });
@@ -308,14 +311,14 @@ class PointerAssignmentsTUSummaryExtractor : public TUSummaryExtractor {
     Finder.findMatches(const_cast<NamedDecl *>(Contributor));
     if (Matcher.Error)
       return std::move(Matcher.Error);
-    return std::make_unique<PointerAssignmentsEntitySummary>(
-        PointerAssignmentsEntitySummary(std::move(Matcher.Results)));
+    return std::make_unique<PointerFlowEntitySummary>(
+        PointerFlowEntitySummary(std::move(Matcher.Results)));
   }
 
   void HandleTranslationUnit(ASTContext &Ctx) override;
 };
 
-void PointerAssignmentsTUSummaryExtractor::HandleTranslationUnit(
+void PointerFlowTUSummaryExtractor::HandleTranslationUnit(
     ASTContext &Ctx) {
   llvm::Error Errors = llvm::ErrorSuccess();
   auto addError = [&Errors](llvm::Error Err) {
@@ -339,7 +342,7 @@ void PointerAssignmentsTUSummaryExtractor::HandleTranslationUnit(
     auto ContributorName = getEntityName(CD);
 
     if (!ContributorName) {
-      addError(entityNameErrFor(Ctx, *CD));
+      addError(makeEntityNameErr(Ctx, *CD));
       continue;
     }
 
@@ -347,37 +350,38 @@ void PointerAssignmentsTUSummaryExtractor::HandleTranslationUnit(
         addEntity(*ContributorName), std::move(*EntitySummary));
 
     if (!Success)
-      addError(failedToAddEntitySummaryFor(Ctx, CD));
+      addError(makeAddEntitySummaryErr(Ctx, CD));
   }
   // FIXME: handle errors!
   llvm::consumeError(std::move(Errors));
 }
 
 // Proxy functions for unit test:
-extern Expected<std::unique_ptr<PointerAssignmentsEntitySummary>>
-extractEntitySummary(PointerAssignmentsTUSummaryExtractor &Extractor,
+extern Expected<std::unique_ptr<PointerFlowEntitySummary>>
+extractEntitySummary(PointerFlowTUSummaryExtractor &Extractor,
                      const NamedDecl *Contributor, ASTContext &Ctx) {
   return Extractor.extractEntitySummary(Contributor, Ctx);
 }
 
-extern PointerAssignmentsTUSummaryExtractor *
-createPointerAssignmentsTUSummaryExtractor(TUSummaryBuilder &Builder) {
-  return new PointerAssignmentsTUSummaryExtractor(
-      PointerAssignmentsTUSummaryExtractor(Builder));
+extern PointerFlowTUSummaryExtractor *
+createPointerFlowTUSummaryExtractor(TUSummaryBuilder &Builder) {
+  return new PointerFlowTUSummaryExtractor(
+      PointerFlowTUSummaryExtractor(Builder));
 }
 
-extern void deletePointerAssignmentsTUSummaryExtractor(
-    PointerAssignmentsTUSummaryExtractor *Ptr) {
+extern void deletePointerFlowTUSummaryExtractor(
+    PointerFlowTUSummaryExtractor *Ptr) {
   delete Ptr;
 }
 
-extern EntityId addEntity(PointerAssignmentsTUSummaryExtractor &Extractor,
+extern EntityId addEntity(PointerFlowTUSummaryExtractor &Extractor,
                           EntityName &EN) {
   return Extractor.addEntity(EN);
 }
 } // namespace clang::ssaf
 
-volatile int PointerAssignmentsTUSummaryExtractorAnchorSource = 0;
-static TUSummaryExtractorRegistry::Add<PointerAssignmentsTUSummaryExtractor>
-    RegisterExtractor("PointerAssignmentsTUSummaryExtractor",
+// NOLINTNEXTLINE(misc-use-internal-linkage)
+volatile int PointerFlowTUSummaryExtractorAnchorSource = 0;
+static TUSummaryExtractorRegistry::Add<PointerFlowTUSummaryExtractor>
+    RegisterExtractor("PointerFlowTUSummaryExtractor",
                       "The TUSummaryExtractor for pointer assignments");
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/tu-summary.json b/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/tu-summary.json
index d4c2aa93373c5..570ed5cfe07dd 100644
--- a/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/tu-summary.json
+++ b/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/tu-summary.json
@@ -5,7 +5,7 @@
         {
           "entity_id": 2,
           "entity_summary": {
-            "PointerAssignments": [
+            "PointerFlow": [
               [
                 [
                   {
@@ -38,7 +38,7 @@
           }
         }
       ],
-      "summary_name": "PointerAssignments"
+      "summary_name": "PointerFlow"
     },
     {
       "summary_data": [
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerAssignmentsTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp
similarity index 87%
rename from clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerAssignmentsTest.cpp
rename to clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp
index 790a344986f5e..53213a1ad13b8 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerAssignmentsTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp
@@ -1,5 +1,4 @@
-//===- PointerAssignmentsTest.cpp
-//------------------------------------------===//
+//===- PointerFlowTest.cpp ------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -7,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "clang/ScalableStaticAnalysisFramework/Analyses/PointerAssignments.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DynamicRecursiveASTVisitor.h"
@@ -36,32 +35,32 @@ namespace clang::ssaf {
 /////////////////////////////////////////////////////
 // Declare Proxy functions
 /////////////////////////////////////////////////////
-class PointerAssignmentsTUSummaryExtractor;
+class PointerFlowTUSummaryExtractor;
 
-extern Expected<std::unique_ptr<PointerAssignmentsEntitySummary>>
-extractEntitySummary(PointerAssignmentsTUSummaryExtractor &Extractor,
+extern Expected<std::unique_ptr<PointerFlowEntitySummary>>
+extractEntitySummary(PointerFlowTUSummaryExtractor &Extractor,
                      const NamedDecl *Contributor, ASTContext &Ctx);
 
-extern PointerAssignmentsTUSummaryExtractor *
-createPointerAssignmentsTUSummaryExtractor(TUSummaryBuilder &Builder);
+extern PointerFlowTUSummaryExtractor *
+createPointerFlowTUSummaryExtractor(TUSummaryBuilder &Builder);
 
-extern void deletePointerAssignmentsTUSummaryExtractor(
-    PointerAssignmentsTUSummaryExtractor *);
+extern void deletePointerFlowTUSummaryExtractor(
+    PointerFlowTUSummaryExtractor *);
 
-extern EntityId addEntity(PointerAssignmentsTUSummaryExtractor &Extractor,
+extern EntityId addEntity(PointerFlowTUSummaryExtractor &Extractor,
                           EntityName &EN);
 
-class PointerAssignmentsTUSummaryExtractorProxy {
-  PointerAssignmentsTUSummaryExtractor *Ptr;
+class PointerFlowTUSummaryExtractorProxy {
+  PointerFlowTUSummaryExtractor *Ptr;
 
 public:
-  explicit PointerAssignmentsTUSummaryExtractorProxy(TUSummaryBuilder &Builder)
-      : Ptr(createPointerAssignmentsTUSummaryExtractor(Builder)) {}
-  ~PointerAssignmentsTUSummaryExtractorProxy() {
-    deletePointerAssignmentsTUSummaryExtractor(Ptr);
+  explicit PointerFlowTUSummaryExtractorProxy(TUSummaryBuilder &Builder)
+      : Ptr(createPointerFlowTUSummaryExtractor(Builder)) {}
+  ~PointerFlowTUSummaryExtractorProxy() {
+    deletePointerFlowTUSummaryExtractor(Ptr);
   }
 
-  PointerAssignmentsTUSummaryExtractor &operator*() const { return *Ptr; }
+  PointerFlowTUSummaryExtractor &operator*() const { return *Ptr; }
 };
 } // namespace clang::ssaf
 
@@ -178,21 +177,21 @@ struct EPLPair {
   bool isFunRet;
 };
 
-class PointerAssignmentsTest : public testing::Test {
+class PointerFlowTest : public testing::Test {
 protected:
   TUSummary TUSum;
   TUSummaryBuilder Builder;
-  PointerAssignmentsTUSummaryExtractorProxy ExtractorProxy;
+  PointerFlowTUSummaryExtractorProxy ExtractorProxy;
   std::unique_ptr<ASTUnit> AST;
 
-  PointerAssignmentsTest()
+  PointerFlowTest()
       : TUSum(BuildNamespace(BuildNamespaceKind::CompilationUnit, "Mock.cpp")),
         Builder(TUSum), ExtractorProxy(Builder) {}
 
   template <typename ContributorDecl = NamedDecl,
             typename =
                 std::enable_if_t<std::is_base_of_v<NamedDecl, ContributorDecl>>>
-  std::unique_ptr<PointerAssignmentsEntitySummary>
+  std::unique_ptr<PointerFlowEntitySummary>
   setUpTest(StringRef Code, FindEntityByName ContributorName) {
     AST = tooling::buildASTFromCodeWithArgs(
         Code, {"-Wno-unused-value", "-Wno-int-to-pointer-cast"});
@@ -241,7 +240,7 @@ class PointerAssignmentsTest : public testing::Test {
 // 'ToEPL(Test, Line)' is a lambda that converts a 'EPLPair' to a
 // 'EntityPointerLevel':
 static constexpr auto ToEPL =
-    [](PointerAssignmentsTest *Test,
+    [](PointerFlowTest *Test,
        unsigned Line) -> std::function<EntityPointerLevel(const EPLPair &)> {
   return [Test, Line](const EPLPair &Pair) -> EntityPointerLevel {
     std::optional<EntityId> Entity = Pair.isFunRet
@@ -256,7 +255,7 @@ static constexpr auto ToEPL =
 };
 
 EdgeSet
-PointerAssignmentsTest::makeEdges(unsigned Line,
+PointerFlowTest::makeEdges(unsigned Line,
                                   ArrayRef<std::pair<EPLPair, EPLPair>> Edges) {
   EdgeSet Result;
   for (auto Edge : Edges)
@@ -265,12 +264,12 @@ PointerAssignmentsTest::makeEdges(unsigned Line,
   return Result;
 }
 
-TEST_F(PointerAssignmentsTest, IsExtractorRegisteredTest) {
+TEST_F(PointerFlowTest, IsExtractorRegisteredTest) {
   EXPECT_TRUE(
-      isTUSummaryExtractorRegistered("PointerAssignmentsTUSummaryExtractor"));
+      isTUSummaryExtractorRegistered("PointerFlowTUSummaryExtractor"));
 }
 
-TEST_F(PointerAssignmentsTest, IsJSONFormatRegistered) {
+TEST_F(PointerFlowTest, IsJSONFormatRegistered) {
   std::set<llvm::StringRef> ActualNames;
   for (const auto &Entry :
        llvm::Registry<clang::ssaf::JSONFormat::FormatInfo>::entries()) {
@@ -278,7 +277,7 @@ TEST_F(PointerAssignmentsTest, IsJSONFormatRegistered) {
     EXPECT_TRUE(Inserted);
   }
 
-  EXPECT_TRUE(ActualNames.count("PointerAssignments") == 1);
+  EXPECT_TRUE(ActualNames.count("PointerFlow") == 1);
 }
 
 //////////////////////////////////////////////////////////////
@@ -292,7 +291,7 @@ TEST_F(PointerAssignmentsTest, IsJSONFormatRegistered) {
 //   r = q;
 // }
 constexpr const char *const SerilizationTestOracle = R"cpp({
-  "PointerAssignments": [
+  "PointerFlow": [
     [
       [
         {
@@ -324,7 +323,7 @@ constexpr const char *const SerilizationTestOracle = R"cpp({
   ]
 })cpp";
 
-TEST_F(PointerAssignmentsTest, SerializeTest) {
+TEST_F(PointerFlowTest, SerializeTest) {
   auto Sum = setUpTest(R"cpp(
     void foo(int ***p, int ****q, int x, int ****r) {
       p[5][5][5];
@@ -346,7 +345,7 @@ TEST_F(PointerAssignmentsTest, SerializeTest) {
                                           {*getEntityId("q"), 108},
                                           {*getEntityId("r"), 9}};
 
-  Object JData = PointerAssignmentsEntitySummary::summaryToJSON(
+  Object JData = PointerFlowEntitySummary::summaryToJSON(
       *Sum, [&DummyTable](EntityId Id) {
         return Object{{"@", Value(DummyTable[Id])}};
       });
@@ -355,7 +354,7 @@ TEST_F(PointerAssignmentsTest, SerializeTest) {
             SerilizationTestOracle);
 }
 
-TEST_F(PointerAssignmentsTest, DeserializeTest) {
+TEST_F(PointerFlowTest, DeserializeTest) {
   auto Sum = setUpTest(R"cpp(
     void foo(int ***p, int ****q, int x, int ****r) {
       p[5][5][5];
@@ -382,21 +381,21 @@ TEST_F(PointerAssignmentsTest, DeserializeTest) {
   ASSERT_NE(ParsedJSON->getAsObject(), nullptr);
 
   EntityIdTable Ignored;
-  auto ParsedSum = PointerAssignmentsEntitySummary::summaryFromJSON(
+  auto ParsedSum = PointerFlowEntitySummary::summaryFromJSON(
       *ParsedJSON->getAsObject(), Ignored,
       [&DummyTable](const Object &O) -> Expected<EntityId> {
         return DummyTable.at(O.getInteger("@").value());
       });
 
   ASSERT_THAT_EXPECTED(ParsedSum, llvm::Succeeded());
-  EXPECT_EQ(*static_cast<PointerAssignmentsEntitySummary *>(ParsedSum->get()),
+  EXPECT_EQ(*static_cast<PointerFlowEntitySummary *>(ParsedSum->get()),
             *Sum);
 }
 
 //////////////////////////////////////////////////////////////
 //          Simple Assign Tests                             //
 //////////////////////////////////////////////////////////////
-TEST_F(PointerAssignmentsTest, SimpleAssign) {
+TEST_F(PointerFlowTest, SimpleAssign) {
   auto Sum = setUpTest(R"cpp(
     void foo(int *p, int *q) {
       q = p;
@@ -408,7 +407,7 @@ TEST_F(PointerAssignmentsTest, SimpleAssign) {
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
 }
 
-TEST_F(PointerAssignmentsTest, AssignWithSubscriptLHS) {
+TEST_F(PointerFlowTest, AssignWithSubscriptLHS) {
   auto Sum = setUpTest(R"cpp(
     void foo(int **q, int *p, int x) {
       q[x] = p;
@@ -420,7 +419,7 @@ TEST_F(PointerAssignmentsTest, AssignWithSubscriptLHS) {
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 2U}, {"p", 1U}}}));
 }
 
-TEST_F(PointerAssignmentsTest, AssignWithPtrArithRHS) {
+TEST_F(PointerFlowTest, AssignWithPtrArithRHS) {
   auto Sum = setUpTest(R"cpp(
     void foo(int *p, int *q) {
       q = p + 5;
@@ -432,7 +431,7 @@ TEST_F(PointerAssignmentsTest, AssignWithPtrArithRHS) {
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
 }
 
-TEST_F(PointerAssignmentsTest, AssignInSubscript) {
+TEST_F(PointerFlowTest, AssignInSubscript) {
   auto Sum = setUpTest(R"cpp(
     void foo(int *p, int *q) {
       (q = p)[5];
@@ -444,7 +443,7 @@ TEST_F(PointerAssignmentsTest, AssignInSubscript) {
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
 }
 
-TEST_F(PointerAssignmentsTest, MultipleAssign) {
+TEST_F(PointerFlowTest, MultipleAssign) {
   auto Sum = setUpTest(R"cpp(
     void foo(int *p, int *q, int *r) {
       q = p;
@@ -460,7 +459,7 @@ TEST_F(PointerAssignmentsTest, MultipleAssign) {
                                       }));
 }
 
-TEST_F(PointerAssignmentsTest, ChainedAssign) {
+TEST_F(PointerFlowTest, ChainedAssign) {
   auto Sum = setUpTest(R"cpp(
     void foo(int *p, int *q, int *r) {
       r = q = p;
@@ -475,7 +474,7 @@ TEST_F(PointerAssignmentsTest, ChainedAssign) {
                                       }));
 }
 
-TEST_F(PointerAssignmentsTest, CastToRValue) {
+TEST_F(PointerFlowTest, CastToRValue) {
   auto Sum = setUpTest(R"cpp(
     void foo(int *p, int *q) {
       q = static_cast<int *&&>(p);
@@ -487,7 +486,7 @@ TEST_F(PointerAssignmentsTest, CastToRValue) {
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
 }
 
-TEST_F(PointerAssignmentsTest, AssignToMember) {
+TEST_F(PointerFlowTest, AssignToMember) {
   auto Sum = setUpTest(R"cpp(
     struct S { int *field; };
     void foo(S s, int *p) {
@@ -500,7 +499,7 @@ TEST_F(PointerAssignmentsTest, AssignToMember) {
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"field", 1U}, {"p", 1U}}}));
 }
 
-TEST_F(PointerAssignmentsTest, AssignToMember2) {
+TEST_F(PointerFlowTest, AssignToMember2) {
   auto Sum = setUpTest(R"cpp(
     struct S { int *field; };
     void foo(S *s, int *p) {
@@ -516,7 +515,7 @@ TEST_F(PointerAssignmentsTest, AssignToMember2) {
 //////////////////////////////////////////////////////////////
 //          Call Expr Tests.                                //
 //////////////////////////////////////////////////////////////
-TEST_F(PointerAssignmentsTest, CallArg) {
+TEST_F(PointerFlowTest, CallArg) {
   auto Sum = setUpTest(R"cpp(
     void bar(int *param);
     void foo(int *p) {
@@ -529,7 +528,7 @@ TEST_F(PointerAssignmentsTest, CallArg) {
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"param", 1U}, {"p", 1U}}}));
 }
 
-TEST_F(PointerAssignmentsTest, CallMultiArgs) {
+TEST_F(PointerFlowTest, CallMultiArgs) {
   auto Sum = setUpTest(R"cpp(
     void bar(int *param1, int y, int *param2);
     void foo(int *p, int x, int *q) {
@@ -545,7 +544,7 @@ TEST_F(PointerAssignmentsTest, CallMultiArgs) {
                                       }));
 }
 
-TEST_F(PointerAssignmentsTest, CallAsCallArg) {
+TEST_F(PointerFlowTest, CallAsCallArg) {
   auto Sum = setUpTest(R"cpp(
 
     int *bar(int * w);
@@ -560,7 +559,7 @@ TEST_F(PointerAssignmentsTest, CallAsCallArg) {
                                        {{"p", 1U}, {"bar", 1U, true}}}));
 }
 
-TEST_F(PointerAssignmentsTest, CXXOperatorCallMultiArgs) {
+TEST_F(PointerFlowTest, CXXOperatorCallMultiArgs) {
   auto Sum = setUpTest(R"cpp(
     struct S {
       int* operator()(int *a, int *b);
@@ -580,7 +579,7 @@ TEST_F(PointerAssignmentsTest, CXXOperatorCallMultiArgs) {
                                       }));
 }
 
-TEST_F(PointerAssignmentsTest, CXXMemberCall) {
+TEST_F(PointerFlowTest, CXXMemberCall) {
   auto Sum = setUpTest(R"cpp(
     struct S {
       int* method(int *a, int *b);
@@ -598,7 +597,7 @@ TEST_F(PointerAssignmentsTest, CXXMemberCall) {
                                        {{"q", 1U}, {"method", 1U, true}}}));
 }
 
-TEST_F(PointerAssignmentsTest, VirtualMethodCall) {
+TEST_F(PointerFlowTest, VirtualMethodCall) {
   auto Sum = setUpTest(R"cpp(
     struct Base {
       virtual void method(int *a);
@@ -613,7 +612,7 @@ TEST_F(PointerAssignmentsTest, VirtualMethodCall) {
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"a", 1U}, {"p", 1U}}}));
 }
 
-TEST_F(PointerAssignmentsTest, StaticMethodCall) {
+TEST_F(PointerFlowTest, StaticMethodCall) {
   auto Sum = setUpTest(R"cpp(
     struct S {
       static void method(int *a, int *b);
@@ -631,7 +630,7 @@ TEST_F(PointerAssignmentsTest, StaticMethodCall) {
                                       }));
 }
 
-TEST_F(PointerAssignmentsTest, DefaultArg) {
+TEST_F(PointerFlowTest, DefaultArg) {
   auto Sum = setUpTest(R"cpp(
     int *g;
     void bar(int *a, int *b = g);
@@ -646,7 +645,7 @@ TEST_F(PointerAssignmentsTest, DefaultArg) {
                             {{{"a", 1U}, {"p", 1U}}, {{"b", 1U}, {"g", 1U}}}));
 }
 
-TEST_F(PointerAssignmentsTest, DefaultArg2) {
+TEST_F(PointerFlowTest, DefaultArg2) {
   auto Sum = setUpTest(R"cpp(
     int *g;
     void bar(int *a, int *b = g);
@@ -666,7 +665,7 @@ TEST_F(PointerAssignmentsTest, DefaultArg2) {
 //////////////////////////////////////////////////////////////
 //          CXX Ctor Tests.                                 //
 //////////////////////////////////////////////////////////////
-TEST_F(PointerAssignmentsTest, CXXCtorCallMultiArgs) {
+TEST_F(PointerFlowTest, CXXCtorCallMultiArgs) {
   auto Sum = setUpTest(R"cpp(
     struct S {
       S(int *a, int *b) {}
@@ -684,7 +683,7 @@ TEST_F(PointerAssignmentsTest, CXXCtorCallMultiArgs) {
                                       }));
 }
 
-TEST_F(PointerAssignmentsTest, CXXCtorCallMultiArgs2) {
+TEST_F(PointerFlowTest, CXXCtorCallMultiArgs2) {
   auto Sum = setUpTest(R"cpp(
     struct S {
       S(int *a, int x, int *b) {}
@@ -702,7 +701,7 @@ TEST_F(PointerAssignmentsTest, CXXCtorCallMultiArgs2) {
                                       }));
 }
 
-TEST_F(PointerAssignmentsTest, CXXCtorCallAsCallArg) {
+TEST_F(PointerFlowTest, CXXCtorCallAsCallArg) {
   auto Sum = setUpTest(R"cpp(
     struct Wrapper {
       Wrapper(int *q) {}
@@ -718,7 +717,7 @@ TEST_F(PointerAssignmentsTest, CXXCtorCallAsCallArg) {
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
 }
 
-TEST_F(PointerAssignmentsTest, DelegatingCXXCtorCall) {
+TEST_F(PointerFlowTest, DelegatingCXXCtorCall) {
   auto Sum = setUpTest<CXXConstructorDecl>(R"cpp(
     struct S {
       S(int *a, int *b) {}
@@ -734,7 +733,7 @@ TEST_F(PointerAssignmentsTest, DelegatingCXXCtorCall) {
                                       }));
 }
 
-TEST_F(PointerAssignmentsTest, CXXCtorBaseInit) {
+TEST_F(PointerFlowTest, CXXCtorBaseInit) {
   auto Sum = setUpTest<CXXConstructorDecl>(R"cpp(
     struct Base {
       Base(int *a) {}
@@ -752,7 +751,7 @@ TEST_F(PointerAssignmentsTest, CXXCtorBaseInit) {
 //////////////////////////////////////////////////////////////
 //          Initializers Tests.                             //
 //////////////////////////////////////////////////////////////
-TEST_F(PointerAssignmentsTest, LocalVarDeclInit) {
+TEST_F(PointerFlowTest, LocalVarDeclInit) {
   auto Sum = setUpTest(R"cpp(
     void foo(int *p) {
       int *q = p;
@@ -764,7 +763,7 @@ TEST_F(PointerAssignmentsTest, LocalVarDeclInit) {
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
 }
 
-TEST_F(PointerAssignmentsTest, LocalVarDeclInit2) {
+TEST_F(PointerFlowTest, LocalVarDeclInit2) {
   auto Sum = setUpTest(R"cpp(
     void foo(int (*arr)[10]) {
       int (*p)[10] = arr;
@@ -776,7 +775,7 @@ TEST_F(PointerAssignmentsTest, LocalVarDeclInit2) {
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"p", 1U}, {"arr", 1U}}}));
 }
 
-TEST_F(PointerAssignmentsTest, FieldInit) {
+TEST_F(PointerFlowTest, FieldInit) {
   auto Sum = setUpTest(R"cpp(
     void foo(int *p) {
       struct Bar {
@@ -790,7 +789,7 @@ TEST_F(PointerAssignmentsTest, FieldInit) {
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"field", 1U}, {"p", 1U}}}));
 }
 
-TEST_F(PointerAssignmentsTest, CXXCtorMemberInit) {
+TEST_F(PointerFlowTest, CXXCtorMemberInit) {
   StringRef Code = R"cpp(
     void foo(int *p) {
       struct Bar {
@@ -812,7 +811,7 @@ TEST_F(PointerAssignmentsTest, CXXCtorMemberInit) {
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
 }
 
-TEST_F(PointerAssignmentsTest, GlobalVarInit) {
+TEST_F(PointerFlowTest, GlobalVarInit) {
   auto Sum = setUpTest<VarDecl>(R"cpp(
     int *q;
     int *g = q;
@@ -823,7 +822,7 @@ TEST_F(PointerAssignmentsTest, GlobalVarInit) {
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"g", 1U}, {"q", 1U}}}));
 }
 
-TEST_F(PointerAssignmentsTest, StaticLocalInit) {
+TEST_F(PointerFlowTest, StaticLocalInit) {
   auto Sum = setUpTest(R"cpp(
     void foo(int *p) {
       static int *s = p;
@@ -835,7 +834,7 @@ TEST_F(PointerAssignmentsTest, StaticLocalInit) {
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"s", 1U}, {"p", 1U}}}));
 }
 
-TEST_F(PointerAssignmentsTest, StaticMemberInit) {
+TEST_F(PointerFlowTest, StaticMemberInit) {
   auto Sum = setUpTest<VarDecl>(R"cpp(
     int *g;
     struct S { static int *member = g; };   
@@ -850,7 +849,7 @@ TEST_F(PointerAssignmentsTest, StaticMemberInit) {
 //              InitList Tests.                               //
 //////////////////////////////////////////////////////////////
 
-TEST_F(PointerAssignmentsTest, ArrayInitList) {
+TEST_F(PointerFlowTest, ArrayInitList) {
   auto Sum = setUpTest(R"cpp(
     void foo(int *p, int *q) {
       int *arr[] = {p, q};
@@ -865,7 +864,7 @@ TEST_F(PointerAssignmentsTest, ArrayInitList) {
                                       }));
 }
 
-TEST_F(PointerAssignmentsTest, StructInitList) {
+TEST_F(PointerFlowTest, StructInitList) {
   auto Sum = setUpTest(R"cpp(
     struct S { int *a; int *b; };
     void foo(int *p, int *q) {
@@ -882,7 +881,7 @@ TEST_F(PointerAssignmentsTest, StructInitList) {
 }
 
 // A union initialized with a brace-enclosed initializer:
-TEST_F(PointerAssignmentsTest, UnionInitList) {
+TEST_F(PointerFlowTest, UnionInitList) {
   auto Sum = setUpTest(R"cpp(
     union U { int *x; int y; };
     void foo(int *p) {
@@ -895,7 +894,7 @@ TEST_F(PointerAssignmentsTest, UnionInitList) {
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"x", 1U}, {"p", 1U}}}));
 }
 
-TEST_F(PointerAssignmentsTest, NestedInitList) {
+TEST_F(PointerFlowTest, NestedInitList) {
   auto Sum = setUpTest(R"cpp(
     struct Inner { int * a; int * b; };
     struct S { Inner c; int * d; };
@@ -913,7 +912,7 @@ TEST_F(PointerAssignmentsTest, NestedInitList) {
                                       }));
 }
 
-TEST_F(PointerAssignmentsTest, NestedInitList2) {
+TEST_F(PointerFlowTest, NestedInitList2) {
   auto Sum = setUpTest(R"cpp(
     union Inner { int * a; int b; };
     struct S { Inner c; int * d; };
@@ -930,7 +929,7 @@ TEST_F(PointerAssignmentsTest, NestedInitList2) {
                                       }));
 }
 
-TEST_F(PointerAssignmentsTest, NestedInitList3) {
+TEST_F(PointerFlowTest, NestedInitList3) {
   auto Sum = setUpTest(R"cpp(
     struct Inner { int * a; int * b; };
     union S { Inner c; int * d; };
@@ -947,7 +946,7 @@ TEST_F(PointerAssignmentsTest, NestedInitList3) {
                                       }));
 }
 
-TEST_F(PointerAssignmentsTest, NestedArrayInitList) {
+TEST_F(PointerFlowTest, NestedArrayInitList) {
   auto Sum = setUpTest(R"cpp(
     void foo(int *p, int *q, int *r, int *s) {
       int *arr[][2] = {{p, q}, {r, s}};
@@ -964,7 +963,7 @@ TEST_F(PointerAssignmentsTest, NestedArrayInitList) {
                                       }));
 }
 
-TEST_F(PointerAssignmentsTest, MixedNestedArrayStructInitList) {
+TEST_F(PointerFlowTest, MixedNestedArrayStructInitList) {
   auto Sum = setUpTest(R"cpp(
     struct T { int *arr[2]; };
     void foo(int *p, int *q, int *r, int *s) {
@@ -982,7 +981,7 @@ TEST_F(PointerAssignmentsTest, MixedNestedArrayStructInitList) {
                                       }));
 }
 
-TEST_F(PointerAssignmentsTest, ArrayOfStructInitList) {
+TEST_F(PointerFlowTest, ArrayOfStructInitList) {
   auto Sum = setUpTest(R"cpp(
     struct S { int *a; int *b; };
     void foo(int *p, int *q, int *r, int *s) {
@@ -1004,7 +1003,7 @@ TEST_F(PointerAssignmentsTest, ArrayOfStructInitList) {
 //              Return Tests.                               //
 //////////////////////////////////////////////////////////////
 
-TEST_F(PointerAssignmentsTest, ReturnEdge) {
+TEST_F(PointerFlowTest, ReturnEdge) {
   auto Sum = setUpTest(R"cpp(
     int *foo(int *p) {
       return p;
@@ -1016,7 +1015,7 @@ TEST_F(PointerAssignmentsTest, ReturnEdge) {
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"foo", 1U, true}, {"p", 1U}}}));
 }
 
-TEST_F(PointerAssignmentsTest, MultipleReturnEdges) {
+TEST_F(PointerFlowTest, MultipleReturnEdges) {
   auto Sum = setUpTest(R"cpp(
     int *foo(int *p, int *q, bool cond) {
       if (cond)
@@ -1033,7 +1032,7 @@ TEST_F(PointerAssignmentsTest, MultipleReturnEdges) {
                                       }));
 }
 
-TEST_F(PointerAssignmentsTest, NoReturnEdgeForNonPointerReturnType) {
+TEST_F(PointerFlowTest, NoReturnEdgeForNonPointerReturnType) {
   auto Sum = setUpTest(R"cpp(
     int foo(int *p, int x) {
       return x;
@@ -1045,7 +1044,7 @@ TEST_F(PointerAssignmentsTest, NoReturnEdgeForNonPointerReturnType) {
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {}));
 }
 
-TEST_F(PointerAssignmentsTest, ReturnEdgeNotFromNestedFunction) {
+TEST_F(PointerFlowTest, ReturnEdgeNotFromNestedFunction) {
   auto *Code = R"cpp(
     int *foo(int *p) {
       struct Inner {
@@ -1065,7 +1064,7 @@ TEST_F(PointerAssignmentsTest, ReturnEdgeNotFromNestedFunction) {
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"bar", 1U, true}, {"q", 1U}}}));
 }
 
-TEST_F(PointerAssignmentsTest, ReturnEdgeInClassMethod) {
+TEST_F(PointerFlowTest, ReturnEdgeInClassMethod) {
   auto Sum = setUpTest(R"cpp(
   void foo() {
     struct S {
@@ -1079,7 +1078,7 @@ TEST_F(PointerAssignmentsTest, ReturnEdgeInClassMethod) {
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"method", 1U, true}, {"p", 1U}}}));
 }
 
-TEST_F(PointerAssignmentsTest, NoEdgeFromIndirectCall) {
+TEST_F(PointerFlowTest, NoEdgeFromIndirectCall) {
   auto Sum = setUpTest(R"cpp(
     void bar(int *param1);
     void baz(int *param2);
@@ -1108,7 +1107,7 @@ TEST_F(PointerAssignmentsTest, NoEdgeFromIndirectCall) {
 //          Lambda Tests.                                   //
 //////////////////////////////////////////////////////////////
 
-TEST_F(PointerAssignmentsTest, ReturnInLambda) {
+TEST_F(PointerFlowTest, ReturnInLambda) {
   StringRef Code = R"cpp(
     int* foo(int *p) {
       auto local = [](int *r) { return r; };
@@ -1129,7 +1128,7 @@ TEST_F(PointerAssignmentsTest, ReturnInLambda) {
                             {{{LambdaOfVar{"local"}, 1U, true}, {"r", 1U}}}));
 }
 
-TEST_F(PointerAssignmentsTest, NestedLambdaAssign) {
+TEST_F(PointerFlowTest, NestedLambdaAssign) {
   StringRef Code = R"cpp(
     void foo() {
       auto outer_lambda = [](int *r, int *s) {
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt b/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
index 245ee4cb856ab..d853dd1b79df5 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
+++ b/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
@@ -1,5 +1,5 @@
 add_distinct_clang_unittest(ClangScalableAnalysisTests
-  Analyses/PointerAssignmentsTest.cpp
+  Analyses/PointerFlow/PointerFlowTest.cpp
   Analyses/CallGraph/CallGraphExtractorTest.cpp
   Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
   ASTEntityMappingTest.cpp

>From 5730becfa09978e7738a6197182dc84a9585cac4 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Tue, 7 Apr 2026 18:05:33 -0700
Subject: [PATCH 3/5] improve the PR

---
 .../Analyses/PointerFlow/PointerFlow.h        |  27 +---
 .../Analyses/PointerFlow/PointerFlow.cpp      |  44 +++---
 .../Inputs/tu-summary-bad-array.json          |  88 ++++++++++++
 .../Inputs/tu-summary-bad-summary.json        |  65 +++++++++
 .../PointerFlow/Inputs/tu-summary-no-key.json |  94 +++++++++++++
 .../PointerFlow/Inputs/tu-summary.json        |  94 +++++++++++++
 .../PointerFlow/tu-summary-serialization.test |  27 ++++
 .../Analysis/Scalable/ssaf-format/list.test   |   1 +
 .../Analyses/PointerFlow/PointerFlowTest.cpp  | 125 ------------------
 9 files changed, 404 insertions(+), 161 deletions(-)
 create mode 100644 clang/test/Analysis/Scalable/PointerFlow/Inputs/tu-summary-bad-array.json
 create mode 100644 clang/test/Analysis/Scalable/PointerFlow/Inputs/tu-summary-bad-summary.json
 create mode 100644 clang/test/Analysis/Scalable/PointerFlow/Inputs/tu-summary-no-key.json
 create mode 100644 clang/test/Analysis/Scalable/PointerFlow/Inputs/tu-summary.json
 create mode 100644 clang/test/Analysis/Scalable/PointerFlow/tu-summary-serialization.test

diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h
index b95c42328ee69..2bb0974de92a4 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h
@@ -11,11 +11,10 @@
 //  nodes.
 //
 //===----------------------------------------------------------------------===//
-#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_POINTERFLOW_POINTERASSIGNMENTS_H
-#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_POINTERFLOW_POINTERASSIGNMENTS_H
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_POINTERFLOW_POINTERFLOW_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_POINTERFLOW_POINTERFLOW_H
 
 #include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
 
 namespace clang::ssaf {
@@ -27,6 +26,9 @@ class PointerFlowEntitySummary final : public EntitySummary {
   EdgeSet Edges;
 
   friend class PointerFlowTUSummaryExtractor;
+  friend PointerFlowEntitySummary buildPointerFlowEntitySummary(EdgeSet Edges);
+  friend llvm::iterator_range<EdgeSet::const_iterator>
+  getEdges(const PointerFlowEntitySummary &);
 
   PointerFlowEntitySummary(EdgeSet Edges)
       : EntitySummary(), Edges(std::move(Edges)) {}
@@ -42,25 +44,8 @@ class PointerFlowEntitySummary final : public EntitySummary {
 
   bool empty() const { return Edges.empty() && Edges.empty(); }
 
-  static llvm::json::Object
-  summaryToJSON(const EntitySummary &ES,
-                JSONFormat::EntityIdToJSONFn EntityId2JSON);
-
-  static llvm::Expected<std::unique_ptr<EntitySummary>>
-  summaryFromJSON(const llvm::json::Object &Data, EntityIdTable &,
-                  JSONFormat::EntityIdFromJSONFn EntityIdFromJSON);
-
   static SummaryName summaryName() { return SummaryName{"PointerFlow"}; }
 };
-
-struct PointerFlowJSONFormatInfo : JSONFormat::FormatInfo {
-  PointerFlowJSONFormatInfo()
-      : JSONFormat::FormatInfo(
-            PointerFlowEntitySummary::summaryName(),
-            PointerFlowEntitySummary::summaryToJSON,
-            PointerFlowEntitySummary::summaryFromJSON) {}
-};
-
 } // namespace clang::ssaf
 
-#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_POINTERFLOW_POINTERASSIGNMENTS_H
\ No newline at end of file
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_POINTERFLOW_POINTERFLOW_H
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.cpp
index 58aea3461c9c2..1ca731e4f58ac 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.cpp
@@ -10,19 +10,29 @@
 #include "SSAFAnalysesCommon.h"
 #include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
-#include "clang/ScalableStaticAnalysisFramework/SSAFForceLinker.h" // IWYU pragma: keep
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/JSON.h"
 
+using namespace clang;
+using namespace ssaf;
+using Object = llvm::json::Object;
+using Array = llvm::json::Array;
+using Value = llvm::json::Value;
+
 namespace {
 constexpr const char *const PointerFlowKey = "PointerFlow";
 } // namespace
 
-namespace clang::ssaf {
-using Object = llvm::json::Object;
-using Array = llvm::json::Array;
-using Value = llvm::json::Value;
+ssaf::PointerFlowEntitySummary
+ssaf::buildPointerFlowEntitySummary(EdgeSet Edges) {
+  return PointerFlowEntitySummary(std::move(Edges));
+}
+
+llvm::iterator_range<EdgeSet::const_iterator>
+ssaf::getEdges(const PointerFlowEntitySummary &Sum) {
+  return Sum.Edges;
+}
 
 // Writes the 'Edges' map as an array of array of EntityPointerLevels:
 // Array [
@@ -30,12 +40,13 @@ using Value = llvm::json::Value;
 //    Array [ [lhs-node], [rhs-node], [rhs-node], ...]
 //    ...
 // ]
-llvm::json::Object PointerFlowEntitySummary::summaryToJSON(
-    const EntitySummary &ES, JSONFormat::EntityIdToJSONFn EntityId2JSON) {
+static llvm::json::Object
+summaryToJSON(const EntitySummary &ES,
+              JSONFormat::EntityIdToJSONFn EntityId2JSON) {
   Array EdgesData;
 
   for (const auto &Entry :
-       static_cast<const PointerFlowEntitySummary &>(ES).Edges) {
+       getEdges(static_cast<const PointerFlowEntitySummary &>(ES))) {
     Array EdgesEntryData;
     EntityPointerLevel LHS = Entry.first;
 
@@ -52,10 +63,9 @@ llvm::json::Object PointerFlowEntitySummary::summaryToJSON(
   return Data;
 }
 
-llvm::Expected<std::unique_ptr<EntitySummary>>
-PointerFlowEntitySummary::summaryFromJSON(
-    const Object &Data, EntityIdTable &,
-    JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) {
+static llvm::Expected<std::unique_ptr<EntitySummary>>
+summaryFromJSON(const Object &Data, EntityIdTable &,
+                JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) {
   const Value *EdgesData = Data.get(PointerFlowKey);
 
   if (!EdgesData)
@@ -91,14 +101,18 @@ PointerFlowEntitySummary::summaryFromJSON(
     Edges[*EPLs.begin()].insert(EPLs.begin() + 1, EPLs.end());
   }
   return std::make_unique<PointerFlowEntitySummary>(
-      PointerFlowEntitySummary(std::move(Edges)));
+      buildPointerFlowEntitySummary(std::move(Edges)));
 }
 
+struct PointerFlowJSONFormatInfo : JSONFormat::FormatInfo {
+  PointerFlowJSONFormatInfo()
+      : JSONFormat::FormatInfo(PointerFlowEntitySummary::summaryName(),
+                               summaryToJSON, summaryFromJSON) {}
+};
+
 static llvm::Registry<JSONFormat::FormatInfo>::Add<PointerFlowJSONFormatInfo>
     RegisterPointerFlowJSONFormatInfo(
         "PointerFlow", "JSON Format info for PointerFlowEntitySummary");
 
-} // namespace clang::ssaf
-
 // NOLINTNEXTLINE(misc-use-internal-linkage)
 volatile int PointerFlowSSAFJSONFormatAnchorSource = 0;
\ No newline at end of file
diff --git a/clang/test/Analysis/Scalable/PointerFlow/Inputs/tu-summary-bad-array.json b/clang/test/Analysis/Scalable/PointerFlow/Inputs/tu-summary-bad-array.json
new file mode 100644
index 0000000000000..3848bd68ac0fb
--- /dev/null
+++ b/clang/test/Analysis/Scalable/PointerFlow/Inputs/tu-summary-bad-array.json
@@ -0,0 +1,88 @@
+{
+  "data": [
+    {
+      "summary_data": [
+        {
+          "entity_id": 2,
+          "entity_summary": {
+            "PointerFlow": [
+              [
+                [
+                  {
+                    "@": 0
+                  },
+                  2
+                ]
+              ],
+              [
+                [
+                  {
+                    "@": 2
+                  },
+                  1
+                ],
+                [
+                  {
+                    "@": 0
+                  },
+                  1
+                ]
+              ]
+            ]
+          }
+        }
+      ],
+      "summary_name": "PointerFlow"
+    }
+  ],
+  "id_table": [
+    {
+      "id": 2,
+      "name": {
+        "namespace": [],
+        "suffix": "",
+        "usr": "c:@F at foo#***I#*S0_#I#"
+      }
+    },
+    {
+      "id": 0,
+      "name": {
+        "namespace": [],
+        "suffix": "1",
+        "usr": "c:@F at foo#***I#*S0_#I#"
+      }
+    },
+    {
+      "id": 1,
+      "name": {
+        "namespace": [],
+        "suffix": "2",
+        "usr": "c:@F at foo#***I#*S0_#I#"
+      }
+    }
+  ],
+  "linkage_table": [
+    {
+      "id": 0,
+      "linkage": {
+        "type": "External"
+      }
+    },
+    {
+      "id": 1,
+      "linkage": {
+        "type": "Internal"
+      }
+    },
+    {
+      "id": 2,
+      "linkage": {
+        "type": "Internal"
+      }
+    }
+  ],
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "Mock.cpp"
+  }
+}
diff --git a/clang/test/Analysis/Scalable/PointerFlow/Inputs/tu-summary-bad-summary.json b/clang/test/Analysis/Scalable/PointerFlow/Inputs/tu-summary-bad-summary.json
new file mode 100644
index 0000000000000..77c02313b6add
--- /dev/null
+++ b/clang/test/Analysis/Scalable/PointerFlow/Inputs/tu-summary-bad-summary.json
@@ -0,0 +1,65 @@
+{
+  "data": [
+    {
+      "summary_data": [
+        {
+          "entity_id": 2,
+          "entity_summary": {
+            "PointerFlow": {}
+          }
+        }
+      ],
+      "summary_name": "PointerFlow"
+    }
+  ],
+  "id_table": [
+    {
+      "id": 2,
+      "name": {
+        "namespace": [],
+        "suffix": "",
+        "usr": "c:@F at foo#***I#*S0_#I#"
+      }
+    },
+    {
+      "id": 0,
+      "name": {
+        "namespace": [],
+        "suffix": "1",
+        "usr": "c:@F at foo#***I#*S0_#I#"
+      }
+    },
+    {
+      "id": 1,
+      "name": {
+        "namespace": [],
+        "suffix": "2",
+        "usr": "c:@F at foo#***I#*S0_#I#"
+      }
+    }
+  ],
+  "linkage_table": [
+    {
+      "id": 0,
+      "linkage": {
+        "type": "External"
+      }
+    },
+    {
+      "id": 1,
+      "linkage": {
+        "type": "Internal"
+      }
+    },
+    {
+      "id": 2,
+      "linkage": {
+        "type": "Internal"
+      }
+    }
+  ],
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "Mock.cpp"
+  }
+}
diff --git a/clang/test/Analysis/Scalable/PointerFlow/Inputs/tu-summary-no-key.json b/clang/test/Analysis/Scalable/PointerFlow/Inputs/tu-summary-no-key.json
new file mode 100644
index 0000000000000..e950edbfa1f3c
--- /dev/null
+++ b/clang/test/Analysis/Scalable/PointerFlow/Inputs/tu-summary-no-key.json
@@ -0,0 +1,94 @@
+{
+  "data": [
+    {
+      "summary_data": [
+        {
+          "entity_id": 2,
+          "entity_summary": {
+            "NoPointerFlow": [
+              [
+                [
+                  {
+                    "@": 0
+                  },
+                  2
+                ],
+                [
+                  {
+                    "@": 1
+                  },
+                  1
+                ]
+              ],
+              [
+                [
+                  {
+                    "@": 2
+                  },
+                  1
+                ],
+                [
+                  {
+                    "@": 0
+                  },
+                  1
+                ]
+              ]
+            ]
+          }
+        }
+      ],
+      "summary_name": "PointerFlow"
+    }
+  ],
+  "id_table": [
+    {
+      "id": 2,
+      "name": {
+        "namespace": [],
+        "suffix": "",
+        "usr": "c:@F at foo#***I#*S0_#I#"
+      }
+    },
+    {
+      "id": 0,
+      "name": {
+        "namespace": [],
+        "suffix": "1",
+        "usr": "c:@F at foo#***I#*S0_#I#"
+      }
+    },
+    {
+      "id": 1,
+      "name": {
+        "namespace": [],
+        "suffix": "2",
+        "usr": "c:@F at foo#***I#*S0_#I#"
+      }
+    }
+  ],
+  "linkage_table": [
+    {
+      "id": 0,
+      "linkage": {
+        "type": "External"
+      }
+    },
+    {
+      "id": 1,
+      "linkage": {
+        "type": "Internal"
+      }
+    },
+    {
+      "id": 2,
+      "linkage": {
+        "type": "Internal"
+      }
+    }
+  ],
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "Mock.cpp"
+  }
+}
diff --git a/clang/test/Analysis/Scalable/PointerFlow/Inputs/tu-summary.json b/clang/test/Analysis/Scalable/PointerFlow/Inputs/tu-summary.json
new file mode 100644
index 0000000000000..b90cdbb574b15
--- /dev/null
+++ b/clang/test/Analysis/Scalable/PointerFlow/Inputs/tu-summary.json
@@ -0,0 +1,94 @@
+{
+  "data": [
+    {
+      "summary_data": [
+        {
+          "entity_id": 2,
+          "entity_summary": {
+            "PointerFlow": [
+              [
+                [
+                  {
+                    "@": 0
+                  },
+                  2
+                ],
+                [
+                  {
+                    "@": 1
+                  },
+                  1
+                ]
+              ],
+              [
+                [
+                  {
+                    "@": 2
+                  },
+                  1
+                ],
+                [
+                  {
+                    "@": 0
+                  },
+                  1
+                ]
+              ]
+            ]
+          }
+        }
+      ],
+      "summary_name": "PointerFlow"
+    }
+  ],
+  "id_table": [
+    {
+      "id": 2,
+      "name": {
+        "namespace": [],
+        "suffix": "",
+        "usr": "c:@F at foo#***I#*S0_#I#"
+      }
+    },
+    {
+      "id": 0,
+      "name": {
+        "namespace": [],
+        "suffix": "1",
+        "usr": "c:@F at foo#***I#*S0_#I#"
+      }
+    },
+    {
+      "id": 1,
+      "name": {
+        "namespace": [],
+        "suffix": "2",
+        "usr": "c:@F at foo#***I#*S0_#I#"
+      }
+    }
+  ],
+  "linkage_table": [
+    {
+      "id": 0,
+      "linkage": {
+        "type": "External"
+      }
+    },
+    {
+      "id": 1,
+      "linkage": {
+        "type": "Internal"
+      }
+    },
+    {
+      "id": 2,
+      "linkage": {
+        "type": "Internal"
+      }
+    }
+  ],
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "Mock.cpp"
+  }
+}
diff --git a/clang/test/Analysis/Scalable/PointerFlow/tu-summary-serialization.test b/clang/test/Analysis/Scalable/PointerFlow/tu-summary-serialization.test
new file mode 100644
index 0000000000000..4754e61637f34
--- /dev/null
+++ b/clang/test/Analysis/Scalable/PointerFlow/tu-summary-serialization.test
@@ -0,0 +1,27 @@
+// RUN: rm -rf %t.json
+// RUN: clang-ssaf-format -type=tu  %S/Inputs/tu-summary.json -o %t.json
+// RUN: diff %S/Inputs/tu-summary.json %t.json
+
+// Negative tests:
+
+// RUN: not clang-ssaf-format -type=tu %S/Inputs/tu-summary-no-key.json 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-NO-KEY
+// CHECK-NO-KEY: saw {
+// CHECK-NO-KEY:   "NoPointerFlow": [
+// CHECK-NO-KEY:     [
+// CHECK-NO-KEY: } but expected a JSON object with the key: PointerFlow
+
+// RUN: not clang-ssaf-format -type=tu %S/Inputs/tu-summary-bad-summary.json 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-BAD-SUMMARY
+// CHECK-BAD-SUMMARY: saw {} but expected a JSON array of arary of EntityPointerLevels
+
+// RUN: not clang-ssaf-format -type=tu %S/Inputs/tu-summary-bad-array.json 2>&1 \
+// RUN:   | FileCheck %s --check-prefix=CHECK-BAD-ARRAY
+// CHECK-BAD-ARRAY: saw [
+// CHECK-BAD-ARRAY:  [
+// CHECK-BAD-ARRAY:    {
+// CHECK-BAD-ARRAY:      "@": 0
+// CHECK-BAD-ARRAY:    },
+// CHECK-BAD-ARRAY:    2
+// CHECK-BAD-ARRAY:  ]
+// CHECK-BAD-ARRAY:] but expected a JSON array of EntityPointerLevels with a size greater than 1: [lhs, rhs, rhs, ...]
diff --git a/clang/test/Analysis/Scalable/ssaf-format/list.test b/clang/test/Analysis/Scalable/ssaf-format/list.test
index c56bec1365f29..d5e8a229aedf2 100644
--- a/clang/test/Analysis/Scalable/ssaf-format/list.test
+++ b/clang/test/Analysis/Scalable/ssaf-format/list.test
@@ -9,3 +9,4 @@
 // CHECK-DAG:     Analyses:
 // CHECK-DAG:         [[NthFormat]].[[MthSummary:[0-9]+]]. CallGraph - JSON Format info for CallGraph summary
 // CHECK-DAG:         [[NthFormat]].[[MthSummary:[0-9]+]]. UnsafeBufferUsage - JSON Format info for UnsafeBufferUsageEntitySummary
+// CHECK-DAG:         [[NthFormat]].[[MthSummary:[0-9]+]]. PointerFlow - JSON Format info for PointerFlowEntitySummary
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp
index 53213a1ad13b8..2a3fb8ef9ee72 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp
@@ -15,14 +15,12 @@
 #include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummary.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
 #include "clang/Tooling/Tooling.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/Error.h"
-#include "llvm/Testing/Support/Error.h"
 #include "gtest/gtest.h"
 #include <memory>
 #include <type_traits>
@@ -269,129 +267,6 @@ TEST_F(PointerFlowTest, IsExtractorRegisteredTest) {
       isTUSummaryExtractorRegistered("PointerFlowTUSummaryExtractor"));
 }
 
-TEST_F(PointerFlowTest, IsJSONFormatRegistered) {
-  std::set<llvm::StringRef> ActualNames;
-  for (const auto &Entry :
-       llvm::Registry<clang::ssaf::JSONFormat::FormatInfo>::entries()) {
-    bool Inserted = ActualNames.insert(Entry.getName()).second;
-    EXPECT_TRUE(Inserted);
-  }
-
-  EXPECT_TRUE(ActualNames.count("PointerFlow") == 1);
-}
-
-//////////////////////////////////////////////////////////////
-//                     JSON Tests                           //
-//////////////////////////////////////////////////////////////
-// Oracle JSON output for the example:
-// void foo(int ***p, int ****q, int x, int ****r) {
-//   p[5][5][5];
-//   q[5][5][5][5];
-//   q[x] = p;
-//   r = q;
-// }
-constexpr const char *const SerilizationTestOracle = R"cpp({
-  "PointerFlow": [
-    [
-      [
-        {
-          "@": 108
-        },
-        2
-      ],
-      [
-        {
-          "@": 42
-        },
-        1
-      ]
-    ],
-    [
-      [
-        {
-          "@": 9
-        },
-        1
-      ],
-      [
-        {
-          "@": 108
-        },
-        1
-      ]
-    ]
-  ]
-})cpp";
-
-TEST_F(PointerFlowTest, SerializeTest) {
-  auto Sum = setUpTest(R"cpp(
-    void foo(int ***p, int ****q, int x, int ****r) {
-      p[5][5][5];
-      q[5][5][5][5];
-      q[x] = p;
-      r = q;
-    }
-  )cpp",
-                       "foo");
-  ASSERT_NE(Sum, nullptr);
-  EXPECT_EQ(*Sum, makeEdges(__LINE__, {
-                                          /*[0]=*/{{"q", 2U}, {"p", 1U}},
-                                          /*[1]=*/{{"r", 1U}, {"q", 1U}},
-                                      }));
-
-  using Object = llvm::json::Object;
-  using Value = llvm::json::Value;
-  std::map<EntityId, uint64_t> DummyTable{{*getEntityId("p"), 42},
-                                          {*getEntityId("q"), 108},
-                                          {*getEntityId("r"), 9}};
-
-  Object JData = PointerFlowEntitySummary::summaryToJSON(
-      *Sum, [&DummyTable](EntityId Id) {
-        return Object{{"@", Value(DummyTable[Id])}};
-      });
-
-  EXPECT_EQ(llvm::formatv("{0:2}", llvm::json::Value(std::move(JData))).str(),
-            SerilizationTestOracle);
-}
-
-TEST_F(PointerFlowTest, DeserializeTest) {
-  auto Sum = setUpTest(R"cpp(
-    void foo(int ***p, int ****q, int x, int ****r) {
-      p[5][5][5];
-      q[5][5][5][5];
-      q[x] = p;
-      r = q;
-    }
-  )cpp",
-                       "foo");
-  ASSERT_NE(Sum, nullptr);
-  EXPECT_EQ(*Sum, makeEdges(__LINE__, {
-                                          /*[0]=*/{{"q", 2U}, {"p", 1U}},
-                                          /*[1]=*/{{"r", 1U}, {"q", 1U}},
-                                      }));
-
-  using Object = llvm::json::Object;
-  using Value = llvm::json::Value;
-  std::map<uint64_t, EntityId> DummyTable{{42, *getEntityId("p")},
-                                          {108, *getEntityId("q")},
-                                          {9, *getEntityId("r")}};
-  Expected<Value> ParsedJSON = llvm::json::parse(SerilizationTestOracle);
-
-  ASSERT_THAT_EXPECTED(ParsedJSON, llvm::Succeeded());
-  ASSERT_NE(ParsedJSON->getAsObject(), nullptr);
-
-  EntityIdTable Ignored;
-  auto ParsedSum = PointerFlowEntitySummary::summaryFromJSON(
-      *ParsedJSON->getAsObject(), Ignored,
-      [&DummyTable](const Object &O) -> Expected<EntityId> {
-        return DummyTable.at(O.getInteger("@").value());
-      });
-
-  ASSERT_THAT_EXPECTED(ParsedSum, llvm::Succeeded());
-  EXPECT_EQ(*static_cast<PointerFlowEntitySummary *>(ParsedSum->get()),
-            *Sum);
-}
-
 //////////////////////////////////////////////////////////////
 //          Simple Assign Tests                             //
 //////////////////////////////////////////////////////////////

>From 492ec75ee199697b669deb2850689b9923bc2dd1 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Wed, 8 Apr 2026 11:58:36 -0700
Subject: [PATCH 4/5] refactor tests to use HandleTranslationUnit, get rid of
 proxy functions

---
 .../Analyses/PointerFlow/PointerFlow.h        |   4 +-
 .../PointerFlow/PointerFlowExtractor.cpp      |  78 +--
 .../Analyses/SSAFAnalysesCommon.h             |  11 +-
 .../Analyses/PointerFlow/PointerFlowTest.cpp  | 497 ++++++++++--------
 4 files changed, 324 insertions(+), 266 deletions(-)

diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h
index 2bb0974de92a4..b93decd701f05 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h
@@ -34,6 +34,8 @@ class PointerFlowEntitySummary final : public EntitySummary {
       : EntitySummary(), Edges(std::move(Edges)) {}
 
 public:
+  static constexpr llvm::StringLiteral Name = "PointerFlow";
+
   SummaryName getSummaryName() const override { return summaryName(); };
 
   bool operator==(const EdgeSet &Other) const { return Edges == Other; }
@@ -44,7 +46,7 @@ class PointerFlowEntitySummary final : public EntitySummary {
 
   bool empty() const { return Edges.empty() && Edges.empty(); }
 
-  static SummaryName summaryName() { return SummaryName{"PointerFlow"}; }
+  static SummaryName summaryName() { return SummaryName{Name.str()}; }
 };
 } // namespace clang::ssaf
 
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowExtractor.cpp
index 49e682a199309..16eeb70dfe25c 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowExtractor.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowExtractor.cpp
@@ -315,73 +315,35 @@ class PointerFlowTUSummaryExtractor : public TUSummaryExtractor {
         PointerFlowEntitySummary(std::move(Matcher.Results)));
   }
 
-  void HandleTranslationUnit(ASTContext &Ctx) override;
-};
+  void HandleTranslationUnit(ASTContext &Ctx) override {
+    ContributorFinder ContributorFinder;
 
-void PointerFlowTUSummaryExtractor::HandleTranslationUnit(
-    ASTContext &Ctx) {
-  llvm::Error Errors = llvm::ErrorSuccess();
-  auto addError = [&Errors](llvm::Error Err) {
-    Errors = llvm::joinErrors(std::move(Errors), std::move(Err));
-  };
-  ContributorFinder ContributorFinder;
-
-  ContributorFinder.VisitTranslationUnitDecl(Ctx.getTranslationUnitDecl());
-  for (auto *CD : ContributorFinder.Contributors) {
-    auto EntitySummary = extractEntitySummary(CD, Ctx);
-
-    if (!EntitySummary) {
-      addError(EntitySummary.takeError());
-      continue;
-    }
-    assert(*EntitySummary &&
-           "std::unique_ptr<EntitySummary> should not be null");
-    if ((*EntitySummary)->empty())
-      continue;
+    ContributorFinder.TraverseAST(Ctx);
+    for (auto *CD : ContributorFinder.Contributors) {
+      auto EntitySummary = extractEntitySummary(CD, Ctx);
 
-    auto ContributorName = getEntityName(CD);
+      if (!EntitySummary)
+        llvm::reportFatalInternalError(EntitySummary.takeError());
+      assert(*EntitySummary);
+      if ((*EntitySummary)->empty())
+        continue;
 
-    if (!ContributorName) {
-      addError(makeEntityNameErr(Ctx, *CD));
-      continue;
-    }
+      auto ContributorName = getEntityName(CD);
 
-    auto [EntitySummaryPtr, Success] = SummaryBuilder.addSummary(
-        addEntity(*ContributorName), std::move(*EntitySummary));
+      if (!ContributorName)
+        llvm::reportFatalInternalError(makeEntityNameErr(Ctx, *CD));
 
-    if (!Success)
-      addError(makeAddEntitySummaryErr(Ctx, CD));
+      auto [Ignored, InsertionSucceeded] = SummaryBuilder.addSummary(
+          addEntity(*ContributorName), std::move(*EntitySummary));
+
+      assert(InsertionSucceeded && "duplicated contributor extraction");
+    }
   }
-  // FIXME: handle errors!
-  llvm::consumeError(std::move(Errors));
-}
-
-// Proxy functions for unit test:
-extern Expected<std::unique_ptr<PointerFlowEntitySummary>>
-extractEntitySummary(PointerFlowTUSummaryExtractor &Extractor,
-                     const NamedDecl *Contributor, ASTContext &Ctx) {
-  return Extractor.extractEntitySummary(Contributor, Ctx);
-}
-
-extern PointerFlowTUSummaryExtractor *
-createPointerFlowTUSummaryExtractor(TUSummaryBuilder &Builder) {
-  return new PointerFlowTUSummaryExtractor(
-      PointerFlowTUSummaryExtractor(Builder));
-}
-
-extern void deletePointerFlowTUSummaryExtractor(
-    PointerFlowTUSummaryExtractor *Ptr) {
-  delete Ptr;
-}
-
-extern EntityId addEntity(PointerFlowTUSummaryExtractor &Extractor,
-                          EntityName &EN) {
-  return Extractor.addEntity(EN);
-}
+};
 } // namespace clang::ssaf
 
 // NOLINTNEXTLINE(misc-use-internal-linkage)
 volatile int PointerFlowTUSummaryExtractorAnchorSource = 0;
 static TUSummaryExtractorRegistry::Add<PointerFlowTUSummaryExtractor>
-    RegisterExtractor("PointerFlowTUSummaryExtractor",
+    RegisterExtractor(PointerFlowEntitySummary::Name,
                       "The TUSummaryExtractor for pointer assignments");
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
index 17c9bf7ba0491..f4f0a12606c72 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
@@ -14,6 +14,7 @@
 
 #include "clang/AST/ASTTypeTraits.h"
 #include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/DynamicRecursiveASTVisitor.h"
 #include "clang/Basic/SourceLocation.h"
@@ -82,10 +83,18 @@ class ContributorFinder : public DynamicRecursiveASTVisitor {
   bool VisitVarDecl(VarDecl *D) override {
     DeclContext *DC = D->getDeclContext();
 
-    if (DC->isFileContext() || DC->isNamespace())
+    // Collects Decl for global variables or static data members:
+    if (DC->isFileContext() || DC->isNamespace() || D->isStaticDataMember())
       Contributors.push_back(D);
     return true;
   }
+
+  bool VisitLambdaExpr(LambdaExpr *L) override {
+    // TraversetLambdaExpr directly visits the body stmt, but we need to collect
+    // the CXXMethodDecl as a contributor:
+    VisitFunctionDecl(L->getCallOperator());
+    return true;
+  }
 };
 
 /// An AST visitor that skips the root node's strict-descendants that are
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp
index 2a3fb8ef9ee72..d8fab3032d770 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h"
+#include "TestFixture.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DynamicRecursiveASTVisitor.h"
@@ -21,6 +22,7 @@
 #include "clang/Tooling/Tooling.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/Error.h"
+#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include <memory>
 #include <type_traits>
@@ -29,39 +31,6 @@
 using namespace clang;
 using namespace ssaf;
 
-namespace clang::ssaf {
-/////////////////////////////////////////////////////
-// Declare Proxy functions
-/////////////////////////////////////////////////////
-class PointerFlowTUSummaryExtractor;
-
-extern Expected<std::unique_ptr<PointerFlowEntitySummary>>
-extractEntitySummary(PointerFlowTUSummaryExtractor &Extractor,
-                     const NamedDecl *Contributor, ASTContext &Ctx);
-
-extern PointerFlowTUSummaryExtractor *
-createPointerFlowTUSummaryExtractor(TUSummaryBuilder &Builder);
-
-extern void deletePointerFlowTUSummaryExtractor(
-    PointerFlowTUSummaryExtractor *);
-
-extern EntityId addEntity(PointerFlowTUSummaryExtractor &Extractor,
-                          EntityName &EN);
-
-class PointerFlowTUSummaryExtractorProxy {
-  PointerFlowTUSummaryExtractor *Ptr;
-
-public:
-  explicit PointerFlowTUSummaryExtractorProxy(TUSummaryBuilder &Builder)
-      : Ptr(createPointerFlowTUSummaryExtractor(Builder)) {}
-  ~PointerFlowTUSummaryExtractorProxy() {
-    deletePointerFlowTUSummaryExtractor(Ptr);
-  }
-
-  PointerFlowTUSummaryExtractor &operator*() const { return *Ptr; }
-};
-} // namespace clang::ssaf
-
 namespace {
 // Use FindEntityByName to identify entities in unit tests.
 // Unit tests are simple enough to meet the following assumptions:
@@ -100,8 +69,6 @@ StringRef toStringRef(const FindEntityByName &N) {
 
 const NamedDecl *matchNamedDeclByFindEntityByName(const FindEntityByName &N,
                                                   const NamedDecl *D) {
-  if (std::holds_alternative<LambdaOfVar>(N))
-    D->dump();
   return std::visit(
       Overloaded{
           [&D](StringRef S) -> const NamedDecl * {
@@ -119,7 +86,6 @@ const NamedDecl *matchNamedDeclByFindEntityByName(const FindEntityByName &N,
           },
           [&D](const LambdaOfVar &L) -> const NamedDecl * {
             if (const auto *VD = dyn_cast<VarDecl>(D); VD && VD->getInit()) {
-              VD->dump();
               if (isa<LambdaExpr>(VD->getInit()) &&
                   VD->getNameAsString() == L.VarName)
                 return cast<LambdaExpr>(VD->getInit())->getCallOperator();
@@ -175,51 +141,81 @@ struct EPLPair {
   bool isFunRet;
 };
 
-class PointerFlowTest : public testing::Test {
+class PointerFlowTest : public TestFixture {
 protected:
   TUSummary TUSum;
   TUSummaryBuilder Builder;
-  PointerFlowTUSummaryExtractorProxy ExtractorProxy;
+  std::unique_ptr<TUSummaryExtractor> Extractor;
   std::unique_ptr<ASTUnit> AST;
 
   PointerFlowTest()
       : TUSum(BuildNamespace(BuildNamespaceKind::CompilationUnit, "Mock.cpp")),
-        Builder(TUSum), ExtractorProxy(Builder) {}
+        Builder(TUSum), Extractor(nullptr) {}
 
   template <typename ContributorDecl = NamedDecl,
             typename =
                 std::enable_if_t<std::is_base_of_v<NamedDecl, ContributorDecl>>>
-  std::unique_ptr<PointerFlowEntitySummary>
-  setUpTest(StringRef Code, FindEntityByName ContributorName) {
+  bool setUpTest(StringRef Code) {
     AST = tooling::buildASTFromCodeWithArgs(
         Code, {"-Wno-unused-value", "-Wno-int-to-pointer-cast"});
 
-    const auto *ContributorDefn = findEntityByName<ContributorDecl>(
-        ContributorName, AST->getASTContext());
+    for (auto &E : clang::ssaf::TUSummaryExtractorRegistry::entries()) {
+      if (E.getName() == PointerFlowEntitySummary::Name) {
+        Extractor = E.instantiate(Builder);
+        break;
+      }
+    }
 
-    if (!ContributorDefn)
+    if (!Extractor) {
+      ADD_FAILURE() << "failed to find PointerFlowTUSummaryExtractor";
+      return false;
+    }
+    Extractor->HandleTranslationUnit(AST->getASTContext());
+    return true;
+  }
+
+  template <typename ContributorDecl = NamedDecl>
+  const PointerFlowEntitySummary *getEntitySummary(FindEntityByName Name) {
+    const auto *ContributorDefn =
+        findEntityByName<ContributorDecl>(Name, AST->getASTContext());
+
+    if (!ContributorDefn) {
+      ADD_FAILURE() << "failed to find Decl of \"" << toStringRef(Name) << "\"";
       return nullptr;
+    }
 
     std::optional<EntityName> EN = getEntityName(ContributorDefn);
 
-    if (!EN)
+    if (!EN) {
+      ADD_FAILURE() << "failed to get EntityName for contributor \""
+                    << toStringRef(Name) << "\"";
       return nullptr;
+    }
+
+    EntityId ContributorEntityId = Builder.addEntity(*EN);
+    auto &TUSumData = getData(TUSum);
+    auto EntitiesSumIter =
+        TUSumData.find(PointerFlowEntitySummary::summaryName());
 
-    auto Sum = extractEntitySummary(*ExtractorProxy, ContributorDefn,
-                                    AST->getASTContext());
-    if (!Sum) {
-      llvm::consumeError(std::move(Sum.takeError()));
+    // If none entity summary was collected, it may not be an entry in
+    // `TUSumData`:
+    if (EntitiesSumIter == TUSumData.end())
       return nullptr;
-    }
-    assert(*Sum);
-    return std::move(*Sum);
+
+    auto EntitySumIter = EntitiesSumIter->second.find(ContributorEntityId);
+
+    // If entity summary is empty, it may not exist:
+    if (EntitySumIter == EntitiesSumIter->second.end())
+      return nullptr;
+    return static_cast<const PointerFlowEntitySummary *>(
+        EntitySumIter->second.get());
   }
 
 public:
   std::optional<EntityId> getEntityId(FindEntityByName Name) {
     if (const auto *D = findEntityByName(Name, AST->getASTContext())) {
       if (auto EntityName = getEntityName(D))
-        return addEntity(*ExtractorProxy, *EntityName);
+        return Builder.addEntity(*EntityName);
     }
     return std::nullopt;
   }
@@ -227,7 +223,7 @@ class PointerFlowTest : public testing::Test {
   std::optional<EntityId> getEntityIdForReturn(FindEntityByName FunName) {
     if (const auto *D = findFnByName(FunName, AST->getASTContext())) {
       if (auto EntityName = getEntityNameForReturn(D))
-        return addEntity(*ExtractorProxy, *EntityName);
+        return Builder.addEntity(*EntityName);
     }
     return std::nullopt;
   }
@@ -254,7 +250,7 @@ static constexpr auto ToEPL =
 
 EdgeSet
 PointerFlowTest::makeEdges(unsigned Line,
-                                  ArrayRef<std::pair<EPLPair, EPLPair>> Edges) {
+                           ArrayRef<std::pair<EPLPair, EPLPair>> Edges) {
   EdgeSet Result;
   for (auto Edge : Edges)
     Result[ToEPL(this, Line)(Edge.first)].insert(
@@ -263,69 +259,78 @@ PointerFlowTest::makeEdges(unsigned Line,
 }
 
 TEST_F(PointerFlowTest, IsExtractorRegisteredTest) {
-  EXPECT_TRUE(
-      isTUSummaryExtractorRegistered("PointerFlowTUSummaryExtractor"));
+  EXPECT_TRUE(isTUSummaryExtractorRegistered("PointerFlow"));
 }
 
 //////////////////////////////////////////////////////////////
 //          Simple Assign Tests                             //
 //////////////////////////////////////////////////////////////
 TEST_F(PointerFlowTest, SimpleAssign) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     void foo(int *p, int *q) {
       q = p;
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
 }
 
 TEST_F(PointerFlowTest, AssignWithSubscriptLHS) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     void foo(int **q, int *p, int x) {
       q[x] = p;
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 2U}, {"p", 1U}}}));
 }
 
 TEST_F(PointerFlowTest, AssignWithPtrArithRHS) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     void foo(int *p, int *q) {
       q = p + 5;
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
 }
 
 TEST_F(PointerFlowTest, AssignInSubscript) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     void foo(int *p, int *q) {
       (q = p)[5];
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
 }
 
 TEST_F(PointerFlowTest, MultipleAssign) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     void foo(int *p, int *q, int *r) {
       q = p;
       r = q;
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {
@@ -335,12 +340,14 @@ TEST_F(PointerFlowTest, MultipleAssign) {
 }
 
 TEST_F(PointerFlowTest, ChainedAssign) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     void foo(int *p, int *q, int *r) {
       r = q = p;
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {
@@ -350,38 +357,44 @@ TEST_F(PointerFlowTest, ChainedAssign) {
 }
 
 TEST_F(PointerFlowTest, CastToRValue) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     void foo(int *p, int *q) {
       q = static_cast<int *&&>(p);
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
 }
 
 TEST_F(PointerFlowTest, AssignToMember) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     struct S { int *field; };
     void foo(S s, int *p) {
       s.field = p;
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"field", 1U}, {"p", 1U}}}));
 }
 
 TEST_F(PointerFlowTest, AssignToMember2) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     struct S { int *field; };
     void foo(S *s, int *p) {
       s->field = p;
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"field", 1U}, {"p", 1U}}}));
@@ -391,26 +404,30 @@ TEST_F(PointerFlowTest, AssignToMember2) {
 //          Call Expr Tests.                                //
 //////////////////////////////////////////////////////////////
 TEST_F(PointerFlowTest, CallArg) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     void bar(int *param);
     void foo(int *p) {
       bar(p);
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"param", 1U}, {"p", 1U}}}));
 }
 
 TEST_F(PointerFlowTest, CallMultiArgs) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     void bar(int *param1, int y, int *param2);
     void foo(int *p, int x, int *q) {
       bar(p, x, q);
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {
@@ -420,14 +437,16 @@ TEST_F(PointerFlowTest, CallMultiArgs) {
 }
 
 TEST_F(PointerFlowTest, CallAsCallArg) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
 
     int *bar(int * w);
     void foo(int * p) {
       foo(bar(p));
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"w", 1U}, {"p", 1U}},
@@ -435,15 +454,17 @@ TEST_F(PointerFlowTest, CallAsCallArg) {
 }
 
 TEST_F(PointerFlowTest, CXXOperatorCallMultiArgs) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     struct S {
       int* operator()(int *a, int *b);
     };
     void foo(S obj, int *p, int *q) {
       foo(obj, obj(p, q), obj(p, q));
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {
@@ -455,15 +476,17 @@ TEST_F(PointerFlowTest, CXXOperatorCallMultiArgs) {
 }
 
 TEST_F(PointerFlowTest, CXXMemberCall) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     struct S {
       int* method(int *a, int *b);
     };
     void foo(S obj, int *p, int *q) {
       foo(obj, obj.method(p, q), obj.method(p, q));
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"a", 1U}, {"p", 1U}},
@@ -473,30 +496,34 @@ TEST_F(PointerFlowTest, CXXMemberCall) {
 }
 
 TEST_F(PointerFlowTest, VirtualMethodCall) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     struct Base {
       virtual void method(int *a);
     };
     void foo(Base &obj, int *p) {
       obj.method(p);
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"a", 1U}, {"p", 1U}}}));
 }
 
 TEST_F(PointerFlowTest, StaticMethodCall) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     struct S {
       static void method(int *a, int *b);
     };
     void foo(int *p, int *q) {
       S::method(p, q);
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {
@@ -506,14 +533,16 @@ TEST_F(PointerFlowTest, StaticMethodCall) {
 }
 
 TEST_F(PointerFlowTest, DefaultArg) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     int *g;
     void bar(int *a, int *b = g);
     void foo(int *p) {
       bar(p);
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__,
@@ -521,14 +550,16 @@ TEST_F(PointerFlowTest, DefaultArg) {
 }
 
 TEST_F(PointerFlowTest, DefaultArg2) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     int *g;
     void bar(int *a, int *b = g);
     void foo(int *p) {
       bar(p, p);
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {
@@ -541,15 +572,17 @@ TEST_F(PointerFlowTest, DefaultArg2) {
 //          CXX Ctor Tests.                                 //
 //////////////////////////////////////////////////////////////
 TEST_F(PointerFlowTest, CXXCtorCallMultiArgs) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     struct S {
       S(int *a, int *b) {}
     };
     void foo(int *p, int *q) {
       S s{p, q};
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {
@@ -559,15 +592,17 @@ TEST_F(PointerFlowTest, CXXCtorCallMultiArgs) {
 }
 
 TEST_F(PointerFlowTest, CXXCtorCallMultiArgs2) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     struct S {
       S(int *a, int x, int *b) {}
     };
     void foo(int *p, int x, int *q) {
       S s{p, x, q};
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {
@@ -577,7 +612,7 @@ TEST_F(PointerFlowTest, CXXCtorCallMultiArgs2) {
 }
 
 TEST_F(PointerFlowTest, CXXCtorCallAsCallArg) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     struct Wrapper {
       Wrapper(int *q) {}
     };
@@ -585,21 +620,25 @@ TEST_F(PointerFlowTest, CXXCtorCallAsCallArg) {
     void foo(int *p) {
       bar(Wrapper{p});
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
 }
 
 TEST_F(PointerFlowTest, DelegatingCXXCtorCall) {
-  auto Sum = setUpTest<CXXConstructorDecl>(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     struct S {
       S(int *a, int *b) {}
       S(int *p) : S(p, p) {}
     };
-  )cpp",
-                                           CXXCtorOfNumParms{"S", 1});
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary<CXXConstructorDecl>(CXXCtorOfNumParms{"S", 1});
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {
@@ -609,15 +648,17 @@ TEST_F(PointerFlowTest, DelegatingCXXCtorCall) {
 }
 
 TEST_F(PointerFlowTest, CXXCtorBaseInit) {
-  auto Sum = setUpTest<CXXConstructorDecl>(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     struct Base {
       Base(int *a) {}
     };
     struct Derived : Base {
       Derived(int *p) : Base(p) {}
     };
-  )cpp",
-                                           "Derived");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary<CXXConstructorDecl>("Derived");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"a", 1U}, {"p", 1U}}}));
@@ -627,38 +668,44 @@ TEST_F(PointerFlowTest, CXXCtorBaseInit) {
 //          Initializers Tests.                             //
 //////////////////////////////////////////////////////////////
 TEST_F(PointerFlowTest, LocalVarDeclInit) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     void foo(int *p) {
       int *q = p;
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
 }
 
 TEST_F(PointerFlowTest, LocalVarDeclInit2) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     void foo(int (*arr)[10]) {
       int (*p)[10] = arr;
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"p", 1U}, {"arr", 1U}}}));
 }
 
 TEST_F(PointerFlowTest, FieldInit) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     void foo(int *p) {
       struct Bar {
         int *field = p;
       };
     }
-  )cpp",
-                       "Bar");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary("Bar");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"field", 1U}, {"p", 1U}}}));
@@ -675,46 +722,54 @@ TEST_F(PointerFlowTest, CXXCtorMemberInit) {
     }
   )cpp";
 
-  auto Sum = setUpTest<CXXConstructorDecl>(Code, "Bar");
+  ASSERT_EQ(setUpTest(Code), true);
+  auto *Sum = getEntitySummary<CXXConstructorDecl>("Bar");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"member", 1U}, {"q", 1U}}}));
 
-  Sum = setUpTest(Code, "foo");
+  Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
 }
 
 TEST_F(PointerFlowTest, GlobalVarInit) {
-  auto Sum = setUpTest<VarDecl>(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     int *q;
     int *g = q;
-  )cpp",
-                                "g");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary<VarDecl>("g");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"g", 1U}, {"q", 1U}}}));
 }
 
 TEST_F(PointerFlowTest, StaticLocalInit) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     void foo(int *p) {
       static int *s = p;
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"s", 1U}, {"p", 1U}}}));
 }
 
 TEST_F(PointerFlowTest, StaticMemberInit) {
-  auto Sum = setUpTest<VarDecl>(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     int *g;
-    struct S { static int *member = g; };   
-  )cpp",
-                                "member");
+    struct S { static int *member; };
+    int *S::member = g;
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary<VarDecl>("member");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"member", 1U}, {"g", 1U}}}));
@@ -725,12 +780,14 @@ TEST_F(PointerFlowTest, StaticMemberInit) {
 //////////////////////////////////////////////////////////////
 
 TEST_F(PointerFlowTest, ArrayInitList) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     void foo(int *p, int *q) {
       int *arr[] = {p, q};
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {
@@ -740,13 +797,15 @@ TEST_F(PointerFlowTest, ArrayInitList) {
 }
 
 TEST_F(PointerFlowTest, StructInitList) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     struct S { int *a; int *b; };
     void foo(int *p, int *q) {
       S s = {p, q};
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {
@@ -757,27 +816,31 @@ TEST_F(PointerFlowTest, StructInitList) {
 
 // A union initialized with a brace-enclosed initializer:
 TEST_F(PointerFlowTest, UnionInitList) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     union U { int *x; int y; };
     void foo(int *p) {
       U u = {p};
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"x", 1U}, {"p", 1U}}}));
 }
 
 TEST_F(PointerFlowTest, NestedInitList) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     struct Inner { int * a; int * b; };
     struct S { Inner c; int * d; };
     void foo(int *p, int *q, int *r) {
       S s = {{p, q}, r};
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {
@@ -788,14 +851,16 @@ TEST_F(PointerFlowTest, NestedInitList) {
 }
 
 TEST_F(PointerFlowTest, NestedInitList2) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     union Inner { int * a; int b; };
     struct S { Inner c; int * d; };
     void foo(int *p, int *q) {
       S s = {{p}, q};
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {
@@ -805,14 +870,16 @@ TEST_F(PointerFlowTest, NestedInitList2) {
 }
 
 TEST_F(PointerFlowTest, NestedInitList3) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     struct Inner { int * a; int * b; };
     union S { Inner c; int * d; };
     void foo(int *p, int *q) {
       S s = {{p, q}};
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {
@@ -822,12 +889,14 @@ TEST_F(PointerFlowTest, NestedInitList3) {
 }
 
 TEST_F(PointerFlowTest, NestedArrayInitList) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     void foo(int *p, int *q, int *r, int *s) {
       int *arr[][2] = {{p, q}, {r, s}};
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {
@@ -839,13 +908,15 @@ TEST_F(PointerFlowTest, NestedArrayInitList) {
 }
 
 TEST_F(PointerFlowTest, MixedNestedArrayStructInitList) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     struct T { int *arr[2]; };
     void foo(int *p, int *q, int *r, int *s) {
       T t[2] = {{p, q}, {r, s}};
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {
@@ -857,13 +928,15 @@ TEST_F(PointerFlowTest, MixedNestedArrayStructInitList) {
 }
 
 TEST_F(PointerFlowTest, ArrayOfStructInitList) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     struct S { int *a; int *b; };
     void foo(int *p, int *q, int *r, int *s) {
       S arr[] = {{p, q}, {r, s}};
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {
@@ -879,26 +952,30 @@ TEST_F(PointerFlowTest, ArrayOfStructInitList) {
 //////////////////////////////////////////////////////////////
 
 TEST_F(PointerFlowTest, ReturnEdge) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     int *foo(int *p) {
       return p;
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"foo", 1U, true}, {"p", 1U}}}));
 }
 
 TEST_F(PointerFlowTest, MultipleReturnEdges) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     int *foo(int *p, int *q, bool cond) {
       if (cond)
         return p;
       return q;
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {
@@ -908,19 +985,21 @@ TEST_F(PointerFlowTest, MultipleReturnEdges) {
 }
 
 TEST_F(PointerFlowTest, NoReturnEdgeForNonPointerReturnType) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     int foo(int *p, int x) {
       return x;
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
 
-  ASSERT_NE(Sum, nullptr);
-  EXPECT_EQ(*Sum, makeEdges(__LINE__, {}));
+  auto *Sum = getEntitySummary("foo");
+
+  EXPECT_THAT(Sum, testing::AnyOf(testing::IsNull(),
+                                  testing::Pointee(makeEdges(__LINE__, {}))));
 }
 
 TEST_F(PointerFlowTest, ReturnEdgeNotFromNestedFunction) {
-  auto *Code = R"cpp(
+  StringRef Code = R"cpp(
     int *foo(int *p) {
       struct Inner {
         int *bar(int *q) { return q; }
@@ -928,54 +1007,58 @@ TEST_F(PointerFlowTest, ReturnEdgeNotFromNestedFunction) {
       return p;
     }
   )cpp";
-  auto Sum = setUpTest(Code, "foo");
+
+  ASSERT_EQ(setUpTest(Code), true);
+  auto *Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"foo", 1U, true}, {"p", 1U}}}));
 
-  Sum = setUpTest(Code, "bar");
+  Sum = getEntitySummary("bar");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"bar", 1U, true}, {"q", 1U}}}));
 }
 
 TEST_F(PointerFlowTest, ReturnEdgeInClassMethod) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
   void foo() {
     struct S {
       int *method(int *p, int *q) { return p; }
     };
   }
-  )cpp",
-                       "method");
+  )cpp"),
+            true);
+
+  auto *Sum = getEntitySummary("method");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"method", 1U, true}, {"p", 1U}}}));
 }
 
 TEST_F(PointerFlowTest, NoEdgeFromIndirectCall) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     void bar(int *param1);
     void baz(int *param2);
-    
+
     void foo(int *p, void (*fp)(int *)) {
       fp(p);
     }
 
-    void main() {
+    int main() {
       int *q;
       foo(q, bar);
       foo(q, baz);
+      return 0;
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
 
-  ASSERT_NE(Sum, nullptr);
-  EXPECT_EQ(
-      *Sum,
-      makeEdges(
-          __LINE__,
-          /* FIXME or TBD: Currently indirect calls produce no edge: */ {}));
+  /* FIXME or TBD: Currently indirect calls produce no edge: */
+  auto *Sum = getEntitySummary("foo");
+
+  EXPECT_THAT(Sum, testing::AnyOf(testing::IsNull(),
+                                  testing::Pointee(makeEdges(__LINE__, {}))));
 }
 
 //////////////////////////////////////////////////////////////
@@ -990,14 +1073,15 @@ TEST_F(PointerFlowTest, ReturnInLambda) {
     }
   )cpp";
 
-  auto Sum = setUpTest(Code, "foo");
+  ASSERT_EQ(setUpTest(Code), true);
+  auto *Sum = getEntitySummary("foo");
 
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"r", 1U}, {"p", 1U}},
                                        {{"foo", 1U, true},
                                         {LambdaOfVar{"local"}, 1U, true}}}));
 
-  Sum = setUpTest(Code, LambdaOfVar{"local"});
+  Sum = getEntitySummary(LambdaOfVar{"local"});
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__,
                             {{{LambdaOfVar{"local"}, 1U, true}, {"r", 1U}}}));
@@ -1013,11 +1097,12 @@ TEST_F(PointerFlowTest, NestedLambdaAssign) {
     }
   )cpp";
 
-  auto Sum = setUpTest(Code, LambdaOfVar{"outer_lambda"});
+  ASSERT_EQ(setUpTest(Code), true);
+  auto *Sum = getEntitySummary(LambdaOfVar{"outer_lambda"});
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"s", 1U}, {"r", 1U}}}));
 
-  Sum = setUpTest(Code, LambdaOfVar{"inner_lambda"});
+  Sum = getEntitySummary(LambdaOfVar{"inner_lambda"});
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"y", 1U}, {"x", 1U}}}));
 }

>From 33209adc6f4a6574ccb3260959b6934ce1ff110a Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Wed, 8 Apr 2026 15:16:50 -0700
Subject: [PATCH 5/5] add include of ExprCXX.h

---
 .../Analyses/SSAFAnalysesCommon.h                                | 1 +
 1 file changed, 1 insertion(+)

diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
index f4f0a12606c72..93d2ea6bad4de 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
@@ -17,6 +17,7 @@
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/DynamicRecursiveASTVisitor.h"
+#include "clang/AST/ExprCXX.h"
 #include "clang/Basic/SourceLocation.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/Twine.h"



More information about the llvm-branch-commits mailing list