[llvm-branch-commits] [clang] [NFC][SSAF][UnsafeBufferUsage] Separate EntityPointerLevel and UnsafeBufferUsage (PR #188648)

Ziqing Luo via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Thu Apr 9 19:21:21 PDT 2026


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

>From cb8c65a3e04198b27b9706d52b4dd4a99478a240 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Thu, 12 Mar 2026 14:10:19 -0700
Subject: [PATCH 01/30] Reapply "[clang][ssaf] Add UnsafeBufferUsage summary
 extractor for functions (#182941)"

This reverts commit 53739c75a8720aaef8032628267ed4fd050af038.

Reapply after module dependency issues are resolved.

(rdar://169191570)
---
 .../UnsafeBufferUsage/UnsafeBufferUsage.h     |  56 +--
 .../UnsafeBufferUsageExtractor.h              |  40 ++
 .../UnsafeBufferUsageBuilder.h                |  32 --
 .../Analyses/CMakeLists.txt                   |  15 +
 .../UnsafeBufferUsageExtractor.cpp            | 281 ++++++++++++
 .../CMakeLists.txt                            |   2 +
 .../UnsafeBufferUsageTest.cpp                 | 423 ++++++++++++++++--
 .../CMakeLists.txt                            |   1 +
 8 files changed, 743 insertions(+), 107 deletions(-)
 rename clang/include/clang/ScalableStaticAnalysisFramework/{Core => }/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h (62%)
 create mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h
 delete mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsageBuilder.h
 create mode 100644 clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
 create mode 100644 clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp

diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
similarity index 62%
rename from clang/include/clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
rename to clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
index 5b58ed0684333..5f418000723b4 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
@@ -6,13 +6,12 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGE_H
-#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGE_H
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGE_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGE_H
 
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/SummaryName.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
-#include "llvm/ADT/iterator_range.h"
 #include <set>
 
 namespace clang::ssaf {
@@ -26,23 +25,19 @@ namespace clang::ssaf {
 /// *' 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');
-///   - 'P.EntityId' identifies an lvalue object and 'P.PointerLevel == 0'.
-/// The latter case represents address-of expressions.
+/// 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, 1)' is associated with 'int *[10]' of 'p';
-/// '(p, 2)' is associated with 'int *' of 'p';
-/// '(p, 0)' represents '&p'.
+/// - '(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 UnsafeBufferUsageTUSummaryBuilder;
-  friend class UnsafeBufferUsageEntitySummary;
+  friend class UnsafeBufferUsageTUSummaryExtractor;
 
   EntityPointerLevel(EntityId Entity, unsigned PointerLevel)
       : Entity(Entity), PointerLevel(PointerLevel) {}
@@ -52,7 +47,8 @@ class EntityPointerLevel {
   unsigned getPointerLevel() const { return PointerLevel; }
 
   bool operator==(const EntityPointerLevel &Other) const {
-    return Entity == Other.Entity && PointerLevel == Other.PointerLevel;
+    return std::tie(Entity, PointerLevel) ==
+           std::tie(Other.Entity, Other.PointerLevel);
   }
 
   bool operator!=(const EntityPointerLevel &Other) const {
@@ -64,7 +60,8 @@ class EntityPointerLevel {
            std::tie(Other.Entity, Other.PointerLevel);
   }
 
-  // Comparator supporting partial comparison against EntityId:
+  /// Compares `EntityPointerLevel`s; additionally, partially compares
+  /// `EntityPointerLevel` with `EntityId`.
   struct Comparator {
     using is_transparent = void;
     bool operator()(const EntityPointerLevel &L,
@@ -88,33 +85,20 @@ using EntityPointerLevelSet =
 class UnsafeBufferUsageEntitySummary final : public EntitySummary {
   const EntityPointerLevelSet UnsafeBuffers;
 
-  friend class UnsafeBufferUsageTUSummaryBuilder;
+  friend class UnsafeBufferUsageTUSummaryExtractor;
 
-  UnsafeBufferUsageEntitySummary(EntityPointerLevelSet &&UnsafeBuffers)
+  UnsafeBufferUsageEntitySummary(EntityPointerLevelSet UnsafeBuffers)
       : EntitySummary(), UnsafeBuffers(std::move(UnsafeBuffers)) {}
 
 public:
-  using const_iterator = EntityPointerLevelSet::const_iterator;
-
-  const_iterator begin() const { return UnsafeBuffers.begin(); }
-  const_iterator end() const { return UnsafeBuffers.end(); }
-
-  const_iterator find(const EntityPointerLevel &V) const {
-    return UnsafeBuffers.find(V);
-  }
-
-  llvm::iterator_range<const_iterator> getSubsetOf(EntityId Entity) const {
-    return llvm::make_range(UnsafeBuffers.equal_range(Entity));
-  }
-
-  /// \return the size of the set of EntityLevelPointers, which represents the
-  /// set of unsafe buffers
-  size_t getNumUnsafeBuffers() { return UnsafeBuffers.size(); }
-
   SummaryName getSummaryName() const override {
     return SummaryName{"UnsafeBufferUsage"};
   };
+
+  bool operator==(const EntityPointerLevelSet &Other) const {
+    return UnsafeBuffers == Other;
+  }
 };
 } // namespace clang::ssaf
 
-#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGE_H
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGE_H
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h
new file mode 100644
index 0000000000000..765b2c37562ce
--- /dev/null
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h
@@ -0,0 +1,40 @@
+//===- UnsafeBufferUsageExtractor.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_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEBUILDER_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEBUILDER_H
+
+#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.h"
+#include "llvm/Support/Error.h"
+#include <memory>
+
+namespace clang::ssaf {
+class UnsafeBufferUsageTUSummaryExtractor : public TUSummaryExtractor {
+public:
+  UnsafeBufferUsageTUSummaryExtractor(TUSummaryBuilder &Builder)
+      : TUSummaryExtractor(Builder) {}
+
+  static EntityPointerLevel buildEntityPointerLevel(EntityId Entity,
+                                                    unsigned PointerLevel) {
+    return {Entity, PointerLevel};
+  }
+
+  EntityId addEntity(EntityName EN) { return SummaryBuilder.addEntity(EN); }
+
+  std::unique_ptr<UnsafeBufferUsageEntitySummary>
+  extractEntitySummary(const Decl *Contributor, ASTContext &Ctx,
+                       llvm::Error &Error);
+
+  void HandleTranslationUnit(ASTContext &Ctx) override;
+};
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEBUILDER_H
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsageBuilder.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsageBuilder.h
deleted file mode 100644
index db6c097510a57..0000000000000
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsageBuilder.h
+++ /dev/null
@@ -1,32 +0,0 @@
-//===- UnsafeBufferUsageBuilder.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_CORE_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEBUILDER_H
-#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEBUILDER_H
-
-#include "clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
-#include <memory>
-
-namespace clang::ssaf {
-class UnsafeBufferUsageTUSummaryBuilder : public TUSummaryBuilder {
-public:
-  static EntityPointerLevel buildEntityPointerLevel(EntityId Entity,
-                                                    unsigned PointerLevel) {
-    return {Entity, PointerLevel};
-  }
-
-  static std::unique_ptr<UnsafeBufferUsageEntitySummary>
-  buildUnsafeBufferUsageEntitySummary(EntityPointerLevelSet &&UnsafeBuffers) {
-    return std::make_unique<UnsafeBufferUsageEntitySummary>(
-        UnsafeBufferUsageEntitySummary(std::move(UnsafeBuffers)));
-  }
-};
-} // namespace clang::ssaf
-
-#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEBUILDER_H
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
new file mode 100644
index 0000000000000..34c6dd9b61203
--- /dev/null
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
@@ -0,0 +1,15 @@
+set(LLVM_LINK_COMPONENTS
+  Support
+  )
+
+add_clang_library(clangScalableStaticAnalysisFrameworkAnalyses
+  UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
+
+  LINK_LIBS
+  clangAST
+  clangAnalysis
+  clangBasic
+  clangScalableStaticAnalysisFrameworkCore
+
+  DEPENDS
+  )
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
new file mode 100644
index 0000000000000..c150b2918db59
--- /dev/null
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
@@ -0,0 +1,281 @@
+//===- UnsafeBufferUsageExtractor.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/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DynamicRecursiveASTVisitor.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/iterator_range.h"
+#include "llvm/Support/Error.h"
+#include <memory>
+
+namespace {
+using namespace clang;
+using namespace ssaf;
+
+static bool hasPointerType(const Expr *E) {
+  auto Ty = E->getType();
+  return !Ty.isNull() && !Ty->isFunctionPointerType() &&
+         (Ty->isPointerType() || Ty->isArrayType());
+}
+
+constexpr inline auto buildEntityPointerLevel =
+    UnsafeBufferUsageTUSummaryExtractor::buildEntityPointerLevel;
+
+// Translate a pointer 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.
+//
+// The translation is a process of traversing into the pointer 'E' until its
+// base address can be represented by an entity, with the number of dereferences
+// tracked by incrementing the pointer level.  Naturally, taking address of, as
+// the inverse operation of dereference, is tracked by decrementing the pointer
+// level.
+//
+// For example, suppose there are pointers and arrays declared as
+//   int *ptr, **p1, **p2;
+//   int arr[10][10];
+// , the translation of expressions involving these base addresses will be:
+//   Translate(ptr + 5)            -> {(ptr, 1)}
+//   Translate(arr[5])             -> {(arr, 2)}
+//   Translate(cond ? p1[5] : p2)  -> {(p1, 2), (p2, 1)}
+//   Translate(&arr[5])            -> {(arr, 1)}
+class EntityPointerLevelTranslator
+    : ConstStmtVisitor<EntityPointerLevelTranslator,
+                       Expected<EntityPointerLevelSet>> {
+  friend class StmtVisitorBase;
+
+  // Fallback method for all unsupported expression kind:
+  llvm::Error fallback(const Stmt *E) {
+    return llvm::createStringError(
+        "unsupported expression kind for translation to "
+        "EntityPointerLevel: %s",
+        E->getStmtClassName());
+  }
+
+  static EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E) {
+    return buildEntityPointerLevel(E.getEntity(), E.getPointerLevel() + 1);
+  }
+
+  static EntityPointerLevel decrementPointerLevel(const EntityPointerLevel &E) {
+    assert(E.getPointerLevel() > 0);
+    return buildEntityPointerLevel(E.getEntity(), E.getPointerLevel() - 1);
+  }
+
+  EntityPointerLevel createEntityPointerLevelFor(const EntityName &Name) {
+    return buildEntityPointerLevel(Extractor.addEntity(Name), 1);
+  }
+
+  // The common helper function for Translate(*base):
+  // Translate(*base) -> Translate(base) with .pointerLevel + 1
+  Expected<EntityPointerLevelSet> translateDereferencePointer(const Expr *Ptr) {
+    assert(hasPointerType(Ptr));
+
+    Expected<EntityPointerLevelSet> SubResult = Visit(Ptr);
+    if (!SubResult)
+      return SubResult.takeError();
+
+    auto Incremented = llvm::map_range(*SubResult, incrementPointerLevel);
+    return EntityPointerLevelSet{Incremented.begin(), Incremented.end()};
+  }
+
+  UnsafeBufferUsageTUSummaryExtractor &Extractor;
+
+public:
+  EntityPointerLevelTranslator(UnsafeBufferUsageTUSummaryExtractor &Extractor)
+      : Extractor(Extractor) {}
+
+  Expected<EntityPointerLevelSet> translate(const Expr *E) { return Visit(E); }
+
+private:
+  Expected<EntityPointerLevelSet> VisitStmt(const Stmt *E) {
+    return fallback(E);
+  }
+
+  // Translate(base + x)           -> Translate(base)
+  // Translate(x + base)           -> Translate(base)
+  // Translate(base - x)           -> Translate(base)
+  // Translate(base {+=, -=, =} x) -> Translate(base)
+  // Translate(x, base)            -> Translate(base)
+  Expected<EntityPointerLevelSet> VisitBinaryOperator(const BinaryOperator *E) {
+    switch (E->getOpcode()) {
+    case clang::BO_Add:
+      if (hasPointerType(E->getLHS()))
+        return Visit(E->getLHS());
+      return Visit(E->getRHS());
+    case clang::BO_Sub:
+    case clang::BO_AddAssign:
+    case clang::BO_SubAssign:
+    case clang::BO_Assign:
+      return Visit(E->getLHS());
+    case clang::BO_Comma:
+      return Visit(E->getRHS());
+    default:
+      return fallback(E);
+    }
+  }
+
+  // Translate({++, --}base)   -> Translate(base)
+  // Translate(base{++, --})   -> Translate(base)
+  // Translate(*base)          -> Translate(base) with .pointerLevel += 1
+  // Translate(&base)          -> {}, if Translate(base) is {}
+  //                           -> Translate(base) with .pointerLevel -= 1
+  Expected<EntityPointerLevelSet> VisitUnaryOperator(const UnaryOperator *E) {
+    switch (E->getOpcode()) {
+    case clang::UO_PostInc:
+    case clang::UO_PostDec:
+    case clang::UO_PreInc:
+    case clang::UO_PreDec:
+      return Visit(E->getSubExpr());
+    case clang::UO_AddrOf: {
+      Expected<EntityPointerLevelSet> SubResult = Visit(E->getSubExpr());
+      if (!SubResult)
+        return SubResult.takeError();
+
+      auto Decremented = llvm::map_range(*SubResult, decrementPointerLevel);
+      return EntityPointerLevelSet{Decremented.begin(), Decremented.end()};
+    }
+    case clang::UO_Deref:
+      return translateDereferencePointer(E->getSubExpr());
+    default:
+      return fallback(E);
+    }
+  }
+
+  // Translate((T*)base) -> Translate(p) if p has pointer type
+  //                     -> {} otherwise
+  Expected<EntityPointerLevelSet> VisitCastExpr(const CastExpr *E) {
+    if (hasPointerType(E->getSubExpr()))
+      return Visit(E->getSubExpr());
+    return EntityPointerLevelSet{};
+  }
+
+  // Translate(f(...)) -> {} if it is an indirect call
+  //                   -> {(f_return, 1)}, otherwise
+  Expected<EntityPointerLevelSet> VisitCallExpr(const CallExpr *E) {
+    if (auto *FD = E->getDirectCallee())
+      if (auto FDEntityName = getEntityNameForReturn(FD))
+        return EntityPointerLevelSet{
+            createEntityPointerLevelFor(*FDEntityName)};
+    return EntityPointerLevelSet{};
+  }
+
+  // Translate(base[x]) -> Translate(*base)
+  Expected<EntityPointerLevelSet>
+  VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+    return translateDereferencePointer(E->getBase());
+  }
+
+  // Translate(cond ? base1 : base2) := Translate(base1) U Translate(base2)
+  Expected<EntityPointerLevelSet>
+  VisitAbstractConditionalOperator(const AbstractConditionalOperator *E) {
+    Expected<EntityPointerLevelSet> ReT = Visit(E->getTrueExpr());
+    Expected<EntityPointerLevelSet> ReF = Visit(E->getFalseExpr());
+
+    if (ReT && ReF) {
+      ReT->insert(ReF->begin(), ReF->end());
+      return ReT;
+    }
+    if (!ReF && !ReT)
+      return llvm::joinErrors(ReT.takeError(), ReF.takeError());
+    if (!ReF)
+      return ReF.takeError();
+    return ReT.takeError();
+  }
+
+  Expected<EntityPointerLevelSet> VisitParenExpr(const ParenExpr *E) {
+    return Visit(E->getSubExpr());
+  }
+
+  // Translate("string-literal") -> {}
+  // Buffer accesses on string literals are unsafe, but string literals are not
+  // entities so there is no EntityPointerLevel associated with it.
+  Expected<EntityPointerLevelSet> VisitStringLiteral(const StringLiteral *E) {
+    return EntityPointerLevelSet{};
+  }
+
+  // Translate(DRE) -> {(Decl, 1)}
+  Expected<EntityPointerLevelSet> VisitDeclRefExpr(const DeclRefExpr *E) {
+    if (auto EntityName = getEntityName(E->getDecl()))
+      return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
+    return llvm::createStringError(
+        "failed to create entity name from the Decl of " +
+        E->getNameInfo().getAsString());
+  }
+
+  // Translate({., ->}f) -> {(MemberDecl, 1)}
+  Expected<EntityPointerLevelSet> VisitMemberExpr(const MemberExpr *E) {
+    if (auto EntityName = getEntityName(E->getMemberDecl()))
+      return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
+    return llvm::createStringError(
+        "failed to create entity name from the MemberDecl of " +
+        E->getMemberDecl()->getNameAsString());
+  }
+
+  Expected<EntityPointerLevelSet>
+  VisitOpaqueValueExpr(const OpaqueValueExpr *S) {
+    return Visit(S->getSourceExpr());
+  }
+};
+
+Expected<EntityPointerLevelSet>
+buildEntityPointerLevels(std::set<const Expr *> &&UnsafePointers,
+                         UnsafeBufferUsageTUSummaryExtractor &Extractor) {
+  EntityPointerLevelSet Result{};
+  EntityPointerLevelTranslator Translator{Extractor};
+  llvm::Error AllErrors = llvm::ErrorSuccess();
+
+  for (const Expr *Ptr : UnsafePointers) {
+    Expected<EntityPointerLevelSet> Translation = Translator.translate(Ptr);
+
+    if (Translation) {
+      // Filter out those temporary invalid EntityPointerLevels associated with
+      // `&E` pointers:
+      auto FilteredTranslation = llvm::make_filter_range(
+          *Translation, [](const EntityPointerLevel &E) -> bool {
+            return E.getPointerLevel() > 0;
+          });
+      Result.insert(FilteredTranslation.begin(), FilteredTranslation.end());
+      continue;
+    }
+    AllErrors = llvm::joinErrors(std::move(AllErrors), Translation.takeError());
+  }
+  if (AllErrors)
+    return AllErrors;
+  return Result;
+}
+} // namespace
+
+std::unique_ptr<UnsafeBufferUsageEntitySummary>
+UnsafeBufferUsageTUSummaryExtractor::extractEntitySummary(
+    const Decl *Contributor, ASTContext &Ctx, llvm::Error &Error) {
+  // FIXME: findUnsafePointers should accept more kinds of `Decl`s than just
+  // `FunctionDecl`:
+  if (const auto *FD = dyn_cast<FunctionDecl>(Contributor)) {
+    Expected<EntityPointerLevelSet> EPLs =
+        buildEntityPointerLevels(findUnsafePointers(FD), *this);
+
+    if (EPLs)
+      return std::make_unique<UnsafeBufferUsageEntitySummary>(
+          UnsafeBufferUsageEntitySummary(std::move(*EPLs)));
+    Error = EPLs.takeError();
+  }
+  return nullptr;
+}
+
+void UnsafeBufferUsageTUSummaryExtractor::HandleTranslationUnit(
+    ASTContext &Ctx) {
+  // FIXME: impl me!
+}
diff --git a/clang/lib/ScalableStaticAnalysisFramework/CMakeLists.txt b/clang/lib/ScalableStaticAnalysisFramework/CMakeLists.txt
index d3d75430233fe..6bcb1a0038127 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/CMakeLists.txt
+++ b/clang/lib/ScalableStaticAnalysisFramework/CMakeLists.txt
@@ -1,2 +1,4 @@
+add_subdirectory(Analyses)
 add_subdirectory(Core)
 add_subdirectory(Frontend)
+
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index 082ff7f8ac175..81c742dc30811 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -6,37 +6,144 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsageBuilder.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "clang/AST/DynamicRecursiveASTVisitor.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityIdTable.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummary.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
+#include "clang/Tooling/Tooling.h"
+#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
 using namespace clang;
 using namespace ssaf;
+using testing::UnorderedElementsAre;
 
 namespace {
 
+template <typename SomeDecl = NamedDecl>
+const SomeDecl *findDeclByName(StringRef Name, ASTContext &Ctx) {
+  class NamedDeclFinder : public DynamicRecursiveASTVisitor {
+  public:
+    StringRef SearchingName;
+    const NamedDecl *FoundDecl = nullptr;
+
+    NamedDeclFinder(StringRef SearchingName) : SearchingName(SearchingName) {}
+
+    bool VisitDecl(Decl *D) override {
+      if (const auto *ND = dyn_cast<NamedDecl>(D)) {
+        if (ND->getNameAsString() == SearchingName) {
+          FoundDecl = ND;
+          return false;
+        }
+      }
+      return true;
+    }
+  };
+
+  NamedDeclFinder Finder(Name);
+
+  Finder.TraverseDecl(Ctx.getTranslationUnitDecl());
+  return dyn_cast_or_null<SomeDecl>(Finder.FoundDecl);
+}
+
+const FunctionDecl *findFnByName(StringRef Name, ASTContext &Ctx) {
+  return findDeclByName<FunctionDecl>(Name, Ctx);
+}
+
 constexpr inline auto buildEntityPointerLevel =
-    UnsafeBufferUsageTUSummaryBuilder::buildEntityPointerLevel;
-constexpr inline auto buildUnsafeBufferUsageEntitySummary =
-    UnsafeBufferUsageTUSummaryBuilder::buildUnsafeBufferUsageEntitySummary;
+    UnsafeBufferUsageTUSummaryExtractor::buildEntityPointerLevel;
 
 class UnsafeBufferUsageTest : public testing::Test {
 protected:
-  EntityIdTable Table;
+  TUSummary TUSum;
+  TUSummaryBuilder TUSummaryBuilder;
+  UnsafeBufferUsageTUSummaryExtractor Extractor;
+  std::unique_ptr<ASTUnit> AST;
+
+  UnsafeBufferUsageTest()
+      : TUSum(BuildNamespace(BuildNamespaceKind::CompilationUnit, "Mock.cpp")),
+        TUSummaryBuilder(TUSum), Extractor(TUSummaryBuilder) {}
+
+  std::unique_ptr<UnsafeBufferUsageEntitySummary>
+  setUpTest(StringRef Code, StringRef ContributorName) {
+    AST = tooling::buildASTFromCodeWithArgs(
+        Code, {"-Wno-unused-value -Wno-int-to-pointer-cast"});
+
+    const auto *ContributorDefn =
+        findDeclByName(ContributorName, AST->getASTContext());
+    std::optional<EntityName> EN = getEntityName(ContributorDefn);
+
+    if (!ContributorDefn || !EN)
+      return nullptr;
+
+    llvm::Error Error = llvm::ErrorSuccess();
+    auto Sum = Extractor.extractEntitySummary(ContributorDefn,
+                                              AST->getASTContext(), Error);
+
+    if (Error) {
+      llvm::consumeError(std::move(Error));
+      return nullptr;
+    }
+    return Sum;
+  }
+
+  std::optional<EntityId> getEntityId(StringRef Name) {
+    if (const auto *D = findDeclByName(Name, AST->getASTContext()))
+      if (auto EntityName = getEntityName(D))
+        return Extractor.addEntity(*EntityName);
+    return std::nullopt;
+  }
+
+  std::optional<EntityId> getEntityIdForReturn(StringRef FunName) {
+    if (const auto *D = findFnByName(FunName, AST->getASTContext()))
+      if (auto EntityName = getEntityNameForReturn(D))
+        return Extractor.addEntity(*EntityName);
+    return std::nullopt;
+  }
+
+  // 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:
+  struct EPLPair {
+    EPLPair(StringRef Name, unsigned Lv, bool isFunRet = false)
+        : Name(Name), Lv(Lv), isFunRet(isFunRet) {}
+
+    StringRef Name;
+    unsigned Lv;
+    bool isFunRet;
+  };
+
+  EntityPointerLevelSet makeSet(unsigned Line, ArrayRef<EPLPair> Pairs) {
+    auto EPLs = llvm::map_range(
+        Pairs, [this, Line](const EPLPair &Pair) -> EntityPointerLevel {
+          std::optional<EntityId> Entity = Pair.isFunRet
+                                               ? getEntityIdForReturn(Pair.Name)
+                                               : getEntityId(Pair.Name);
+          if (!Entity)
+            ADD_FAILURE_AT(__FILE__, Line) << "Entity not found: " << Pair.Name;
+          return buildEntityPointerLevel(*Entity, Pair.Lv);
+        });
+    return EntityPointerLevelSet{EPLs.begin(), EPLs.end()};
+  }
 };
 
 //////////////////////////////////////////////////////////////
 //                   Data Structure Tests                   //
 //////////////////////////////////////////////////////////////
 
-#define EXPECT_CONTAINS(Set, Elt) EXPECT_NE((Set).find(Elt), (Set).end())
-#define EXPECT_EXCLUDES(Set, Elt) EXPECT_EQ((Set).find(Elt), (Set).end())
+static llvm::iterator_range<EntityPointerLevelSet::iterator>
+getSubsetOf(const EntityPointerLevelSet &Set, EntityId Entity) {
+  return llvm::make_range(Set.equal_range(Entity));
+}
 
 TEST_F(UnsafeBufferUsageTest, EntityPointerLevelComparison) {
-  EntityId E1 = Table.getId({"c:@F at foo", "", {}});
-  EntityId E2 = Table.getId({"c:@F at bar", "", {}});
+  EntityId E1 = Extractor.addEntity({"c:@F at foo", "", {}});
+  EntityId E2 = Extractor.addEntity({"c:@F at bar", "", {}});
 
   auto P1 = buildEntityPointerLevel(E1, 2);
   auto P2 = buildEntityPointerLevel(E1, 2);
@@ -53,48 +160,286 @@ TEST_F(UnsafeBufferUsageTest, EntityPointerLevelComparison) {
   EXPECT_FALSE(P2 < P1);
 }
 
-TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageEntitySummaryTest) {
-  EntityId E1 = Table.getId({"c:@F at foo", "", {}});
-  EntityId E2 = Table.getId({"c:@F at bar", "", {}});
-  EntityId E3 = Table.getId({"c:@F at baz", "", {}});
+TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageEntityPointerLevelSetTest) {
+  EntityId E1 = Extractor.addEntity({"c:@F at foo", "", {}});
+  EntityId E2 = Extractor.addEntity({"c:@F at bar", "", {}});
+  EntityId E3 = Extractor.addEntity({"c:@F at baz", "", {}});
 
   auto P1 = buildEntityPointerLevel(E1, 1);
   auto P2 = buildEntityPointerLevel(E1, 2);
   auto P3 = buildEntityPointerLevel(E2, 1);
   auto P4 = buildEntityPointerLevel(E2, 2);
   auto P5 = buildEntityPointerLevel(E3, 1);
-  auto P6 = buildEntityPointerLevel(E3, 2);
 
   EntityPointerLevelSet Set{P1, P2, P3, P4, P5};
-  auto ES = buildUnsafeBufferUsageEntitySummary(std::move(Set));
-  ASSERT_TRUE(ES);
 
-  EXPECT_CONTAINS(*ES, P1);
-  EXPECT_CONTAINS(*ES, P2);
-  EXPECT_CONTAINS(*ES, P3);
-  EXPECT_CONTAINS(*ES, P4);
-  EXPECT_CONTAINS(*ES, P5);
-  EXPECT_EXCLUDES(*ES, P6);
+  EXPECT_THAT(Set, UnorderedElementsAre(P1, P2, P3, P4, P5));
+  EXPECT_THAT(getSubsetOf(Set, E1), UnorderedElementsAre(P1, P2));
+  EXPECT_THAT(getSubsetOf(Set, E2), UnorderedElementsAre(P3, P4));
+  EXPECT_THAT(getSubsetOf(Set, E3), UnorderedElementsAre(P5));
+}
+
+//////////////////////////////////////////////////////////////
+//                   Extractor Tests                        //
+//////////////////////////////////////////////////////////////
+
+TEST_F(UnsafeBufferUsageTest, SimpleFunctionWithUnsafePointer) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int *p) {
+      p[5];
+    }
+  )cpp",
+                       "foo");
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}}));
+}
 
-  EntityPointerLevelSet Subset1{ES->getSubsetOf(E1).begin(),
-                                ES->getSubsetOf(E1).end()};
+TEST_F(UnsafeBufferUsageTest, PointerArithmetic) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int *p, int *q) {
+      *(p + 5);
+      *(q - 3);
+    }
+  )cpp",
+                       "foo");
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, PointerIncrementDecrement) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int *p, int *q, int *r, int *s) {
+      (++p)[5];
+      (q++)[5];
+      (--r)[5];
+      (s--)[5];
+    }
+  )cpp",
+                       "foo");
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum,
+            makeSet(__LINE__, {{"p", 1U}, {"q", 1U}, {"r", 1U}, {"s", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, PointerAssignment) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int *p, int *q) {
+      (p = q + 5)[5];
+    }
+  )cpp",
+                       "foo");
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, CompoundAssignment) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int *p, int *q) {
+      (p += 5)[5];
+      (q -= 3)[5];
+    }
+  )cpp",
+                       "foo");
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}}));
+}
 
-  EXPECT_CONTAINS(Subset1, P1);
-  EXPECT_CONTAINS(Subset1, P2);
-  EXPECT_EQ(Subset1.size(), 2U);
+TEST_F(UnsafeBufferUsageTest, MultiLevelPointer) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int **p, int **q, int **r) {
+      (*p)[5];
+      *(*q);
+      *(q[5]);
+      r[5][5];
+    }
+  )cpp",
+                       "foo");
 
-  EntityPointerLevelSet Subset2{ES->getSubsetOf(E2).begin(),
-                                ES->getSubsetOf(E2).end()};
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum,
+            makeSet(__LINE__, {{"p", 2U}, {"q", 1U}, {"r", 1U}, {"r", 2U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, ConditionalOperator) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int **p, int **q, int cond) {
+      (cond ? *p : *q)[5];
+      cond ? p[5] : q[5];
+    }
+  )cpp",
+                       "foo");
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum,
+            makeSet(__LINE__, {{"p", 1U}, {"q", 1U}, {"p", 2U}, {"q", 2U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, CastExpression) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(void *p, int q) {
+      ((int*)p)[5];
+      ((int*)q)[5];
+    }
+  )cpp",
+                       "foo");
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, CommaOperator) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int *p, int x) {
+      (x++, p)[5];
+    }
+  )cpp",
+                       "foo");
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, CommaOperator2) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int **p, int **q, int x) {
+      (p[x] = 0, q[x] = 0)[5];
+    }
+  )cpp",
+                       "foo");
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}, {"q", 2U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, ParenthesizedExpression) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int *p) {
+      (((p)))[5];
+    }
+  )cpp",
+                       "foo");
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, ArrayParameter) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int arr[], int arr2[][10]) {
+      int n = 5;
+      arr[100];
+      arr2[5][n];
+    }
+  )cpp",
+                       "foo");
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"arr", 1U}, {"arr2", 1U}, {"arr2", 2U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, FunctionCall) {
+  auto Sum = setUpTest(R"cpp(
+    int ** (*fp)();
+    int ** foo() {
+      fp = &foo;
+      foo()[5];
+      (*fp())[5];
+    }
+  )cpp",
+                       "foo");
+
+  EXPECT_NE(Sum, nullptr);
+  // No (foo, 2) becasue indirect calls are ignored.
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"foo", 1U, true}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, StructMemberAccess) {
+  auto Sum = setUpTest(R"cpp(
+    struct S {
+      int *ptr;
+      int (*ptr_to_arr)[10];
+    };
+    void foo(struct S obj) {
+      int n = 5;
+      obj.ptr[5];
+      (*obj.ptr_to_arr)[n];
+    }
+  )cpp",
+                       "foo");
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"ptr", 1U}, {"ptr_to_arr", 2U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, StringLiteralSubscript) {
+  auto Sum = setUpTest(R"cpp(
+    void foo() {
+      "hello"[5];
+    }
+  )cpp",
+                       "foo");
+
+  EXPECT_NE(Sum, nullptr);
+  // String literals should not generate pointer kind variables
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {}));
+}
+
+TEST_F(UnsafeBufferUsageTest, OpaqueValueExpr) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int *p, int *q) {
+       (p ?: q)[5];
+    }
+  )cpp",
+                       "foo");
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, AddressOfOperator) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int x) {
+      (&x)[5];
+    }
+  )cpp",
+                       "foo");
+
+  EXPECT_NE(Sum, nullptr);
+  // Address-of should not generate pointer kind variables for 'x'
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {}));
+}
+
+TEST_F(UnsafeBufferUsageTest, AddressOfThenDereference) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int *p, int *q) {
+      (*(&p))[5];
+      (&(*q))[5];
+    }
+  )cpp",
+                       "foo");
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1}, {"q", 1}}));
+}
 
-  EXPECT_CONTAINS(Subset2, P3);
-  EXPECT_CONTAINS(Subset2, P4);
-  EXPECT_EQ(Subset2.size(), 2U);
+TEST_F(UnsafeBufferUsageTest, PointerToArrayOfPointers) {
+  auto Sum = setUpTest(R"cpp(
+    void foo() {
+      int * arr[10];
+      int * (*p)[10] = arr;
 
-  EntityPointerLevelSet Subset3{ES->getSubsetOf(E3).begin(),
-                                ES->getSubsetOf(E3).end()};
+      (*p)[5][5]; // '(*p)[5]' is unsafe 
+                  // '(*p)' is fine because 5 < 10
+    }
+  )cpp",
+                       "foo");
 
-  EXPECT_CONTAINS(Subset3, P5);
-  EXPECT_EXCLUDES(Subset3, P6);
-  EXPECT_EQ(Subset3.size(), 1U);
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 3}}));
 }
 } // namespace
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt b/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
index 871d9e6b0c02c..f541e81d42789 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
+++ b/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
@@ -29,6 +29,7 @@ add_distinct_clang_unittest(ClangScalableAnalysisTests
   clangASTMatchers
   clangBasic
   clangFrontend
+  clangScalableStaticAnalysisFrameworkAnalyses
   clangScalableStaticAnalysisFrameworkCore
   clangScalableStaticAnalysisFrameworkFrontend
   clangSerialization

>From 09fcf76d7f0e854bcb2fc3521e4692d0f1974efd Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Thu, 5 Mar 2026 13:00:44 -0800
Subject: [PATCH 02/30] [ssaf][UnsafeBufferUsage] Add support for extracting
 unsafe pointers from all kinds of contributors

- Generalize the -Wunsafe-buffer-usage API for finding unsafe pointers in all kinds of Decls
- Add support in SSAF-based UnsafeBufferUsage analysis for extracting from various contributors
- Mock implementation of HandleTranslationUnit

rdar://171735836
---
 .../Analysis/Analyses/UnsafeBufferUsage.h     |   9 +-
 .../UnsafeBufferUsage/UnsafeBufferUsage.h     |   2 +
 clang/lib/Analysis/UnsafeBufferUsage.cpp      |  61 +++++---
 .../UnsafeBufferUsageExtractor.cpp            | 140 ++++++++++++++----
 .../UnsafeBufferUsageTest.cpp                 |  62 +++++++-
 5 files changed, 226 insertions(+), 48 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
index 876682ad779d4..e0d583c735e61 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
@@ -201,7 +201,14 @@ bool anyConflict(const llvm::SmallVectorImpl<FixItHint> &FixIts,
                  const SourceManager &SM);
 } // namespace internal
 
-std::set<const Expr *> findUnsafePointers(const FunctionDecl *FD);
+/// Find unsafe pointers in body/initializer of `D`, if `D` is one of the
+/// followings:
+///   VarDecl
+///   FieldDecl
+///   FunctionDecl
+///   BlockDecl
+///   ObjCMethodDecl
+std::set<const Expr *> findUnsafePointers(const Decl *D);
 } // end namespace clang
 
 #endif /* LLVM_CLANG_ANALYSIS_ANALYSES_UNSAFEBUFFERUSAGE_H */
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
index 5f418000723b4..c0a4c2f76ab48 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
@@ -98,6 +98,8 @@ class UnsafeBufferUsageEntitySummary final : public EntitySummary {
   bool operator==(const EntityPointerLevelSet &Other) const {
     return UnsafeBuffers == Other;
   }
+
+  bool empty() const { return UnsafeBuffers.empty(); }
 };
 } // namespace clang::ssaf
 
diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 133e39b8fac2b..d6f554246a1b4 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -28,6 +28,7 @@
 #include "clang/Lex/Preprocessor.h"
 #include "llvm/ADT/APInt.h"
 #include "llvm/ADT/APSInt.h"
+#include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/STLFunctionalExtras.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
@@ -36,6 +37,7 @@
 #include <queue>
 #include <set>
 #include <sstream>
+#include <vector>
 
 using namespace clang;
 
@@ -2942,7 +2944,37 @@ template <typename NodeTy> struct CompareNode {
   }
 };
 
-std::set<const Expr *> clang::findUnsafePointers(const FunctionDecl *FD) {
+// Populate `Stmts` with the body/initializer Stmt of `D`, if `D` is one of the
+// followings:
+//   VarDecl
+//   FieldDecl
+//   FunctionDecl
+//   BlockDecl
+//   ObjCMethodDecl
+static void populateStmtsForFindingGadgets(SmallVector<const Stmt *> &Stmts,
+                                           const Decl *D) {
+  auto AddStmt = [&Stmts](const Stmt *S) {
+    if (S)
+      Stmts.push_back(S);
+  };
+  if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
+    AddStmt(FD->getBody());
+    for (const auto *PD : FD->parameters())
+      AddStmt(PD->getDefaultArg());
+    if (const auto *CtorD = dyn_cast<CXXConstructorDecl>(FD))
+      llvm::append_range(
+          Stmts, llvm::map_range(CtorD->inits(),
+                                 std::mem_fn(&CXXCtorInitializer::getInit)));
+  } else 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)) {
+    AddStmt(FD->getInClassInitializer());
+  }
+}
+
+std::set<const Expr *> clang::findUnsafePointers(const Decl *D) {
   class MockReporter : public UnsafeBufferUsageHandler {
   public:
     MockReporter() {}
@@ -2981,9 +3013,13 @@ std::set<const Expr *> clang::findUnsafePointers(const FunctionDecl *FD) {
   WarningGadgetList WarningGadgets;
   DeclUseTracker Tracker;
   MockReporter IgnoreHandler;
+  ASTContext &Ctx = D->getASTContext();
+  SmallVector<const Stmt *> Stmts;
 
-  findGadgets(FD->getBody(), FD->getASTContext(), IgnoreHandler, false,
-              FixableGadgets, WarningGadgets, Tracker);
+  populateStmtsForFindingGadgets(Stmts, D);
+  for (auto *Stmt : Stmts)
+    findGadgets(Stmt, Ctx, IgnoreHandler, false, FixableGadgets, WarningGadgets,
+                Tracker);
 
   std::set<const Expr *> Result;
   for (auto &G : WarningGadgets) {
@@ -4673,9 +4709,6 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
 #endif
 
   assert(D);
-
-  SmallVector<Stmt *> Stmts;
-
   if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
     // Consteval functions are free of UB by the spec, so we don't need to
     // visit them or produce diagnostics.
@@ -4697,27 +4730,21 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
         break;
       }
     }
+  }
 
-    Stmts.push_back(FD->getBody());
+  SmallVector<const Stmt *> Stmts;
 
-    if (const auto *ID = dyn_cast<CXXConstructorDecl>(D)) {
-      for (const CXXCtorInitializer *CI : ID->inits()) {
-        Stmts.push_back(CI->getInit());
-      }
-    }
-  } else if (isa<BlockDecl>(D) || isa<ObjCMethodDecl>(D)) {
-    Stmts.push_back(D->getBody());
-  }
+  populateStmtsForFindingGadgets(Stmts, D);
 
   assert(!Stmts.empty());
 
   FixableGadgetList FixableGadgets;
   WarningGadgetList WarningGadgets;
   DeclUseTracker Tracker;
-  for (Stmt *S : Stmts) {
+  for (const Stmt *S : Stmts) {
     findGadgets(S, D->getASTContext(), Handler, EmitSuggestions, FixableGadgets,
                 WarningGadgets, Tracker);
   }
   applyGadgets(D, std::move(FixableGadgets), std::move(WarningGadgets),
                std::move(Tracker), Handler, EmitSuggestions);
-}
+}
\ No newline at end of file
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
index c150b2918db59..c609168e4dc7d 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
@@ -33,6 +33,32 @@ static bool hasPointerType(const Expr *E) {
 constexpr inline auto buildEntityPointerLevel =
     UnsafeBufferUsageTUSummaryExtractor::buildEntityPointerLevel;
 
+static llvm::Error makeUnsupportedStmtKindError(const Stmt *Unsupported) {
+  return llvm::createStringError(
+      "unsupported expression kind for translation to "
+      "EntityPointerLevel: %s",
+      Unsupported->getStmtClassName());
+}
+
+static llvm::Error makeCreateEntityNameError(const NamedDecl *FailedDecl,
+                                             ASTContext &Ctx) {
+  std::string LocStr = FailedDecl->getSourceRange().getBegin().printToString(
+      Ctx.getSourceManager());
+  return llvm::createStringError(
+      "failed to create entity name for %s declared at %s",
+      FailedDecl->getNameAsString().c_str(), LocStr.c_str());
+}
+
+static llvm::Error makeAddEntitySummaryError(const NamedDecl *FailedContributor,
+                                             ASTContext &Ctx) {
+  std::string LocStr =
+      FailedContributor->getSourceRange().getBegin().printToString(
+          Ctx.getSourceManager());
+  return llvm::createStringError(
+      "failed to add entity summary for contributor %s declared at %s",
+      FailedContributor->getNameAsString().c_str(), LocStr.c_str());
+}
+
 // Translate a pointer 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
@@ -59,10 +85,7 @@ class EntityPointerLevelTranslator
 
   // Fallback method for all unsupported expression kind:
   llvm::Error fallback(const Stmt *E) {
-    return llvm::createStringError(
-        "unsupported expression kind for translation to "
-        "EntityPointerLevel: %s",
-        E->getStmtClassName());
+    return makeUnsupportedStmtKindError(E);
   }
 
   static EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E) {
@@ -92,10 +115,12 @@ class EntityPointerLevelTranslator
   }
 
   UnsafeBufferUsageTUSummaryExtractor &Extractor;
+  ASTContext &Ctx;
 
 public:
-  EntityPointerLevelTranslator(UnsafeBufferUsageTUSummaryExtractor &Extractor)
-      : Extractor(Extractor) {}
+  EntityPointerLevelTranslator(UnsafeBufferUsageTUSummaryExtractor &Extractor,
+                               ASTContext &Ctx)
+      : Extractor(Extractor), Ctx(Ctx) {}
 
   Expected<EntityPointerLevelSet> translate(const Expr *E) { return Visit(E); }
 
@@ -210,18 +235,14 @@ class EntityPointerLevelTranslator
   Expected<EntityPointerLevelSet> VisitDeclRefExpr(const DeclRefExpr *E) {
     if (auto EntityName = getEntityName(E->getDecl()))
       return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
-    return llvm::createStringError(
-        "failed to create entity name from the Decl of " +
-        E->getNameInfo().getAsString());
+    return makeCreateEntityNameError(E->getDecl(), Ctx);
   }
 
   // Translate({., ->}f) -> {(MemberDecl, 1)}
   Expected<EntityPointerLevelSet> VisitMemberExpr(const MemberExpr *E) {
     if (auto EntityName = getEntityName(E->getMemberDecl()))
       return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
-    return llvm::createStringError(
-        "failed to create entity name from the MemberDecl of " +
-        E->getMemberDecl()->getNameAsString());
+    return makeCreateEntityNameError(E->getMemberDecl(), Ctx);
   }
 
   Expected<EntityPointerLevelSet>
@@ -232,9 +253,10 @@ class EntityPointerLevelTranslator
 
 Expected<EntityPointerLevelSet>
 buildEntityPointerLevels(std::set<const Expr *> &&UnsafePointers,
-                         UnsafeBufferUsageTUSummaryExtractor &Extractor) {
+                         UnsafeBufferUsageTUSummaryExtractor &Extractor,
+                         ASTContext &Ctx) {
   EntityPointerLevelSet Result{};
-  EntityPointerLevelTranslator Translator{Extractor};
+  EntityPointerLevelTranslator Translator{Extractor, Ctx};
   llvm::Error AllErrors = llvm::ErrorSuccess();
 
   for (const Expr *Ptr : UnsafePointers) {
@@ -258,24 +280,90 @@ buildEntityPointerLevels(std::set<const Expr *> &&UnsafePointers,
 }
 } // namespace
 
+static std::set<const Expr *> findUnsafePointersInContributor(const Decl *D) {
+  if (isa<FunctionDecl>(D) || isa<VarDecl>(D))
+    return findUnsafePointers(D);
+  if (auto *RD = dyn_cast<RecordDecl>(D)) {
+    std::set<const Expr *> Result;
+
+    for (const FieldDecl *FD : RD->fields()) {
+      Result.merge(findUnsafePointers(FD));
+    }
+    return Result;
+  }
+  return {};
+}
+
 std::unique_ptr<UnsafeBufferUsageEntitySummary>
 UnsafeBufferUsageTUSummaryExtractor::extractEntitySummary(
     const Decl *Contributor, ASTContext &Ctx, llvm::Error &Error) {
-  // FIXME: findUnsafePointers should accept more kinds of `Decl`s than just
-  // `FunctionDecl`:
-  if (const auto *FD = dyn_cast<FunctionDecl>(Contributor)) {
-    Expected<EntityPointerLevelSet> EPLs =
-        buildEntityPointerLevels(findUnsafePointers(FD), *this);
-
-    if (EPLs)
-      return std::make_unique<UnsafeBufferUsageEntitySummary>(
-          UnsafeBufferUsageEntitySummary(std::move(*EPLs)));
-    Error = EPLs.takeError();
-  }
+  Expected<EntityPointerLevelSet> EPLs = buildEntityPointerLevels(
+      findUnsafePointersInContributor(Contributor), *this, Ctx);
+
+  if (EPLs)
+    return std::make_unique<UnsafeBufferUsageEntitySummary>(
+        UnsafeBufferUsageEntitySummary(std::move(*EPLs)));
+  Error = EPLs.takeError();
   return nullptr;
 }
 
 void UnsafeBufferUsageTUSummaryExtractor::HandleTranslationUnit(
     ASTContext &Ctx) {
-  // FIXME: impl me!
+
+  // FIXME: I suppose finding contributor Decls is commonly needed by all/many
+  // extractors
+  class ContributorFinder : public DynamicRecursiveASTVisitor {
+  public:
+    std::vector<const NamedDecl *> Contributors;
+
+    bool VisitFunctionDecl(FunctionDecl *D) override {
+      Contributors.push_back(D);
+      return true;
+    }
+
+    bool VisitRecordDecl(RecordDecl *D) override {
+      Contributors.push_back(D);
+      return true;
+    }
+
+    bool VisitVarDecl(VarDecl *D) override {
+      DeclContext *DC = D->getDeclContext();
+
+      if (DC->isFileContext() || DC->isNamespace())
+        Contributors.push_back(D);
+      return false;
+    }
+  } ContributorFinder;
+
+  ContributorFinder.VisitTranslationUnitDecl(Ctx.getTranslationUnitDecl());
+
+  llvm::Error Errors = llvm::ErrorSuccess();
+  auto addError = [&Errors](llvm::Error Err) {
+    Errors = llvm::joinErrors(std::move(Errors), std::move(Err));
+  };
+
+  for (auto *CD : ContributorFinder.Contributors) {
+    llvm::Error Error = llvm::ErrorSuccess();
+    auto EntitySummary = extractEntitySummary(CD, Ctx, Error);
+
+    if (Error)
+      addError(std::move(Error));
+    if (EntitySummary->empty())
+      continue;
+
+    auto ContributorName = getEntityName(CD);
+
+    if (!ContributorName) {
+      addError(makeCreateEntityNameError(CD, Ctx));
+      continue;
+    }
+
+    auto [EntitySummaryPtr, Success] = SummaryBuilder.addSummary(
+        addEntity(*ContributorName), std::move(EntitySummary));
+
+    if (!Success)
+      addError(makeAddEntitySummaryError(CD, Ctx));
+  }
+  // FIXME: handle errors!
+  llvm::consumeError(std::move(Errors));
 }
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index 81c742dc30811..67eec3714047b 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -35,7 +35,7 @@ const SomeDecl *findDeclByName(StringRef Name, ASTContext &Ctx) {
     NamedDeclFinder(StringRef SearchingName) : SearchingName(SearchingName) {}
 
     bool VisitDecl(Decl *D) override {
-      if (const auto *ND = dyn_cast<NamedDecl>(D)) {
+      if (const auto *ND = dyn_cast<SomeDecl>(D)) {
         if (ND->getNameAsString() == SearchingName) {
           FoundDecl = ND;
           return false;
@@ -69,16 +69,21 @@ class UnsafeBufferUsageTest : public testing::Test {
       : TUSum(BuildNamespace(BuildNamespaceKind::CompilationUnit, "Mock.cpp")),
         TUSummaryBuilder(TUSum), Extractor(TUSummaryBuilder) {}
 
+  template <typename ContributorDecl = NamedDecl>
   std::unique_ptr<UnsafeBufferUsageEntitySummary>
   setUpTest(StringRef Code, StringRef ContributorName) {
     AST = tooling::buildASTFromCodeWithArgs(
-        Code, {"-Wno-unused-value -Wno-int-to-pointer-cast"});
+        Code, {"-Wno-unused-value", "-Wno-int-to-pointer-cast"});
 
     const auto *ContributorDefn =
-        findDeclByName(ContributorName, AST->getASTContext());
+        findDeclByName<ContributorDecl>(ContributorName, AST->getASTContext());
+
+    if (!ContributorDefn)
+      return nullptr;
+
     std::optional<EntityName> EN = getEntityName(ContributorDefn);
 
-    if (!ContributorDefn || !EN)
+    if (!EN)
       return nullptr;
 
     llvm::Error Error = llvm::ErrorSuccess();
@@ -442,4 +447,53 @@ TEST_F(UnsafeBufferUsageTest, PointerToArrayOfPointers) {
   EXPECT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 3}}));
 }
+
+TEST_F(UnsafeBufferUsageTest, UnsafePointerInGlobalVariableInitializer) {
+  auto Sum = setUpTest(R"cpp(
+      int *gp;
+      int x = gp[5];
+    )cpp",
+                       {"x"});
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, UnsafePointerInFieldInitializer) {
+  auto Sum = setUpTest<>(R"cpp(
+      int *gp;
+      struct Foo {
+        int field = gp[5];
+      };
+    )cpp",
+                       {"Foo"});
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, UnsafePointerInCXXCtorInitializer) {
+  auto Sum = setUpTest<CXXConstructorDecl>(R"cpp(
+      struct Foo {
+        int member;
+        Foo(int *p) : member(p[5]) {}
+      };
+    )cpp",
+                                           {"Foo"});
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, UnsafePointerInDefaultArg) {
+  auto Sum = setUpTest(R"cpp(
+    int * gp;
+    void foo(int x = gp[5]);
+    )cpp",
+                       {"foo"});
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
+}
+
 } // namespace

>From 613955c1b475383116fc8182aa12a21d87d63ddd Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Thu, 5 Mar 2026 14:09:34 -0800
Subject: [PATCH 03/30] clean up

---
 clang/lib/Analysis/UnsafeBufferUsage.cpp                        | 2 +-
 .../Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp        | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index d6f554246a1b4..5a9241acbee36 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -4747,4 +4747,4 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
   }
   applyGadgets(D, std::move(FixableGadgets), std::move(WarningGadgets),
                std::move(Tracker), Handler, EmitSuggestions);
-}
\ No newline at end of file
+}
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index 67eec3714047b..db11717a86747 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -460,7 +460,7 @@ TEST_F(UnsafeBufferUsageTest, UnsafePointerInGlobalVariableInitializer) {
 }
 
 TEST_F(UnsafeBufferUsageTest, UnsafePointerInFieldInitializer) {
-  auto Sum = setUpTest<>(R"cpp(
+  auto Sum = setUpTest(R"cpp(
       int *gp;
       struct Foo {
         int field = gp[5];

>From deef1a0a65f6e03f19f6fa74ea232b6149b570be Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Mon, 16 Mar 2026 17:36:39 -0700
Subject: [PATCH 04/30] Address comments

---
 .../UnsafeBufferUsageTest.cpp                 | 80 +++++++++++++++++++
 1 file changed, 80 insertions(+)

diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index db11717a86747..8108ef8ad77b5 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -472,6 +472,35 @@ TEST_F(UnsafeBufferUsageTest, UnsafePointerInFieldInitializer) {
   EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
 }
 
+TEST_F(UnsafeBufferUsageTest, UnsafePointerInFieldInitializer2) {
+  auto Sum = setUpTest(R"cpp(
+      int *gp;
+      union Foo {
+        int field = gp[5];
+        int x;
+      };
+    )cpp",
+                       {"Foo"});
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, InitializerList) {
+  auto Sum = setUpTest(R"cpp(
+      int *gp;
+      struct Foo {
+        int field;
+        int x;
+      };
+      Foo FooObj{gp[5], 0};
+    )cpp",
+                       {"FooObj"});
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
+}
+
 TEST_F(UnsafeBufferUsageTest, UnsafePointerInCXXCtorInitializer) {
   auto Sum = setUpTest<CXXConstructorDecl>(R"cpp(
       struct Foo {
@@ -496,4 +525,55 @@ TEST_F(UnsafeBufferUsageTest, UnsafePointerInDefaultArg) {
   EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
 }
 
+TEST_F(UnsafeBufferUsageTest, NestedDefinitions) {
+  StringRef Code = R"cpp(
+    int * a = [](){
+      struct Foo {
+        void bar(int * ptr) { ptr[3] = 0; }
+      };
+      return nullptr;
+    }();
+    )cpp";
+  auto Sum = setUpTest(Code, {"bar"});
+
+  EXPECT_NE(Sum, nullptr);
+  // The closest contributor owns the fact:
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"ptr", 1U}}));
+
+  Sum = setUpTest(Code, {"Foo"});
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_TRUE(Sum->empty());
+
+  Sum = setUpTest(Code, {"a"});
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_TRUE(Sum->empty());
+}
+
+TEST_F(UnsafeBufferUsageTest, NestedDefinitions2) {
+  StringRef Code = R"cpp(
+    int main(void) {
+       struct Foo {
+          void bar(int * ptr) { ptr[3] = 0; }
+       };
+    }
+    )cpp";
+  auto Sum = setUpTest(Code, {"bar"});
+
+  EXPECT_NE(Sum, nullptr);
+  // The closest contributor owns the fact:
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"ptr", 1U}}));
+
+  Sum = setUpTest(Code, {"Foo"});
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_TRUE(Sum->empty());
+
+  Sum = setUpTest(Code, {"main"});
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_TRUE(Sum->empty());
+}
+
 } // namespace

>From 5ffde95abf78f9b14d9d3d64afaef3360559bee3 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Thu, 12 Mar 2026 14:10:19 -0700
Subject: [PATCH 05/30] Reapply "[clang][ssaf] Add UnsafeBufferUsage summary
 extractor for functions (#182941)"

This reverts commit 53739c75a8720aaef8032628267ed4fd050af038.

Reapply after module dependency issues are resolved.

(rdar://169191570)
---
 .../UnsafeBufferUsage/UnsafeBufferUsage.h     |  56 +--
 .../UnsafeBufferUsageExtractor.h              |  40 ++
 .../UnsafeBufferUsageBuilder.h                |  32 --
 .../Analyses/CMakeLists.txt                   |  15 +
 .../UnsafeBufferUsageExtractor.cpp            | 281 ++++++++++++
 .../CMakeLists.txt                            |   2 +
 .../UnsafeBufferUsageTest.cpp                 | 423 ++++++++++++++++--
 .../CMakeLists.txt                            |   1 +
 8 files changed, 743 insertions(+), 107 deletions(-)
 rename clang/include/clang/ScalableStaticAnalysisFramework/{Core => }/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h (62%)
 create mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h
 delete mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsageBuilder.h
 create mode 100644 clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
 create mode 100644 clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp

diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
similarity index 62%
rename from clang/include/clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
rename to clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
index 5b58ed0684333..5f418000723b4 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
@@ -6,13 +6,12 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGE_H
-#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGE_H
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGE_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGE_H
 
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/SummaryName.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
-#include "llvm/ADT/iterator_range.h"
 #include <set>
 
 namespace clang::ssaf {
@@ -26,23 +25,19 @@ namespace clang::ssaf {
 /// *' 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');
-///   - 'P.EntityId' identifies an lvalue object and 'P.PointerLevel == 0'.
-/// The latter case represents address-of expressions.
+/// 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, 1)' is associated with 'int *[10]' of 'p';
-/// '(p, 2)' is associated with 'int *' of 'p';
-/// '(p, 0)' represents '&p'.
+/// - '(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 UnsafeBufferUsageTUSummaryBuilder;
-  friend class UnsafeBufferUsageEntitySummary;
+  friend class UnsafeBufferUsageTUSummaryExtractor;
 
   EntityPointerLevel(EntityId Entity, unsigned PointerLevel)
       : Entity(Entity), PointerLevel(PointerLevel) {}
@@ -52,7 +47,8 @@ class EntityPointerLevel {
   unsigned getPointerLevel() const { return PointerLevel; }
 
   bool operator==(const EntityPointerLevel &Other) const {
-    return Entity == Other.Entity && PointerLevel == Other.PointerLevel;
+    return std::tie(Entity, PointerLevel) ==
+           std::tie(Other.Entity, Other.PointerLevel);
   }
 
   bool operator!=(const EntityPointerLevel &Other) const {
@@ -64,7 +60,8 @@ class EntityPointerLevel {
            std::tie(Other.Entity, Other.PointerLevel);
   }
 
-  // Comparator supporting partial comparison against EntityId:
+  /// Compares `EntityPointerLevel`s; additionally, partially compares
+  /// `EntityPointerLevel` with `EntityId`.
   struct Comparator {
     using is_transparent = void;
     bool operator()(const EntityPointerLevel &L,
@@ -88,33 +85,20 @@ using EntityPointerLevelSet =
 class UnsafeBufferUsageEntitySummary final : public EntitySummary {
   const EntityPointerLevelSet UnsafeBuffers;
 
-  friend class UnsafeBufferUsageTUSummaryBuilder;
+  friend class UnsafeBufferUsageTUSummaryExtractor;
 
-  UnsafeBufferUsageEntitySummary(EntityPointerLevelSet &&UnsafeBuffers)
+  UnsafeBufferUsageEntitySummary(EntityPointerLevelSet UnsafeBuffers)
       : EntitySummary(), UnsafeBuffers(std::move(UnsafeBuffers)) {}
 
 public:
-  using const_iterator = EntityPointerLevelSet::const_iterator;
-
-  const_iterator begin() const { return UnsafeBuffers.begin(); }
-  const_iterator end() const { return UnsafeBuffers.end(); }
-
-  const_iterator find(const EntityPointerLevel &V) const {
-    return UnsafeBuffers.find(V);
-  }
-
-  llvm::iterator_range<const_iterator> getSubsetOf(EntityId Entity) const {
-    return llvm::make_range(UnsafeBuffers.equal_range(Entity));
-  }
-
-  /// \return the size of the set of EntityLevelPointers, which represents the
-  /// set of unsafe buffers
-  size_t getNumUnsafeBuffers() { return UnsafeBuffers.size(); }
-
   SummaryName getSummaryName() const override {
     return SummaryName{"UnsafeBufferUsage"};
   };
+
+  bool operator==(const EntityPointerLevelSet &Other) const {
+    return UnsafeBuffers == Other;
+  }
 };
 } // namespace clang::ssaf
 
-#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGE_H
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGE_H
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h
new file mode 100644
index 0000000000000..765b2c37562ce
--- /dev/null
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h
@@ -0,0 +1,40 @@
+//===- UnsafeBufferUsageExtractor.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_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEBUILDER_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEBUILDER_H
+
+#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.h"
+#include "llvm/Support/Error.h"
+#include <memory>
+
+namespace clang::ssaf {
+class UnsafeBufferUsageTUSummaryExtractor : public TUSummaryExtractor {
+public:
+  UnsafeBufferUsageTUSummaryExtractor(TUSummaryBuilder &Builder)
+      : TUSummaryExtractor(Builder) {}
+
+  static EntityPointerLevel buildEntityPointerLevel(EntityId Entity,
+                                                    unsigned PointerLevel) {
+    return {Entity, PointerLevel};
+  }
+
+  EntityId addEntity(EntityName EN) { return SummaryBuilder.addEntity(EN); }
+
+  std::unique_ptr<UnsafeBufferUsageEntitySummary>
+  extractEntitySummary(const Decl *Contributor, ASTContext &Ctx,
+                       llvm::Error &Error);
+
+  void HandleTranslationUnit(ASTContext &Ctx) override;
+};
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEBUILDER_H
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsageBuilder.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsageBuilder.h
deleted file mode 100644
index db6c097510a57..0000000000000
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsageBuilder.h
+++ /dev/null
@@ -1,32 +0,0 @@
-//===- UnsafeBufferUsageBuilder.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_CORE_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEBUILDER_H
-#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEBUILDER_H
-
-#include "clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
-#include <memory>
-
-namespace clang::ssaf {
-class UnsafeBufferUsageTUSummaryBuilder : public TUSummaryBuilder {
-public:
-  static EntityPointerLevel buildEntityPointerLevel(EntityId Entity,
-                                                    unsigned PointerLevel) {
-    return {Entity, PointerLevel};
-  }
-
-  static std::unique_ptr<UnsafeBufferUsageEntitySummary>
-  buildUnsafeBufferUsageEntitySummary(EntityPointerLevelSet &&UnsafeBuffers) {
-    return std::make_unique<UnsafeBufferUsageEntitySummary>(
-        UnsafeBufferUsageEntitySummary(std::move(UnsafeBuffers)));
-  }
-};
-} // namespace clang::ssaf
-
-#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEBUILDER_H
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
new file mode 100644
index 0000000000000..34c6dd9b61203
--- /dev/null
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
@@ -0,0 +1,15 @@
+set(LLVM_LINK_COMPONENTS
+  Support
+  )
+
+add_clang_library(clangScalableStaticAnalysisFrameworkAnalyses
+  UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
+
+  LINK_LIBS
+  clangAST
+  clangAnalysis
+  clangBasic
+  clangScalableStaticAnalysisFrameworkCore
+
+  DEPENDS
+  )
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
new file mode 100644
index 0000000000000..c150b2918db59
--- /dev/null
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
@@ -0,0 +1,281 @@
+//===- UnsafeBufferUsageExtractor.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/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DynamicRecursiveASTVisitor.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/iterator_range.h"
+#include "llvm/Support/Error.h"
+#include <memory>
+
+namespace {
+using namespace clang;
+using namespace ssaf;
+
+static bool hasPointerType(const Expr *E) {
+  auto Ty = E->getType();
+  return !Ty.isNull() && !Ty->isFunctionPointerType() &&
+         (Ty->isPointerType() || Ty->isArrayType());
+}
+
+constexpr inline auto buildEntityPointerLevel =
+    UnsafeBufferUsageTUSummaryExtractor::buildEntityPointerLevel;
+
+// Translate a pointer 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.
+//
+// The translation is a process of traversing into the pointer 'E' until its
+// base address can be represented by an entity, with the number of dereferences
+// tracked by incrementing the pointer level.  Naturally, taking address of, as
+// the inverse operation of dereference, is tracked by decrementing the pointer
+// level.
+//
+// For example, suppose there are pointers and arrays declared as
+//   int *ptr, **p1, **p2;
+//   int arr[10][10];
+// , the translation of expressions involving these base addresses will be:
+//   Translate(ptr + 5)            -> {(ptr, 1)}
+//   Translate(arr[5])             -> {(arr, 2)}
+//   Translate(cond ? p1[5] : p2)  -> {(p1, 2), (p2, 1)}
+//   Translate(&arr[5])            -> {(arr, 1)}
+class EntityPointerLevelTranslator
+    : ConstStmtVisitor<EntityPointerLevelTranslator,
+                       Expected<EntityPointerLevelSet>> {
+  friend class StmtVisitorBase;
+
+  // Fallback method for all unsupported expression kind:
+  llvm::Error fallback(const Stmt *E) {
+    return llvm::createStringError(
+        "unsupported expression kind for translation to "
+        "EntityPointerLevel: %s",
+        E->getStmtClassName());
+  }
+
+  static EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E) {
+    return buildEntityPointerLevel(E.getEntity(), E.getPointerLevel() + 1);
+  }
+
+  static EntityPointerLevel decrementPointerLevel(const EntityPointerLevel &E) {
+    assert(E.getPointerLevel() > 0);
+    return buildEntityPointerLevel(E.getEntity(), E.getPointerLevel() - 1);
+  }
+
+  EntityPointerLevel createEntityPointerLevelFor(const EntityName &Name) {
+    return buildEntityPointerLevel(Extractor.addEntity(Name), 1);
+  }
+
+  // The common helper function for Translate(*base):
+  // Translate(*base) -> Translate(base) with .pointerLevel + 1
+  Expected<EntityPointerLevelSet> translateDereferencePointer(const Expr *Ptr) {
+    assert(hasPointerType(Ptr));
+
+    Expected<EntityPointerLevelSet> SubResult = Visit(Ptr);
+    if (!SubResult)
+      return SubResult.takeError();
+
+    auto Incremented = llvm::map_range(*SubResult, incrementPointerLevel);
+    return EntityPointerLevelSet{Incremented.begin(), Incremented.end()};
+  }
+
+  UnsafeBufferUsageTUSummaryExtractor &Extractor;
+
+public:
+  EntityPointerLevelTranslator(UnsafeBufferUsageTUSummaryExtractor &Extractor)
+      : Extractor(Extractor) {}
+
+  Expected<EntityPointerLevelSet> translate(const Expr *E) { return Visit(E); }
+
+private:
+  Expected<EntityPointerLevelSet> VisitStmt(const Stmt *E) {
+    return fallback(E);
+  }
+
+  // Translate(base + x)           -> Translate(base)
+  // Translate(x + base)           -> Translate(base)
+  // Translate(base - x)           -> Translate(base)
+  // Translate(base {+=, -=, =} x) -> Translate(base)
+  // Translate(x, base)            -> Translate(base)
+  Expected<EntityPointerLevelSet> VisitBinaryOperator(const BinaryOperator *E) {
+    switch (E->getOpcode()) {
+    case clang::BO_Add:
+      if (hasPointerType(E->getLHS()))
+        return Visit(E->getLHS());
+      return Visit(E->getRHS());
+    case clang::BO_Sub:
+    case clang::BO_AddAssign:
+    case clang::BO_SubAssign:
+    case clang::BO_Assign:
+      return Visit(E->getLHS());
+    case clang::BO_Comma:
+      return Visit(E->getRHS());
+    default:
+      return fallback(E);
+    }
+  }
+
+  // Translate({++, --}base)   -> Translate(base)
+  // Translate(base{++, --})   -> Translate(base)
+  // Translate(*base)          -> Translate(base) with .pointerLevel += 1
+  // Translate(&base)          -> {}, if Translate(base) is {}
+  //                           -> Translate(base) with .pointerLevel -= 1
+  Expected<EntityPointerLevelSet> VisitUnaryOperator(const UnaryOperator *E) {
+    switch (E->getOpcode()) {
+    case clang::UO_PostInc:
+    case clang::UO_PostDec:
+    case clang::UO_PreInc:
+    case clang::UO_PreDec:
+      return Visit(E->getSubExpr());
+    case clang::UO_AddrOf: {
+      Expected<EntityPointerLevelSet> SubResult = Visit(E->getSubExpr());
+      if (!SubResult)
+        return SubResult.takeError();
+
+      auto Decremented = llvm::map_range(*SubResult, decrementPointerLevel);
+      return EntityPointerLevelSet{Decremented.begin(), Decremented.end()};
+    }
+    case clang::UO_Deref:
+      return translateDereferencePointer(E->getSubExpr());
+    default:
+      return fallback(E);
+    }
+  }
+
+  // Translate((T*)base) -> Translate(p) if p has pointer type
+  //                     -> {} otherwise
+  Expected<EntityPointerLevelSet> VisitCastExpr(const CastExpr *E) {
+    if (hasPointerType(E->getSubExpr()))
+      return Visit(E->getSubExpr());
+    return EntityPointerLevelSet{};
+  }
+
+  // Translate(f(...)) -> {} if it is an indirect call
+  //                   -> {(f_return, 1)}, otherwise
+  Expected<EntityPointerLevelSet> VisitCallExpr(const CallExpr *E) {
+    if (auto *FD = E->getDirectCallee())
+      if (auto FDEntityName = getEntityNameForReturn(FD))
+        return EntityPointerLevelSet{
+            createEntityPointerLevelFor(*FDEntityName)};
+    return EntityPointerLevelSet{};
+  }
+
+  // Translate(base[x]) -> Translate(*base)
+  Expected<EntityPointerLevelSet>
+  VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+    return translateDereferencePointer(E->getBase());
+  }
+
+  // Translate(cond ? base1 : base2) := Translate(base1) U Translate(base2)
+  Expected<EntityPointerLevelSet>
+  VisitAbstractConditionalOperator(const AbstractConditionalOperator *E) {
+    Expected<EntityPointerLevelSet> ReT = Visit(E->getTrueExpr());
+    Expected<EntityPointerLevelSet> ReF = Visit(E->getFalseExpr());
+
+    if (ReT && ReF) {
+      ReT->insert(ReF->begin(), ReF->end());
+      return ReT;
+    }
+    if (!ReF && !ReT)
+      return llvm::joinErrors(ReT.takeError(), ReF.takeError());
+    if (!ReF)
+      return ReF.takeError();
+    return ReT.takeError();
+  }
+
+  Expected<EntityPointerLevelSet> VisitParenExpr(const ParenExpr *E) {
+    return Visit(E->getSubExpr());
+  }
+
+  // Translate("string-literal") -> {}
+  // Buffer accesses on string literals are unsafe, but string literals are not
+  // entities so there is no EntityPointerLevel associated with it.
+  Expected<EntityPointerLevelSet> VisitStringLiteral(const StringLiteral *E) {
+    return EntityPointerLevelSet{};
+  }
+
+  // Translate(DRE) -> {(Decl, 1)}
+  Expected<EntityPointerLevelSet> VisitDeclRefExpr(const DeclRefExpr *E) {
+    if (auto EntityName = getEntityName(E->getDecl()))
+      return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
+    return llvm::createStringError(
+        "failed to create entity name from the Decl of " +
+        E->getNameInfo().getAsString());
+  }
+
+  // Translate({., ->}f) -> {(MemberDecl, 1)}
+  Expected<EntityPointerLevelSet> VisitMemberExpr(const MemberExpr *E) {
+    if (auto EntityName = getEntityName(E->getMemberDecl()))
+      return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
+    return llvm::createStringError(
+        "failed to create entity name from the MemberDecl of " +
+        E->getMemberDecl()->getNameAsString());
+  }
+
+  Expected<EntityPointerLevelSet>
+  VisitOpaqueValueExpr(const OpaqueValueExpr *S) {
+    return Visit(S->getSourceExpr());
+  }
+};
+
+Expected<EntityPointerLevelSet>
+buildEntityPointerLevels(std::set<const Expr *> &&UnsafePointers,
+                         UnsafeBufferUsageTUSummaryExtractor &Extractor) {
+  EntityPointerLevelSet Result{};
+  EntityPointerLevelTranslator Translator{Extractor};
+  llvm::Error AllErrors = llvm::ErrorSuccess();
+
+  for (const Expr *Ptr : UnsafePointers) {
+    Expected<EntityPointerLevelSet> Translation = Translator.translate(Ptr);
+
+    if (Translation) {
+      // Filter out those temporary invalid EntityPointerLevels associated with
+      // `&E` pointers:
+      auto FilteredTranslation = llvm::make_filter_range(
+          *Translation, [](const EntityPointerLevel &E) -> bool {
+            return E.getPointerLevel() > 0;
+          });
+      Result.insert(FilteredTranslation.begin(), FilteredTranslation.end());
+      continue;
+    }
+    AllErrors = llvm::joinErrors(std::move(AllErrors), Translation.takeError());
+  }
+  if (AllErrors)
+    return AllErrors;
+  return Result;
+}
+} // namespace
+
+std::unique_ptr<UnsafeBufferUsageEntitySummary>
+UnsafeBufferUsageTUSummaryExtractor::extractEntitySummary(
+    const Decl *Contributor, ASTContext &Ctx, llvm::Error &Error) {
+  // FIXME: findUnsafePointers should accept more kinds of `Decl`s than just
+  // `FunctionDecl`:
+  if (const auto *FD = dyn_cast<FunctionDecl>(Contributor)) {
+    Expected<EntityPointerLevelSet> EPLs =
+        buildEntityPointerLevels(findUnsafePointers(FD), *this);
+
+    if (EPLs)
+      return std::make_unique<UnsafeBufferUsageEntitySummary>(
+          UnsafeBufferUsageEntitySummary(std::move(*EPLs)));
+    Error = EPLs.takeError();
+  }
+  return nullptr;
+}
+
+void UnsafeBufferUsageTUSummaryExtractor::HandleTranslationUnit(
+    ASTContext &Ctx) {
+  // FIXME: impl me!
+}
diff --git a/clang/lib/ScalableStaticAnalysisFramework/CMakeLists.txt b/clang/lib/ScalableStaticAnalysisFramework/CMakeLists.txt
index d3d75430233fe..6bcb1a0038127 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/CMakeLists.txt
+++ b/clang/lib/ScalableStaticAnalysisFramework/CMakeLists.txt
@@ -1,2 +1,4 @@
+add_subdirectory(Analyses)
 add_subdirectory(Core)
 add_subdirectory(Frontend)
+
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index 082ff7f8ac175..81c742dc30811 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -6,37 +6,144 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsageBuilder.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "clang/AST/DynamicRecursiveASTVisitor.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityIdTable.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummary.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
+#include "clang/Tooling/Tooling.h"
+#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
 using namespace clang;
 using namespace ssaf;
+using testing::UnorderedElementsAre;
 
 namespace {
 
+template <typename SomeDecl = NamedDecl>
+const SomeDecl *findDeclByName(StringRef Name, ASTContext &Ctx) {
+  class NamedDeclFinder : public DynamicRecursiveASTVisitor {
+  public:
+    StringRef SearchingName;
+    const NamedDecl *FoundDecl = nullptr;
+
+    NamedDeclFinder(StringRef SearchingName) : SearchingName(SearchingName) {}
+
+    bool VisitDecl(Decl *D) override {
+      if (const auto *ND = dyn_cast<NamedDecl>(D)) {
+        if (ND->getNameAsString() == SearchingName) {
+          FoundDecl = ND;
+          return false;
+        }
+      }
+      return true;
+    }
+  };
+
+  NamedDeclFinder Finder(Name);
+
+  Finder.TraverseDecl(Ctx.getTranslationUnitDecl());
+  return dyn_cast_or_null<SomeDecl>(Finder.FoundDecl);
+}
+
+const FunctionDecl *findFnByName(StringRef Name, ASTContext &Ctx) {
+  return findDeclByName<FunctionDecl>(Name, Ctx);
+}
+
 constexpr inline auto buildEntityPointerLevel =
-    UnsafeBufferUsageTUSummaryBuilder::buildEntityPointerLevel;
-constexpr inline auto buildUnsafeBufferUsageEntitySummary =
-    UnsafeBufferUsageTUSummaryBuilder::buildUnsafeBufferUsageEntitySummary;
+    UnsafeBufferUsageTUSummaryExtractor::buildEntityPointerLevel;
 
 class UnsafeBufferUsageTest : public testing::Test {
 protected:
-  EntityIdTable Table;
+  TUSummary TUSum;
+  TUSummaryBuilder TUSummaryBuilder;
+  UnsafeBufferUsageTUSummaryExtractor Extractor;
+  std::unique_ptr<ASTUnit> AST;
+
+  UnsafeBufferUsageTest()
+      : TUSum(BuildNamespace(BuildNamespaceKind::CompilationUnit, "Mock.cpp")),
+        TUSummaryBuilder(TUSum), Extractor(TUSummaryBuilder) {}
+
+  std::unique_ptr<UnsafeBufferUsageEntitySummary>
+  setUpTest(StringRef Code, StringRef ContributorName) {
+    AST = tooling::buildASTFromCodeWithArgs(
+        Code, {"-Wno-unused-value -Wno-int-to-pointer-cast"});
+
+    const auto *ContributorDefn =
+        findDeclByName(ContributorName, AST->getASTContext());
+    std::optional<EntityName> EN = getEntityName(ContributorDefn);
+
+    if (!ContributorDefn || !EN)
+      return nullptr;
+
+    llvm::Error Error = llvm::ErrorSuccess();
+    auto Sum = Extractor.extractEntitySummary(ContributorDefn,
+                                              AST->getASTContext(), Error);
+
+    if (Error) {
+      llvm::consumeError(std::move(Error));
+      return nullptr;
+    }
+    return Sum;
+  }
+
+  std::optional<EntityId> getEntityId(StringRef Name) {
+    if (const auto *D = findDeclByName(Name, AST->getASTContext()))
+      if (auto EntityName = getEntityName(D))
+        return Extractor.addEntity(*EntityName);
+    return std::nullopt;
+  }
+
+  std::optional<EntityId> getEntityIdForReturn(StringRef FunName) {
+    if (const auto *D = findFnByName(FunName, AST->getASTContext()))
+      if (auto EntityName = getEntityNameForReturn(D))
+        return Extractor.addEntity(*EntityName);
+    return std::nullopt;
+  }
+
+  // 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:
+  struct EPLPair {
+    EPLPair(StringRef Name, unsigned Lv, bool isFunRet = false)
+        : Name(Name), Lv(Lv), isFunRet(isFunRet) {}
+
+    StringRef Name;
+    unsigned Lv;
+    bool isFunRet;
+  };
+
+  EntityPointerLevelSet makeSet(unsigned Line, ArrayRef<EPLPair> Pairs) {
+    auto EPLs = llvm::map_range(
+        Pairs, [this, Line](const EPLPair &Pair) -> EntityPointerLevel {
+          std::optional<EntityId> Entity = Pair.isFunRet
+                                               ? getEntityIdForReturn(Pair.Name)
+                                               : getEntityId(Pair.Name);
+          if (!Entity)
+            ADD_FAILURE_AT(__FILE__, Line) << "Entity not found: " << Pair.Name;
+          return buildEntityPointerLevel(*Entity, Pair.Lv);
+        });
+    return EntityPointerLevelSet{EPLs.begin(), EPLs.end()};
+  }
 };
 
 //////////////////////////////////////////////////////////////
 //                   Data Structure Tests                   //
 //////////////////////////////////////////////////////////////
 
-#define EXPECT_CONTAINS(Set, Elt) EXPECT_NE((Set).find(Elt), (Set).end())
-#define EXPECT_EXCLUDES(Set, Elt) EXPECT_EQ((Set).find(Elt), (Set).end())
+static llvm::iterator_range<EntityPointerLevelSet::iterator>
+getSubsetOf(const EntityPointerLevelSet &Set, EntityId Entity) {
+  return llvm::make_range(Set.equal_range(Entity));
+}
 
 TEST_F(UnsafeBufferUsageTest, EntityPointerLevelComparison) {
-  EntityId E1 = Table.getId({"c:@F at foo", "", {}});
-  EntityId E2 = Table.getId({"c:@F at bar", "", {}});
+  EntityId E1 = Extractor.addEntity({"c:@F at foo", "", {}});
+  EntityId E2 = Extractor.addEntity({"c:@F at bar", "", {}});
 
   auto P1 = buildEntityPointerLevel(E1, 2);
   auto P2 = buildEntityPointerLevel(E1, 2);
@@ -53,48 +160,286 @@ TEST_F(UnsafeBufferUsageTest, EntityPointerLevelComparison) {
   EXPECT_FALSE(P2 < P1);
 }
 
-TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageEntitySummaryTest) {
-  EntityId E1 = Table.getId({"c:@F at foo", "", {}});
-  EntityId E2 = Table.getId({"c:@F at bar", "", {}});
-  EntityId E3 = Table.getId({"c:@F at baz", "", {}});
+TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageEntityPointerLevelSetTest) {
+  EntityId E1 = Extractor.addEntity({"c:@F at foo", "", {}});
+  EntityId E2 = Extractor.addEntity({"c:@F at bar", "", {}});
+  EntityId E3 = Extractor.addEntity({"c:@F at baz", "", {}});
 
   auto P1 = buildEntityPointerLevel(E1, 1);
   auto P2 = buildEntityPointerLevel(E1, 2);
   auto P3 = buildEntityPointerLevel(E2, 1);
   auto P4 = buildEntityPointerLevel(E2, 2);
   auto P5 = buildEntityPointerLevel(E3, 1);
-  auto P6 = buildEntityPointerLevel(E3, 2);
 
   EntityPointerLevelSet Set{P1, P2, P3, P4, P5};
-  auto ES = buildUnsafeBufferUsageEntitySummary(std::move(Set));
-  ASSERT_TRUE(ES);
 
-  EXPECT_CONTAINS(*ES, P1);
-  EXPECT_CONTAINS(*ES, P2);
-  EXPECT_CONTAINS(*ES, P3);
-  EXPECT_CONTAINS(*ES, P4);
-  EXPECT_CONTAINS(*ES, P5);
-  EXPECT_EXCLUDES(*ES, P6);
+  EXPECT_THAT(Set, UnorderedElementsAre(P1, P2, P3, P4, P5));
+  EXPECT_THAT(getSubsetOf(Set, E1), UnorderedElementsAre(P1, P2));
+  EXPECT_THAT(getSubsetOf(Set, E2), UnorderedElementsAre(P3, P4));
+  EXPECT_THAT(getSubsetOf(Set, E3), UnorderedElementsAre(P5));
+}
+
+//////////////////////////////////////////////////////////////
+//                   Extractor Tests                        //
+//////////////////////////////////////////////////////////////
+
+TEST_F(UnsafeBufferUsageTest, SimpleFunctionWithUnsafePointer) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int *p) {
+      p[5];
+    }
+  )cpp",
+                       "foo");
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}}));
+}
 
-  EntityPointerLevelSet Subset1{ES->getSubsetOf(E1).begin(),
-                                ES->getSubsetOf(E1).end()};
+TEST_F(UnsafeBufferUsageTest, PointerArithmetic) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int *p, int *q) {
+      *(p + 5);
+      *(q - 3);
+    }
+  )cpp",
+                       "foo");
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, PointerIncrementDecrement) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int *p, int *q, int *r, int *s) {
+      (++p)[5];
+      (q++)[5];
+      (--r)[5];
+      (s--)[5];
+    }
+  )cpp",
+                       "foo");
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum,
+            makeSet(__LINE__, {{"p", 1U}, {"q", 1U}, {"r", 1U}, {"s", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, PointerAssignment) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int *p, int *q) {
+      (p = q + 5)[5];
+    }
+  )cpp",
+                       "foo");
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, CompoundAssignment) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int *p, int *q) {
+      (p += 5)[5];
+      (q -= 3)[5];
+    }
+  )cpp",
+                       "foo");
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}}));
+}
 
-  EXPECT_CONTAINS(Subset1, P1);
-  EXPECT_CONTAINS(Subset1, P2);
-  EXPECT_EQ(Subset1.size(), 2U);
+TEST_F(UnsafeBufferUsageTest, MultiLevelPointer) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int **p, int **q, int **r) {
+      (*p)[5];
+      *(*q);
+      *(q[5]);
+      r[5][5];
+    }
+  )cpp",
+                       "foo");
 
-  EntityPointerLevelSet Subset2{ES->getSubsetOf(E2).begin(),
-                                ES->getSubsetOf(E2).end()};
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum,
+            makeSet(__LINE__, {{"p", 2U}, {"q", 1U}, {"r", 1U}, {"r", 2U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, ConditionalOperator) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int **p, int **q, int cond) {
+      (cond ? *p : *q)[5];
+      cond ? p[5] : q[5];
+    }
+  )cpp",
+                       "foo");
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum,
+            makeSet(__LINE__, {{"p", 1U}, {"q", 1U}, {"p", 2U}, {"q", 2U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, CastExpression) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(void *p, int q) {
+      ((int*)p)[5];
+      ((int*)q)[5];
+    }
+  )cpp",
+                       "foo");
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, CommaOperator) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int *p, int x) {
+      (x++, p)[5];
+    }
+  )cpp",
+                       "foo");
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, CommaOperator2) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int **p, int **q, int x) {
+      (p[x] = 0, q[x] = 0)[5];
+    }
+  )cpp",
+                       "foo");
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}, {"q", 2U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, ParenthesizedExpression) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int *p) {
+      (((p)))[5];
+    }
+  )cpp",
+                       "foo");
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, ArrayParameter) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int arr[], int arr2[][10]) {
+      int n = 5;
+      arr[100];
+      arr2[5][n];
+    }
+  )cpp",
+                       "foo");
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"arr", 1U}, {"arr2", 1U}, {"arr2", 2U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, FunctionCall) {
+  auto Sum = setUpTest(R"cpp(
+    int ** (*fp)();
+    int ** foo() {
+      fp = &foo;
+      foo()[5];
+      (*fp())[5];
+    }
+  )cpp",
+                       "foo");
+
+  EXPECT_NE(Sum, nullptr);
+  // No (foo, 2) becasue indirect calls are ignored.
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"foo", 1U, true}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, StructMemberAccess) {
+  auto Sum = setUpTest(R"cpp(
+    struct S {
+      int *ptr;
+      int (*ptr_to_arr)[10];
+    };
+    void foo(struct S obj) {
+      int n = 5;
+      obj.ptr[5];
+      (*obj.ptr_to_arr)[n];
+    }
+  )cpp",
+                       "foo");
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"ptr", 1U}, {"ptr_to_arr", 2U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, StringLiteralSubscript) {
+  auto Sum = setUpTest(R"cpp(
+    void foo() {
+      "hello"[5];
+    }
+  )cpp",
+                       "foo");
+
+  EXPECT_NE(Sum, nullptr);
+  // String literals should not generate pointer kind variables
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {}));
+}
+
+TEST_F(UnsafeBufferUsageTest, OpaqueValueExpr) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int *p, int *q) {
+       (p ?: q)[5];
+    }
+  )cpp",
+                       "foo");
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, AddressOfOperator) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int x) {
+      (&x)[5];
+    }
+  )cpp",
+                       "foo");
+
+  EXPECT_NE(Sum, nullptr);
+  // Address-of should not generate pointer kind variables for 'x'
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {}));
+}
+
+TEST_F(UnsafeBufferUsageTest, AddressOfThenDereference) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int *p, int *q) {
+      (*(&p))[5];
+      (&(*q))[5];
+    }
+  )cpp",
+                       "foo");
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1}, {"q", 1}}));
+}
 
-  EXPECT_CONTAINS(Subset2, P3);
-  EXPECT_CONTAINS(Subset2, P4);
-  EXPECT_EQ(Subset2.size(), 2U);
+TEST_F(UnsafeBufferUsageTest, PointerToArrayOfPointers) {
+  auto Sum = setUpTest(R"cpp(
+    void foo() {
+      int * arr[10];
+      int * (*p)[10] = arr;
 
-  EntityPointerLevelSet Subset3{ES->getSubsetOf(E3).begin(),
-                                ES->getSubsetOf(E3).end()};
+      (*p)[5][5]; // '(*p)[5]' is unsafe 
+                  // '(*p)' is fine because 5 < 10
+    }
+  )cpp",
+                       "foo");
 
-  EXPECT_CONTAINS(Subset3, P5);
-  EXPECT_EXCLUDES(Subset3, P6);
-  EXPECT_EQ(Subset3.size(), 1U);
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 3}}));
 }
 } // namespace
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt b/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
index 871d9e6b0c02c..f541e81d42789 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
+++ b/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
@@ -29,6 +29,7 @@ add_distinct_clang_unittest(ClangScalableAnalysisTests
   clangASTMatchers
   clangBasic
   clangFrontend
+  clangScalableStaticAnalysisFrameworkAnalyses
   clangScalableStaticAnalysisFrameworkCore
   clangScalableStaticAnalysisFrameworkFrontend
   clangSerialization

>From fe7dd289875091a8f41966d9500b5c1d0e9f93a9 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Thu, 5 Mar 2026 13:00:44 -0800
Subject: [PATCH 06/30] [ssaf][UnsafeBufferUsage] Add support for extracting
 unsafe pointers from all kinds of contributors

- Generalize the -Wunsafe-buffer-usage API for finding unsafe pointers in all kinds of Decls
- Add support in SSAF-based UnsafeBufferUsage analysis for extracting from various contributors
- Mock implementation of HandleTranslationUnit

rdar://171735836
---
 .../Analysis/Analyses/UnsafeBufferUsage.h     |   9 +-
 .../UnsafeBufferUsage/UnsafeBufferUsage.h     |   2 +
 clang/lib/Analysis/UnsafeBufferUsage.cpp      |  61 +++++---
 .../UnsafeBufferUsageExtractor.cpp            | 140 ++++++++++++++----
 .../UnsafeBufferUsageTest.cpp                 |  62 +++++++-
 5 files changed, 226 insertions(+), 48 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
index 876682ad779d4..e0d583c735e61 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
@@ -201,7 +201,14 @@ bool anyConflict(const llvm::SmallVectorImpl<FixItHint> &FixIts,
                  const SourceManager &SM);
 } // namespace internal
 
-std::set<const Expr *> findUnsafePointers(const FunctionDecl *FD);
+/// Find unsafe pointers in body/initializer of `D`, if `D` is one of the
+/// followings:
+///   VarDecl
+///   FieldDecl
+///   FunctionDecl
+///   BlockDecl
+///   ObjCMethodDecl
+std::set<const Expr *> findUnsafePointers(const Decl *D);
 } // end namespace clang
 
 #endif /* LLVM_CLANG_ANALYSIS_ANALYSES_UNSAFEBUFFERUSAGE_H */
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
index 5f418000723b4..c0a4c2f76ab48 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
@@ -98,6 +98,8 @@ class UnsafeBufferUsageEntitySummary final : public EntitySummary {
   bool operator==(const EntityPointerLevelSet &Other) const {
     return UnsafeBuffers == Other;
   }
+
+  bool empty() const { return UnsafeBuffers.empty(); }
 };
 } // namespace clang::ssaf
 
diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 133e39b8fac2b..d6f554246a1b4 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -28,6 +28,7 @@
 #include "clang/Lex/Preprocessor.h"
 #include "llvm/ADT/APInt.h"
 #include "llvm/ADT/APSInt.h"
+#include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/STLFunctionalExtras.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
@@ -36,6 +37,7 @@
 #include <queue>
 #include <set>
 #include <sstream>
+#include <vector>
 
 using namespace clang;
 
@@ -2942,7 +2944,37 @@ template <typename NodeTy> struct CompareNode {
   }
 };
 
-std::set<const Expr *> clang::findUnsafePointers(const FunctionDecl *FD) {
+// Populate `Stmts` with the body/initializer Stmt of `D`, if `D` is one of the
+// followings:
+//   VarDecl
+//   FieldDecl
+//   FunctionDecl
+//   BlockDecl
+//   ObjCMethodDecl
+static void populateStmtsForFindingGadgets(SmallVector<const Stmt *> &Stmts,
+                                           const Decl *D) {
+  auto AddStmt = [&Stmts](const Stmt *S) {
+    if (S)
+      Stmts.push_back(S);
+  };
+  if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
+    AddStmt(FD->getBody());
+    for (const auto *PD : FD->parameters())
+      AddStmt(PD->getDefaultArg());
+    if (const auto *CtorD = dyn_cast<CXXConstructorDecl>(FD))
+      llvm::append_range(
+          Stmts, llvm::map_range(CtorD->inits(),
+                                 std::mem_fn(&CXXCtorInitializer::getInit)));
+  } else 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)) {
+    AddStmt(FD->getInClassInitializer());
+  }
+}
+
+std::set<const Expr *> clang::findUnsafePointers(const Decl *D) {
   class MockReporter : public UnsafeBufferUsageHandler {
   public:
     MockReporter() {}
@@ -2981,9 +3013,13 @@ std::set<const Expr *> clang::findUnsafePointers(const FunctionDecl *FD) {
   WarningGadgetList WarningGadgets;
   DeclUseTracker Tracker;
   MockReporter IgnoreHandler;
+  ASTContext &Ctx = D->getASTContext();
+  SmallVector<const Stmt *> Stmts;
 
-  findGadgets(FD->getBody(), FD->getASTContext(), IgnoreHandler, false,
-              FixableGadgets, WarningGadgets, Tracker);
+  populateStmtsForFindingGadgets(Stmts, D);
+  for (auto *Stmt : Stmts)
+    findGadgets(Stmt, Ctx, IgnoreHandler, false, FixableGadgets, WarningGadgets,
+                Tracker);
 
   std::set<const Expr *> Result;
   for (auto &G : WarningGadgets) {
@@ -4673,9 +4709,6 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
 #endif
 
   assert(D);
-
-  SmallVector<Stmt *> Stmts;
-
   if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
     // Consteval functions are free of UB by the spec, so we don't need to
     // visit them or produce diagnostics.
@@ -4697,27 +4730,21 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
         break;
       }
     }
+  }
 
-    Stmts.push_back(FD->getBody());
+  SmallVector<const Stmt *> Stmts;
 
-    if (const auto *ID = dyn_cast<CXXConstructorDecl>(D)) {
-      for (const CXXCtorInitializer *CI : ID->inits()) {
-        Stmts.push_back(CI->getInit());
-      }
-    }
-  } else if (isa<BlockDecl>(D) || isa<ObjCMethodDecl>(D)) {
-    Stmts.push_back(D->getBody());
-  }
+  populateStmtsForFindingGadgets(Stmts, D);
 
   assert(!Stmts.empty());
 
   FixableGadgetList FixableGadgets;
   WarningGadgetList WarningGadgets;
   DeclUseTracker Tracker;
-  for (Stmt *S : Stmts) {
+  for (const Stmt *S : Stmts) {
     findGadgets(S, D->getASTContext(), Handler, EmitSuggestions, FixableGadgets,
                 WarningGadgets, Tracker);
   }
   applyGadgets(D, std::move(FixableGadgets), std::move(WarningGadgets),
                std::move(Tracker), Handler, EmitSuggestions);
-}
+}
\ No newline at end of file
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
index c150b2918db59..c609168e4dc7d 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
@@ -33,6 +33,32 @@ static bool hasPointerType(const Expr *E) {
 constexpr inline auto buildEntityPointerLevel =
     UnsafeBufferUsageTUSummaryExtractor::buildEntityPointerLevel;
 
+static llvm::Error makeUnsupportedStmtKindError(const Stmt *Unsupported) {
+  return llvm::createStringError(
+      "unsupported expression kind for translation to "
+      "EntityPointerLevel: %s",
+      Unsupported->getStmtClassName());
+}
+
+static llvm::Error makeCreateEntityNameError(const NamedDecl *FailedDecl,
+                                             ASTContext &Ctx) {
+  std::string LocStr = FailedDecl->getSourceRange().getBegin().printToString(
+      Ctx.getSourceManager());
+  return llvm::createStringError(
+      "failed to create entity name for %s declared at %s",
+      FailedDecl->getNameAsString().c_str(), LocStr.c_str());
+}
+
+static llvm::Error makeAddEntitySummaryError(const NamedDecl *FailedContributor,
+                                             ASTContext &Ctx) {
+  std::string LocStr =
+      FailedContributor->getSourceRange().getBegin().printToString(
+          Ctx.getSourceManager());
+  return llvm::createStringError(
+      "failed to add entity summary for contributor %s declared at %s",
+      FailedContributor->getNameAsString().c_str(), LocStr.c_str());
+}
+
 // Translate a pointer 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
@@ -59,10 +85,7 @@ class EntityPointerLevelTranslator
 
   // Fallback method for all unsupported expression kind:
   llvm::Error fallback(const Stmt *E) {
-    return llvm::createStringError(
-        "unsupported expression kind for translation to "
-        "EntityPointerLevel: %s",
-        E->getStmtClassName());
+    return makeUnsupportedStmtKindError(E);
   }
 
   static EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E) {
@@ -92,10 +115,12 @@ class EntityPointerLevelTranslator
   }
 
   UnsafeBufferUsageTUSummaryExtractor &Extractor;
+  ASTContext &Ctx;
 
 public:
-  EntityPointerLevelTranslator(UnsafeBufferUsageTUSummaryExtractor &Extractor)
-      : Extractor(Extractor) {}
+  EntityPointerLevelTranslator(UnsafeBufferUsageTUSummaryExtractor &Extractor,
+                               ASTContext &Ctx)
+      : Extractor(Extractor), Ctx(Ctx) {}
 
   Expected<EntityPointerLevelSet> translate(const Expr *E) { return Visit(E); }
 
@@ -210,18 +235,14 @@ class EntityPointerLevelTranslator
   Expected<EntityPointerLevelSet> VisitDeclRefExpr(const DeclRefExpr *E) {
     if (auto EntityName = getEntityName(E->getDecl()))
       return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
-    return llvm::createStringError(
-        "failed to create entity name from the Decl of " +
-        E->getNameInfo().getAsString());
+    return makeCreateEntityNameError(E->getDecl(), Ctx);
   }
 
   // Translate({., ->}f) -> {(MemberDecl, 1)}
   Expected<EntityPointerLevelSet> VisitMemberExpr(const MemberExpr *E) {
     if (auto EntityName = getEntityName(E->getMemberDecl()))
       return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
-    return llvm::createStringError(
-        "failed to create entity name from the MemberDecl of " +
-        E->getMemberDecl()->getNameAsString());
+    return makeCreateEntityNameError(E->getMemberDecl(), Ctx);
   }
 
   Expected<EntityPointerLevelSet>
@@ -232,9 +253,10 @@ class EntityPointerLevelTranslator
 
 Expected<EntityPointerLevelSet>
 buildEntityPointerLevels(std::set<const Expr *> &&UnsafePointers,
-                         UnsafeBufferUsageTUSummaryExtractor &Extractor) {
+                         UnsafeBufferUsageTUSummaryExtractor &Extractor,
+                         ASTContext &Ctx) {
   EntityPointerLevelSet Result{};
-  EntityPointerLevelTranslator Translator{Extractor};
+  EntityPointerLevelTranslator Translator{Extractor, Ctx};
   llvm::Error AllErrors = llvm::ErrorSuccess();
 
   for (const Expr *Ptr : UnsafePointers) {
@@ -258,24 +280,90 @@ buildEntityPointerLevels(std::set<const Expr *> &&UnsafePointers,
 }
 } // namespace
 
+static std::set<const Expr *> findUnsafePointersInContributor(const Decl *D) {
+  if (isa<FunctionDecl>(D) || isa<VarDecl>(D))
+    return findUnsafePointers(D);
+  if (auto *RD = dyn_cast<RecordDecl>(D)) {
+    std::set<const Expr *> Result;
+
+    for (const FieldDecl *FD : RD->fields()) {
+      Result.merge(findUnsafePointers(FD));
+    }
+    return Result;
+  }
+  return {};
+}
+
 std::unique_ptr<UnsafeBufferUsageEntitySummary>
 UnsafeBufferUsageTUSummaryExtractor::extractEntitySummary(
     const Decl *Contributor, ASTContext &Ctx, llvm::Error &Error) {
-  // FIXME: findUnsafePointers should accept more kinds of `Decl`s than just
-  // `FunctionDecl`:
-  if (const auto *FD = dyn_cast<FunctionDecl>(Contributor)) {
-    Expected<EntityPointerLevelSet> EPLs =
-        buildEntityPointerLevels(findUnsafePointers(FD), *this);
-
-    if (EPLs)
-      return std::make_unique<UnsafeBufferUsageEntitySummary>(
-          UnsafeBufferUsageEntitySummary(std::move(*EPLs)));
-    Error = EPLs.takeError();
-  }
+  Expected<EntityPointerLevelSet> EPLs = buildEntityPointerLevels(
+      findUnsafePointersInContributor(Contributor), *this, Ctx);
+
+  if (EPLs)
+    return std::make_unique<UnsafeBufferUsageEntitySummary>(
+        UnsafeBufferUsageEntitySummary(std::move(*EPLs)));
+  Error = EPLs.takeError();
   return nullptr;
 }
 
 void UnsafeBufferUsageTUSummaryExtractor::HandleTranslationUnit(
     ASTContext &Ctx) {
-  // FIXME: impl me!
+
+  // FIXME: I suppose finding contributor Decls is commonly needed by all/many
+  // extractors
+  class ContributorFinder : public DynamicRecursiveASTVisitor {
+  public:
+    std::vector<const NamedDecl *> Contributors;
+
+    bool VisitFunctionDecl(FunctionDecl *D) override {
+      Contributors.push_back(D);
+      return true;
+    }
+
+    bool VisitRecordDecl(RecordDecl *D) override {
+      Contributors.push_back(D);
+      return true;
+    }
+
+    bool VisitVarDecl(VarDecl *D) override {
+      DeclContext *DC = D->getDeclContext();
+
+      if (DC->isFileContext() || DC->isNamespace())
+        Contributors.push_back(D);
+      return false;
+    }
+  } ContributorFinder;
+
+  ContributorFinder.VisitTranslationUnitDecl(Ctx.getTranslationUnitDecl());
+
+  llvm::Error Errors = llvm::ErrorSuccess();
+  auto addError = [&Errors](llvm::Error Err) {
+    Errors = llvm::joinErrors(std::move(Errors), std::move(Err));
+  };
+
+  for (auto *CD : ContributorFinder.Contributors) {
+    llvm::Error Error = llvm::ErrorSuccess();
+    auto EntitySummary = extractEntitySummary(CD, Ctx, Error);
+
+    if (Error)
+      addError(std::move(Error));
+    if (EntitySummary->empty())
+      continue;
+
+    auto ContributorName = getEntityName(CD);
+
+    if (!ContributorName) {
+      addError(makeCreateEntityNameError(CD, Ctx));
+      continue;
+    }
+
+    auto [EntitySummaryPtr, Success] = SummaryBuilder.addSummary(
+        addEntity(*ContributorName), std::move(EntitySummary));
+
+    if (!Success)
+      addError(makeAddEntitySummaryError(CD, Ctx));
+  }
+  // FIXME: handle errors!
+  llvm::consumeError(std::move(Errors));
 }
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index 81c742dc30811..67eec3714047b 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -35,7 +35,7 @@ const SomeDecl *findDeclByName(StringRef Name, ASTContext &Ctx) {
     NamedDeclFinder(StringRef SearchingName) : SearchingName(SearchingName) {}
 
     bool VisitDecl(Decl *D) override {
-      if (const auto *ND = dyn_cast<NamedDecl>(D)) {
+      if (const auto *ND = dyn_cast<SomeDecl>(D)) {
         if (ND->getNameAsString() == SearchingName) {
           FoundDecl = ND;
           return false;
@@ -69,16 +69,21 @@ class UnsafeBufferUsageTest : public testing::Test {
       : TUSum(BuildNamespace(BuildNamespaceKind::CompilationUnit, "Mock.cpp")),
         TUSummaryBuilder(TUSum), Extractor(TUSummaryBuilder) {}
 
+  template <typename ContributorDecl = NamedDecl>
   std::unique_ptr<UnsafeBufferUsageEntitySummary>
   setUpTest(StringRef Code, StringRef ContributorName) {
     AST = tooling::buildASTFromCodeWithArgs(
-        Code, {"-Wno-unused-value -Wno-int-to-pointer-cast"});
+        Code, {"-Wno-unused-value", "-Wno-int-to-pointer-cast"});
 
     const auto *ContributorDefn =
-        findDeclByName(ContributorName, AST->getASTContext());
+        findDeclByName<ContributorDecl>(ContributorName, AST->getASTContext());
+
+    if (!ContributorDefn)
+      return nullptr;
+
     std::optional<EntityName> EN = getEntityName(ContributorDefn);
 
-    if (!ContributorDefn || !EN)
+    if (!EN)
       return nullptr;
 
     llvm::Error Error = llvm::ErrorSuccess();
@@ -442,4 +447,53 @@ TEST_F(UnsafeBufferUsageTest, PointerToArrayOfPointers) {
   EXPECT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 3}}));
 }
+
+TEST_F(UnsafeBufferUsageTest, UnsafePointerInGlobalVariableInitializer) {
+  auto Sum = setUpTest(R"cpp(
+      int *gp;
+      int x = gp[5];
+    )cpp",
+                       {"x"});
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, UnsafePointerInFieldInitializer) {
+  auto Sum = setUpTest<>(R"cpp(
+      int *gp;
+      struct Foo {
+        int field = gp[5];
+      };
+    )cpp",
+                       {"Foo"});
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, UnsafePointerInCXXCtorInitializer) {
+  auto Sum = setUpTest<CXXConstructorDecl>(R"cpp(
+      struct Foo {
+        int member;
+        Foo(int *p) : member(p[5]) {}
+      };
+    )cpp",
+                                           {"Foo"});
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, UnsafePointerInDefaultArg) {
+  auto Sum = setUpTest(R"cpp(
+    int * gp;
+    void foo(int x = gp[5]);
+    )cpp",
+                       {"foo"});
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
+}
+
 } // namespace

>From ebd6232da44692340e7f8049b257fb9145f006e3 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Thu, 5 Mar 2026 14:09:34 -0800
Subject: [PATCH 07/30] clean up

---
 clang/lib/Analysis/UnsafeBufferUsage.cpp                        | 2 +-
 .../Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp        | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index d6f554246a1b4..5a9241acbee36 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -4747,4 +4747,4 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
   }
   applyGadgets(D, std::move(FixableGadgets), std::move(WarningGadgets),
                std::move(Tracker), Handler, EmitSuggestions);
-}
\ No newline at end of file
+}
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index 67eec3714047b..db11717a86747 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -460,7 +460,7 @@ TEST_F(UnsafeBufferUsageTest, UnsafePointerInGlobalVariableInitializer) {
 }
 
 TEST_F(UnsafeBufferUsageTest, UnsafePointerInFieldInitializer) {
-  auto Sum = setUpTest<>(R"cpp(
+  auto Sum = setUpTest(R"cpp(
       int *gp;
       struct Foo {
         int field = gp[5];

>From e26fd174ac3e4cca50a8cf2c9bb446854093d12d Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Mon, 16 Mar 2026 17:36:39 -0700
Subject: [PATCH 08/30] Address comments

---
 .../UnsafeBufferUsageTest.cpp                 | 80 +++++++++++++++++++
 1 file changed, 80 insertions(+)

diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index db11717a86747..8108ef8ad77b5 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -472,6 +472,35 @@ TEST_F(UnsafeBufferUsageTest, UnsafePointerInFieldInitializer) {
   EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
 }
 
+TEST_F(UnsafeBufferUsageTest, UnsafePointerInFieldInitializer2) {
+  auto Sum = setUpTest(R"cpp(
+      int *gp;
+      union Foo {
+        int field = gp[5];
+        int x;
+      };
+    )cpp",
+                       {"Foo"});
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, InitializerList) {
+  auto Sum = setUpTest(R"cpp(
+      int *gp;
+      struct Foo {
+        int field;
+        int x;
+      };
+      Foo FooObj{gp[5], 0};
+    )cpp",
+                       {"FooObj"});
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
+}
+
 TEST_F(UnsafeBufferUsageTest, UnsafePointerInCXXCtorInitializer) {
   auto Sum = setUpTest<CXXConstructorDecl>(R"cpp(
       struct Foo {
@@ -496,4 +525,55 @@ TEST_F(UnsafeBufferUsageTest, UnsafePointerInDefaultArg) {
   EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
 }
 
+TEST_F(UnsafeBufferUsageTest, NestedDefinitions) {
+  StringRef Code = R"cpp(
+    int * a = [](){
+      struct Foo {
+        void bar(int * ptr) { ptr[3] = 0; }
+      };
+      return nullptr;
+    }();
+    )cpp";
+  auto Sum = setUpTest(Code, {"bar"});
+
+  EXPECT_NE(Sum, nullptr);
+  // The closest contributor owns the fact:
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"ptr", 1U}}));
+
+  Sum = setUpTest(Code, {"Foo"});
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_TRUE(Sum->empty());
+
+  Sum = setUpTest(Code, {"a"});
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_TRUE(Sum->empty());
+}
+
+TEST_F(UnsafeBufferUsageTest, NestedDefinitions2) {
+  StringRef Code = R"cpp(
+    int main(void) {
+       struct Foo {
+          void bar(int * ptr) { ptr[3] = 0; }
+       };
+    }
+    )cpp";
+  auto Sum = setUpTest(Code, {"bar"});
+
+  EXPECT_NE(Sum, nullptr);
+  // The closest contributor owns the fact:
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"ptr", 1U}}));
+
+  Sum = setUpTest(Code, {"Foo"});
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_TRUE(Sum->empty());
+
+  Sum = setUpTest(Code, {"main"});
+
+  EXPECT_NE(Sum, nullptr);
+  EXPECT_TRUE(Sum->empty());
+}
+
 } // namespace

>From 9a212055d2a271f8bdb3ef254d73c360ca9a539f Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Tue, 10 Mar 2026 12:05:51 -0700
Subject: [PATCH 09/30] [ssaf][UnsafeBufferUsage] Add JSON serialization for
 UnsafeBufferUsage

Implemented and registered a JSONFormat::FormatInfo for
UnsafeBufferUsage analysis

rdar://171920065
---
 .../UnsafeBufferUsage/UnsafeBufferUsage.h     |  28 +++-
 .../SSAFBuiltinForceLinker.h                  |   4 +
 .../Analyses/CMakeLists.txt                   |   3 +-
 .../UnsafeBufferUsage/UnsafeBufferUsage.cpp   |  89 ++++++++++++
 .../Frontend/CMakeLists.txt                   |   1 +
 .../tu-summary-serialization.test             |   4 +
 .../UnsafeBufferUsage/tu-summary.json         | 108 +++++++++++++++
 .../Analysis/Scalable/ssaf-format/list.test   |   3 +-
 clang/tools/clang-ssaf-format/CMakeLists.txt  |   3 +-
 clang/tools/clang-ssaf-linker/CMakeLists.txt  |   1 +
 .../UnsafeBufferUsageTest.cpp                 | 130 +++++++++++++++++-
 .../CMakeLists.txt                            |   1 -
 12 files changed, 363 insertions(+), 12 deletions(-)
 create mode 100644 clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
 create mode 100644 clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test
 create mode 100644 clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary.json

diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
index c0a4c2f76ab48..2b36c47fe67a5 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
@@ -11,6 +11,7 @@
 
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/SummaryName.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
 #include <set>
 
@@ -38,6 +39,7 @@ class EntityPointerLevel {
   unsigned PointerLevel;
 
   friend class UnsafeBufferUsageTUSummaryExtractor;
+  friend class UnsafeBufferUsageEntitySummary;
 
   EntityPointerLevel(EntityId Entity, unsigned PointerLevel)
       : Entity(Entity), PointerLevel(PointerLevel) {}
@@ -91,15 +93,35 @@ class UnsafeBufferUsageEntitySummary final : public EntitySummary {
       : EntitySummary(), UnsafeBuffers(std::move(UnsafeBuffers)) {}
 
 public:
-  SummaryName getSummaryName() const override {
-    return SummaryName{"UnsafeBufferUsage"};
-  };
+  SummaryName getSummaryName() const override { return summaryName(); };
 
   bool operator==(const EntityPointerLevelSet &Other) const {
     return UnsafeBuffers == Other;
   }
 
+  bool operator==(const UnsafeBufferUsageEntitySummary &Other) const {
+    return UnsafeBuffers == Other.UnsafeBuffers;
+  }
+
   bool empty() const { return UnsafeBuffers.empty(); }
+
+  static llvm::json::Object
+  jsonSerializeFn(const EntitySummary &ES,
+                  JSONFormat::EntityIdToJSONFn EntityId2JSON);
+
+  static llvm::Expected<std::unique_ptr<EntitySummary>>
+  jsonDeserializeFn(const llvm::json::Object &Data, EntityIdTable &,
+                    JSONFormat::EntityIdFromJSONFn EntityIdFromJSON);
+
+  static SummaryName summaryName() { return SummaryName{"UnsafeBufferUsage"}; }
+};
+
+struct UnsafeBufferUsageJSONFormatInfo : JSONFormat::FormatInfo {
+  UnsafeBufferUsageJSONFormatInfo()
+      : JSONFormat::FormatInfo(
+            UnsafeBufferUsageEntitySummary::summaryName(),
+            UnsafeBufferUsageEntitySummary::jsonSerializeFn,
+            UnsafeBufferUsageEntitySummary::jsonDeserializeFn) {}
 };
 } // namespace clang::ssaf
 
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h b/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
index 5f201487ca1fe..e0a394da1a921 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
@@ -25,4 +25,8 @@ extern volatile int SSAFJSONFormatAnchorSource;
 [[maybe_unused]] static int SSAFJSONFormatAnchorDestination =
     SSAFJSONFormatAnchorSource;
 
+extern volatile int UnsafeBufferUsageSSAFJSONFormatAnchorSource;
+[[maybe_unused]] static int UnsafeBufferUsageSSAFJSONFormatAnchorDestination =
+    UnsafeBufferUsageSSAFJSONFormatAnchorSource;
+
 #endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_SSAFBUILTINFORCELINKER_H
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
index 34c6dd9b61203..c85fa044c1e9f 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
@@ -3,7 +3,8 @@ set(LLVM_LINK_COMPONENTS
   )
 
 add_clang_library(clangScalableStaticAnalysisFrameworkAnalyses
-  UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
+  UnsafeBufferUsage/UnsafeBufferUsage.cpp
+  UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp  
 
   LINK_LIBS
   clangAST
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
new file mode 100644
index 0000000000000..44eaa20c74236
--- /dev/null
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
@@ -0,0 +1,89 @@
+//===---------- UnsafeBufferUsage.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/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "clang/ScalableStaticAnalysisFramework/SSAFForceLinker.h" // IWYU pragma: keep
+
+namespace {
+constexpr const char *const UnsafeBuffersKey = "UnsafeBuffers";
+} // namespace
+
+namespace clang::ssaf {
+using Object = llvm::json::Object;
+using Array = llvm::json::Array;
+using Value = llvm::json::Value;
+
+llvm::json::Object UnsafeBufferUsageEntitySummary::jsonSerializeFn(
+    const EntitySummary &ES, JSONFormat::EntityIdToJSONFn EntityId2JSON) {
+  // Writes a EntityPointerLevel as
+  // Array {
+  //   Object {
+  //     "@" : [entity-id]
+  //   },
+  //   [pointer-level]
+  // }
+  Array UnsafeBuffersData;
+
+  for (const auto &EPL :
+       static_cast<const UnsafeBufferUsageEntitySummary &>(ES).UnsafeBuffers)
+    UnsafeBuffersData.push_back(
+        Value(Array{// EntityId:
+                    Value(EntityId2JSON(EPL.getEntity())),
+                    // PointerLevel:
+                    Value(EPL.getPointerLevel())}));
+
+  Object Data;
+
+  Data[UnsafeBuffersKey] = Value(std::move(UnsafeBuffersData));
+  return Data;
+}
+
+llvm::Expected<std::unique_ptr<EntitySummary>>
+UnsafeBufferUsageEntitySummary::jsonDeserializeFn(
+    const llvm::json::Object &Data, EntityIdTable &,
+    JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) {
+  const Array *UnsafeBuffersData = Data.getArray(UnsafeBuffersKey);
+  constexpr const char *const ErrMsg = "unrecognized UnsafeBufferUsageEntitySummary data";
+
+  if (!UnsafeBuffersData)
+    return llvm::createStringError(ErrMsg);
+
+  EntityPointerLevelSet UnsafeBuffers;
+
+  for (auto &EltData : *UnsafeBuffersData) {
+    const Array *EltDataAsArr = EltData.getAsArray();
+
+    if (!EltDataAsArr || EltDataAsArr->size() != 2)
+      return llvm::createStringError(ErrMsg);
+
+    const Object *IdData = (*EltDataAsArr)[0].getAsObject();
+    std::optional<uint64_t> PtrLvData = (*EltDataAsArr)[1].getAsInteger();
+
+    if (!IdData || !PtrLvData)
+      return llvm::createStringError(ErrMsg);
+
+    llvm::Expected<EntityId> Id = EntityIdFromJSON(*IdData);
+
+    if (!Id)
+      return Id.takeError();
+    UnsafeBuffers.insert(EntityPointerLevel(Id.get(), *PtrLvData));
+  }
+  return std::make_unique<UnsafeBufferUsageEntitySummary>(
+      UnsafeBufferUsageEntitySummary(std::move(UnsafeBuffers)));
+}
+
+static llvm::Registry<JSONFormat::FormatInfo>::Add<
+    UnsafeBufferUsageJSONFormatInfo>
+    RegisterUnsafeBufferUsageJSONFormatInfo(
+        "UnsafeBufferUsage",
+        "JSON Format info for UnsafeBufferUsageEntitySummary");
+
+} // namespace clang::ssaf
+
+// NOLINTNEXTLINE(misc-use-internal-linkage)
+volatile int UnsafeBufferUsageSSAFJSONFormatAnchorSource = 0;
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Frontend/CMakeLists.txt b/clang/lib/ScalableStaticAnalysisFramework/Frontend/CMakeLists.txt
index b90d9c0ded1a9..3da1558810572 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Frontend/CMakeLists.txt
+++ b/clang/lib/ScalableStaticAnalysisFramework/Frontend/CMakeLists.txt
@@ -9,6 +9,7 @@ add_clang_library(clangScalableStaticAnalysisFrameworkFrontend
   clangAST
   clangBasic
   clangFrontend
+  clangScalableStaticAnalysisFrameworkAnalyses
   clangScalableStaticAnalysisFrameworkCore
   clangSema
   )
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test
new file mode 100644
index 0000000000000..e8cf6020df16f
--- /dev/null
+++ b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test
@@ -0,0 +1,4 @@
+// RUN: rm -f %t.json
+// RUN: clang-ssaf-format -type=tu  %S/tu-summary.json -o %t.json
+// RUN: diff %S/tu-summary.json %t.json
+//
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary.json b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary.json
new file mode 100644
index 0000000000000..382b294ccc7bd
--- /dev/null
+++ b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary.json
@@ -0,0 +1,108 @@
+{
+  "data": [
+    {
+      "summary_data": [
+        {
+          "entity_id": 2,
+          "entity_summary": {
+            "UnsafeBuffers": [
+              [
+                {
+                  "@": 0
+                },
+                1
+              ],
+              [
+                {
+                  "@": 0
+                },
+                2
+              ],
+              [
+                {
+                  "@": 0
+                },
+                3
+              ],
+              [
+                {
+                  "@": 1
+                },
+                1
+              ],
+              [
+                {
+                  "@": 1
+                },
+                2
+              ],
+              [
+                {
+                  "@": 1
+                },
+                3
+              ],
+              [
+                {
+                  "@": 1
+                },
+                4
+              ]
+            ]
+          }
+        }
+      ],
+      "summary_name": "UnsafeBufferUsage"
+    }
+  ],
+  "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/ssaf-format/list.test b/clang/test/Analysis/Scalable/ssaf-format/list.test
index 4d389d78543ef..c965f7dd15f23 100644
--- a/clang/test/Analysis/Scalable/ssaf-format/list.test
+++ b/clang/test/Analysis/Scalable/ssaf-format/list.test
@@ -6,4 +6,5 @@
 // CHECK: Registered serialization formats:
 // CHECK-EMPTY:
 // CHECK-NEXT:   1. json  JSON serialization format
-// CHECK-NEXT:      Analyses: (none)
+// CHECK-NEXT:     Analyses:
+// CHECK-NEXT:         1.1. UnsafeBufferUsage JSON Format info for UnsafeBufferUsageEntitySummary
diff --git a/clang/tools/clang-ssaf-format/CMakeLists.txt b/clang/tools/clang-ssaf-format/CMakeLists.txt
index f64d12eb3fac0..08d091d93bd77 100644
--- a/clang/tools/clang-ssaf-format/CMakeLists.txt
+++ b/clang/tools/clang-ssaf-format/CMakeLists.txt
@@ -10,5 +10,6 @@ add_clang_tool(clang-ssaf-format
 clang_target_link_libraries(clang-ssaf-format
   PRIVATE
   clangBasic
-  clangScalableStaticAnalysisFrameworkCore
+  clangScalableStaticAnalysisFrameworkAnalyses
+  clangScalableStaticAnalysisFrameworkCore  
   )
diff --git a/clang/tools/clang-ssaf-linker/CMakeLists.txt b/clang/tools/clang-ssaf-linker/CMakeLists.txt
index 0f6041fb109c9..d1880843ef7fd 100644
--- a/clang/tools/clang-ssaf-linker/CMakeLists.txt
+++ b/clang/tools/clang-ssaf-linker/CMakeLists.txt
@@ -10,5 +10,6 @@ add_clang_tool(clang-ssaf-linker
 clang_target_link_libraries(clang-ssaf-linker
   PRIVATE
   clangBasic
+  clangScalableStaticAnalysisFrameworkAnalyses
   clangScalableStaticAnalysisFrameworkCore
   )
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index 8108ef8ad77b5..9ce39e80031fb 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -13,9 +13,11 @@
 #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/TUSummary.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
 #include "clang/Tooling/Tooling.h"
+#include "llvm/Testing/Support/Error.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
@@ -81,11 +83,6 @@ class UnsafeBufferUsageTest : public testing::Test {
     if (!ContributorDefn)
       return nullptr;
 
-    std::optional<EntityName> EN = getEntityName(ContributorDefn);
-
-    if (!EN)
-      return nullptr;
-
     llvm::Error Error = llvm::ErrorSuccess();
     auto Sum = Extractor.extractEntitySummary(ContributorDefn,
                                               AST->getASTContext(), Error);
@@ -184,6 +181,129 @@ TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageEntityPointerLevelSetTest) {
   EXPECT_THAT(getSubsetOf(Set, E3), UnorderedElementsAre(P5));
 }
 
+//////////////////////////////////////////////////////////////
+//                     JSON Tests                           //
+//////////////////////////////////////////////////////////////
+// Oracle JSON output for the example:
+// void foo(int ***p, int ****q, int x) {
+//   p[5][5][5];
+//   q[5][5][5][5];
+// }
+constexpr const char *const SerilizationTestOracle = R"cpp({
+  "UnsafeBuffers": [
+    [
+      {
+        "@": 42
+      },
+      1
+    ],
+    [
+      {
+        "@": 42
+      },
+      2
+    ],
+    [
+      {
+        "@": 42
+      },
+      3
+    ],
+    [
+      {
+        "@": 108
+      },
+      1
+    ],
+    [
+      {
+        "@": 108
+      },
+      2
+    ],
+    [
+      {
+        "@": 108
+      },
+      3
+    ],
+    [
+      {
+        "@": 108
+      },
+      4
+    ]
+  ]
+})cpp";
+
+TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageSerializeTest) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int ***p, int ****q, int x) {
+      p[5][5][5];
+      q[5][5][5][5];
+    }
+  )cpp",
+                       "foo");
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U},
+                                     {"p", 2U},
+                                     {"p", 3U},
+                                     {"q", 1U},
+                                     {"q", 2U},
+                                     {"q", 3U},
+                                     {"q", 4U}}));
+
+  using Object = llvm::json::Object;
+  using Value = llvm::json::Value;
+  std::map<EntityId, uint64_t> DummyTable{{*getEntityId("p"), 42},
+                                          {*getEntityId("q"), 108}};
+  Object JData = UnsafeBufferUsageEntitySummary::jsonSerializeFn(
+      *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(UnsafeBufferUsageTest, UnsafeBufferUsageDeserializeTest) {
+  auto Sum = setUpTest(R"cpp(
+    void foo(int ***p, int ****q, int x) {
+      p[5][5][5];
+      q[5][5][5][5];
+    }
+  )cpp",
+                       "foo");
+  ASSERT_NE(Sum, nullptr);
+  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U},
+                                     {"p", 2U},
+                                     {"p", 3U},
+                                     {"q", 1U},
+                                     {"q", 2U},
+                                     {"q", 3U},
+                                     {"q", 4U}}));
+
+  using Object = llvm::json::Object;
+  using Value = llvm::json::Value;
+  std::map<uint64_t, EntityId> DummyTable{{42, *getEntityId("p")},
+                                          {108, *getEntityId("q")}};
+  Expected<Value> ParsedJSON = llvm::json::parse(SerilizationTestOracle);
+
+  ASSERT_THAT_EXPECTED(ParsedJSON, llvm::Succeeded());
+  ASSERT_NE(ParsedJSON->getAsObject(), nullptr);
+
+  EntityIdTable Ignored;
+  auto ParsedSum = UnsafeBufferUsageEntitySummary::jsonDeserializeFn(
+      *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<UnsafeBufferUsageEntitySummary *>(ParsedSum->get()),
+            *Sum);
+}
+
 //////////////////////////////////////////////////////////////
 //                   Extractor Tests                        //
 //////////////////////////////////////////////////////////////
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt b/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
index f541e81d42789..871d9e6b0c02c 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
+++ b/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
@@ -29,7 +29,6 @@ add_distinct_clang_unittest(ClangScalableAnalysisTests
   clangASTMatchers
   clangBasic
   clangFrontend
-  clangScalableStaticAnalysisFrameworkAnalyses
   clangScalableStaticAnalysisFrameworkCore
   clangScalableStaticAnalysisFrameworkFrontend
   clangSerialization

>From 8e063bd570d4204f503940b9459b2349f89b112b Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Tue, 17 Mar 2026 16:10:44 -0700
Subject: [PATCH 10/30] fix a conflict merge issue

---
 .../Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp        | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index 2e8b820ec0861..3555f5196a7b9 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -67,7 +67,7 @@ class UnsafeBufferUsageTest : public testing::Test {
 
   UnsafeBufferUsageTest()
       : TUSum(BuildNamespace(BuildNamespaceKind::CompilationUnit, "Mock.cpp")),
-        Builder(TUSum), Extractor(TUSummaryBuilder) {}
+        Builder(TUSum), Extractor(Builder) {}
 
   template <typename ContributorDecl = NamedDecl>
   std::unique_ptr<UnsafeBufferUsageEntitySummary>

>From 9a4630ddc19dd5a166b8e18df5bdc80e3f647fc7 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Tue, 17 Mar 2026 16:45:21 -0700
Subject: [PATCH 11/30] Fix clang-format issue

---
 .../Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h               | 1 -
 1 file changed, 1 deletion(-)

diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
index 2c2bd6abd7ab5..c0a4c2f76ab48 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
@@ -100,7 +100,6 @@ class UnsafeBufferUsageEntitySummary final : public EntitySummary {
   }
 
   bool empty() const { return UnsafeBuffers.empty(); }
-
 };
 } // namespace clang::ssaf
 

>From e5dbc4890c105fce5ba81bd7d78a23ba5e82ecd7 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Wed, 25 Mar 2026 18:42:26 -0700
Subject: [PATCH 12/30] address some comments

---
 .../UnsafeBufferUsage/UnsafeBufferUsage.h     |  2 +-
 .../UnsafeBufferUsage/UnsafeBufferUsage.cpp   | 23 +++++++++----------
 2 files changed, 12 insertions(+), 13 deletions(-)

diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
index 2b36c47fe67a5..38077173efb6a 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
@@ -38,8 +38,8 @@ class EntityPointerLevel {
   EntityId Entity;
   unsigned PointerLevel;
 
-  friend class UnsafeBufferUsageTUSummaryExtractor;
   friend class UnsafeBufferUsageEntitySummary;
+  friend class UnsafeBufferUsageTUSummaryExtractor;
 
   EntityPointerLevel(EntityId Entity, unsigned PointerLevel)
       : Entity(Entity), PointerLevel(PointerLevel) {}
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
index 44eaa20c74236..59b3a1c34ff52 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
@@ -16,10 +16,9 @@ constexpr const char *const UnsafeBuffersKey = "UnsafeBuffers";
 namespace clang::ssaf {
 using Object = llvm::json::Object;
 using Array = llvm::json::Array;
-using Value = llvm::json::Value;
 
 llvm::json::Object UnsafeBufferUsageEntitySummary::jsonSerializeFn(
-    const EntitySummary &ES, JSONFormat::EntityIdToJSONFn EntityId2JSON) {
+    const EntitySummary &ES, JSONFormat::EntityIdToJSONFn EntityIdToJSON) {
   // Writes a EntityPointerLevel as
   // Array {
   //   Object {
@@ -31,15 +30,14 @@ llvm::json::Object UnsafeBufferUsageEntitySummary::jsonSerializeFn(
 
   for (const auto &EPL :
        static_cast<const UnsafeBufferUsageEntitySummary &>(ES).UnsafeBuffers)
-    UnsafeBuffersData.push_back(
-        Value(Array{// EntityId:
-                    Value(EntityId2JSON(EPL.getEntity())),
-                    // PointerLevel:
-                    Value(EPL.getPointerLevel())}));
+    UnsafeBuffersData.push_back(Array{// EntityId:
+                                      EntityIdToJSON(EPL.getEntity()),
+                                      // PointerLevel:
+                                      EPL.getPointerLevel()});
 
   Object Data;
 
-  Data[UnsafeBuffersKey] = Value(std::move(UnsafeBuffersData));
+  Data[UnsafeBuffersKey] = std::move(UnsafeBuffersData);
   return Data;
 }
 
@@ -48,12 +46,13 @@ UnsafeBufferUsageEntitySummary::jsonDeserializeFn(
     const llvm::json::Object &Data, EntityIdTable &,
     JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) {
   const Array *UnsafeBuffersData = Data.getArray(UnsafeBuffersKey);
-  constexpr const char *const ErrMsg = "unrecognized UnsafeBufferUsageEntitySummary data";
+  constexpr const char *const ErrMsg =
+      "unrecognized UnsafeBufferUsageEntitySummary data";
 
   if (!UnsafeBuffersData)
     return llvm::createStringError(ErrMsg);
 
-  EntityPointerLevelSet UnsafeBuffers;
+  EntityPointerLevelSet EPLs;
 
   for (auto &EltData : *UnsafeBuffersData) {
     const Array *EltDataAsArr = EltData.getAsArray();
@@ -71,10 +70,10 @@ UnsafeBufferUsageEntitySummary::jsonDeserializeFn(
 
     if (!Id)
       return Id.takeError();
-    UnsafeBuffers.insert(EntityPointerLevel(Id.get(), *PtrLvData));
+    EPLs.insert(EntityPointerLevel(Id.get(), *PtrLvData));
   }
   return std::make_unique<UnsafeBufferUsageEntitySummary>(
-      UnsafeBufferUsageEntitySummary(std::move(UnsafeBuffers)));
+      UnsafeBufferUsageEntitySummary(std::move(EPLs)));
 }
 
 static llvm::Registry<JSONFormat::FormatInfo>::Add<

>From 2cbbc1af59d00069202fc3051a01b2913c6e63d0 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Thu, 26 Mar 2026 16:45:57 -0700
Subject: [PATCH 13/30] address comments

---
 .../UnsafeBufferUsage/UnsafeBufferUsage.cpp   | 10 ++--
 .../tu-summary-bad-element.json               | 53 +++++++++++++++++
 .../tu-summary-bad-ptr-level.json             | 58 +++++++++++++++++++
 .../UnsafeBufferUsage/tu-summary-no-key.json  | 58 +++++++++++++++++++
 .../tu-summary-serialization.test             | 16 ++++-
 .../Analysis/Scalable/ssaf-format/list.test   |  4 +-
 .../UnsafeBufferUsageTest.cpp                 |  7 +--
 7 files changed, 194 insertions(+), 12 deletions(-)
 create mode 100644 clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-bad-element.json
 create mode 100644 clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-bad-ptr-level.json
 create mode 100644 clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-no-key.json

diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
index 59b3a1c34ff52..d2257816c9ad9 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
@@ -8,6 +8,7 @@
 
 #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
 #include "clang/ScalableStaticAnalysisFramework/SSAFForceLinker.h" // IWYU pragma: keep
+#include "llvm/Support/Error.h"
 
 namespace {
 constexpr const char *const UnsafeBuffersKey = "UnsafeBuffers";
@@ -46,11 +47,10 @@ UnsafeBufferUsageEntitySummary::jsonDeserializeFn(
     const llvm::json::Object &Data, EntityIdTable &,
     JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) {
   const Array *UnsafeBuffersData = Data.getArray(UnsafeBuffersKey);
-  constexpr const char *const ErrMsg =
-      "unrecognized UnsafeBufferUsageEntitySummary data";
 
   if (!UnsafeBuffersData)
-    return llvm::createStringError(ErrMsg);
+    return llvm::createStringError("expected a json::Object with a key %s",
+                                   UnsafeBuffersKey);
 
   EntityPointerLevelSet EPLs;
 
@@ -58,13 +58,13 @@ UnsafeBufferUsageEntitySummary::jsonDeserializeFn(
     const Array *EltDataAsArr = EltData.getAsArray();
 
     if (!EltDataAsArr || EltDataAsArr->size() != 2)
-      return llvm::createStringError(ErrMsg);
+      return llvm::createStringError("expected a json::Array of size 2");
 
     const Object *IdData = (*EltDataAsArr)[0].getAsObject();
     std::optional<uint64_t> PtrLvData = (*EltDataAsArr)[1].getAsInteger();
 
     if (!IdData || !PtrLvData)
-      return llvm::createStringError(ErrMsg);
+      return llvm::createStringError("expected a json::Value of integer type");
 
     llvm::Expected<EntityId> Id = EntityIdFromJSON(*IdData);
 
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-bad-element.json b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-bad-element.json
new file mode 100644
index 0000000000000..10d8c9457aaeb
--- /dev/null
+++ b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-bad-element.json
@@ -0,0 +1,53 @@
+{
+  "data": [
+    {
+      "summary_data": [
+        {
+          "entity_id": 2,
+          "entity_summary": {
+            "UnsafeBuffers": [
+              42
+            ]
+          }
+        }
+      ],
+      "summary_name": "UnsafeBufferUsage"
+    }
+  ],
+  "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#"
+      }
+    }
+  ],
+  "linkage_table": [
+    {
+      "id": 0,
+      "linkage": {
+        "type": "External"
+      }
+    },
+    {
+      "id": 2,
+      "linkage": {
+        "type": "Internal"
+      }
+    }
+  ],
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "Mock.cpp"
+  }
+}
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-bad-ptr-level.json b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-bad-ptr-level.json
new file mode 100644
index 0000000000000..6b59ff39e6913
--- /dev/null
+++ b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-bad-ptr-level.json
@@ -0,0 +1,58 @@
+{
+  "data": [
+    {
+      "summary_data": [
+        {
+          "entity_id": 2,
+          "entity_summary": {
+            "UnsafeBuffers": [
+              [
+                {
+                  "@": 0
+                },
+                "not-an-integer"
+              ]
+            ]
+          }
+        }
+      ],
+      "summary_name": "UnsafeBufferUsage"
+    }
+  ],
+  "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#"
+      }
+    }
+  ],
+  "linkage_table": [
+    {
+      "id": 0,
+      "linkage": {
+        "type": "External"
+      }
+    },
+    {
+      "id": 2,
+      "linkage": {
+        "type": "Internal"
+      }
+    }
+  ],
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "Mock.cpp"
+  }
+}
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-no-key.json b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-no-key.json
new file mode 100644
index 0000000000000..555070ce3e660
--- /dev/null
+++ b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-no-key.json
@@ -0,0 +1,58 @@
+{
+  "data": [
+    {
+      "summary_data": [
+        {
+          "entity_id": 2,
+          "entity_summary": {
+            "NotUnsafeBuffers": [
+              [
+                {
+                  "@": 0
+                },
+                1
+              ]
+            ]
+          }
+        }
+      ],
+      "summary_name": "UnsafeBufferUsage"
+    }
+  ],
+  "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#"
+      }
+    }
+  ],
+  "linkage_table": [
+    {
+      "id": 0,
+      "linkage": {
+        "type": "External"
+      }
+    },
+    {
+      "id": 2,
+      "linkage": {
+        "type": "Internal"
+      }
+    }
+  ],
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "Mock.cpp"
+  }
+}
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test
index e8cf6020df16f..3853dc2c2408a 100644
--- a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test
+++ b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test
@@ -1,4 +1,18 @@
 // RUN: rm -f %t.json
 // RUN: clang-ssaf-format -type=tu  %S/tu-summary.json -o %t.json
 // RUN: diff %S/tu-summary.json %t.json
-//
+
+// Negative tests:
+
+// RUN: not clang-ssaf-format -type=tu %S/tu-summary-no-key.json 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-NO-KEY
+// CHECK-NO-KEY: expected a json::Object with a key UnsafeBuffers
+
+
+// RUN: not clang-ssaf-format -type=tu %S/tu-summary-bad-element.json 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-BAD-ELEMENT
+// CHECK-BAD-ELEMENT: expected a json::Array of size 2
+
+// RUN: not clang-ssaf-format -type=tu %S/tu-summary-bad-ptr-level.json 2>&1 \
+// RUN:   | FileCheck %s --check-prefix=CHECK-BAD-PTR-LEVEL
+// CHECK-BAD-PTR-LEVEL: expected a json::Value of integer type
diff --git a/clang/test/Analysis/Scalable/ssaf-format/list.test b/clang/test/Analysis/Scalable/ssaf-format/list.test
index c89dc7c60e62a..28588a18230f9 100644
--- a/clang/test/Analysis/Scalable/ssaf-format/list.test
+++ b/clang/test/Analysis/Scalable/ssaf-format/list.test
@@ -5,6 +5,6 @@
 
 // CHECK: Registered serialization formats:
 // CHECK-EMPTY:
-// CHECK-NEXT:   1. json - JSON serialization format
+// CHECK-NEXT:  1. json - JSON serialization format
 // CHECK-NEXT:     Analyses:
-// CHECK-NEXT:         1.1. UnsafeBufferUsage JSON Format info for UnsafeBufferUsageEntitySummary
+// CHECK-NEXT:         1.1. UnsafeBufferUsage - JSON Format info for UnsafeBufferUsageEntitySummary
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index 8b5b76b6e58b6..d40ff295ea940 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -259,13 +259,12 @@ TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageSerializeTest) {
                                      {"q", 4U}}));
 
   using Object = llvm::json::Object;
-  using Value = llvm::json::Value;
+
   std::map<EntityId, uint64_t> DummyTable{{*getEntityId("p"), 42},
                                           {*getEntityId("q"), 108}};
   Object JData = UnsafeBufferUsageEntitySummary::jsonSerializeFn(
-      *Sum, [&DummyTable](EntityId Id) {
-        return Object{{"@", Value(DummyTable[Id])}};
-      });
+      *Sum,
+      [&DummyTable](EntityId Id) { return Object{{"@", DummyTable[Id]}}; });
 
   EXPECT_EQ(llvm::formatv("{0:2}", llvm::json::Value(std::move(JData))).str(),
             SerilizationTestOracle);

>From 84206125a0eca09c76d9b3783d8a920933c7e735 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Fri, 27 Mar 2026 12:07:19 -0700
Subject: [PATCH 14/30] Update
 clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

adjust the position of the file title

Co-authored-by: Balázs Benics <benicsbalazs at gmail.com>
---
 .../Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp            | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
index d2257816c9ad9..2ec0614f0cefc 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
@@ -1,4 +1,4 @@
-//===---------- UnsafeBufferUsage.cpp -------------------------------------===//
+//===- UnsafeBufferUsage.cpp ----------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.

>From f8a2e0e6668d9981b4ab7d65ee8d958cdc89a26f Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Fri, 27 Mar 2026 12:10:30 -0700
Subject: [PATCH 15/30] Update
 clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Remove "#include SSAFForceLinker.h"

Co-authored-by: Balázs Benics <benicsbalazs at gmail.com>
---
 .../Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp             | 1 -
 1 file changed, 1 deletion(-)

diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
index 2ec0614f0cefc..a20b5895d55c7 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
@@ -7,7 +7,6 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
-#include "clang/ScalableStaticAnalysisFramework/SSAFForceLinker.h" // IWYU pragma: keep
 #include "llvm/Support/Error.h"
 
 namespace {

>From 354f742f5e173c8a62c542a0ea498f33576bcdac Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Fri, 27 Mar 2026 12:14:26 -0700
Subject: [PATCH 16/30] Update
 clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Replace "const char * const" with "llvm::StringLiteral"

Co-authored-by: Balázs Benics <benicsbalazs at gmail.com>
---
 .../Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp          | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
index a20b5895d55c7..27bfde4f2829e 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
@@ -9,9 +9,7 @@
 #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
 #include "llvm/Support/Error.h"
 
-namespace {
-constexpr const char *const UnsafeBuffersKey = "UnsafeBuffers";
-} // namespace
+static constexpr llvm::StringLiteral UnsafeBuffersKey = "UnsafeBuffers";
 
 namespace clang::ssaf {
 using Object = llvm::json::Object;

>From 157f3c29f5a77e1d4695b590c450968f3eeedcb9 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Sun, 29 Mar 2026 15:53:41 -0700
Subject: [PATCH 17/30] refactor UnsafeBufferUsage serialization API and test
 for Format independency

---
 .../UnsafeBufferUsage/UnsafeBufferUsage.h     |  74 ++++++++--
 .../UnsafeBufferUsage/UnsafeBufferUsage.cpp   |  97 +++++--------
 .../{ => Inputs}/tu-summary-bad-element.json  |   0
 .../tu-summary-bad-ptr-level.json             |   0
 .../{ => Inputs}/tu-summary-no-key.json       |   0
 .../{ => Inputs}/tu-summary.json              |   0
 .../tu-summary-serialization.test             |  12 +-
 .../Analysis/Scalable/ssaf-format/list.test   |   2 +-
 .../Analyses/MockSerialization.h              |  96 +++++++++++++
 .../UnsafeBufferUsageTest.cpp                 | 127 +++++++-----------
 10 files changed, 245 insertions(+), 163 deletions(-)
 rename clang/test/Analysis/Scalable/UnsafeBufferUsage/{ => Inputs}/tu-summary-bad-element.json (100%)
 rename clang/test/Analysis/Scalable/UnsafeBufferUsage/{ => Inputs}/tu-summary-bad-ptr-level.json (100%)
 rename clang/test/Analysis/Scalable/UnsafeBufferUsage/{ => Inputs}/tu-summary-no-key.json (100%)
 rename clang/test/Analysis/Scalable/UnsafeBufferUsage/{ => Inputs}/tu-summary.json (100%)
 create mode 100644 clang/unittests/ScalableStaticAnalysisFramework/Analyses/MockSerialization.h

diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
index 38077173efb6a..faf48b55bfaaf 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
@@ -13,6 +13,7 @@
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/SummaryName.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
+#include "llvm/ADT/StringRef.h"
 #include <set>
 
 namespace clang::ssaf {
@@ -92,6 +93,9 @@ class UnsafeBufferUsageEntitySummary final : public EntitySummary {
   UnsafeBufferUsageEntitySummary(EntityPointerLevelSet UnsafeBuffers)
       : EntitySummary(), UnsafeBuffers(std::move(UnsafeBuffers)) {}
 
+  static constexpr llvm::StringLiteral SummarySerializationKey =
+      "UnsafeBuffers";
+
 public:
   SummaryName getSummaryName() const override { return summaryName(); };
 
@@ -105,23 +109,65 @@ class UnsafeBufferUsageEntitySummary final : public EntitySummary {
 
   bool empty() const { return UnsafeBuffers.empty(); }
 
-  static llvm::json::Object
-  jsonSerializeFn(const EntitySummary &ES,
-                  JSONFormat::EntityIdToJSONFn EntityId2JSON);
+  static SummaryName summaryName() { return SummaryName{"UnsafeBufferUsage"}; }
 
-  static llvm::Expected<std::unique_ptr<EntitySummary>>
-  jsonDeserializeFn(const llvm::json::Object &Data, EntityIdTable &,
-                    JSONFormat::EntityIdFromJSONFn EntityIdFromJSON);
+  // A SerializationAPI implementation for a specific Format needs to implement:
+  //
+  // 1) Abstract data types, Object (map), Array, Value, ..., etc. with
+  //   supported operations, and
+  // 2) EntityIdToFormatFn, and
+  // 3) EntityIdFromFotmatFn.
+
+  // Format-independent (de-)serialization functions:
+  template <typename SerializationAPI>
+  typename SerializationAPI::Object static serialize(
+      SerializationAPI &SA, const UnsafeBufferUsageEntitySummary &S) {
+    using Array = typename SerializationAPI::Array;
+    using Object = typename SerializationAPI::Object;
+    Array UnsafeBuffersData;
+
+    UnsafeBuffersData.reserve(S.UnsafeBuffers.size());
+    for (const auto &EPL : S.UnsafeBuffers)
+      UnsafeBuffersData.push_back(
+          Array{SA.EntityIdToFormat(EPL.getEntity()), EPL.getPointerLevel()});
+    return Object{{SummarySerializationKey.data(), std::move(UnsafeBuffersData)}};
+  }
 
-  static SummaryName summaryName() { return SummaryName{"UnsafeBufferUsage"}; }
-};
+  template <typename SerializationAPI>
+  llvm::Expected<std::unique_ptr<EntitySummary>> static deserialize(
+      SerializationAPI &SA, const typename SerializationAPI::Object &Data) {
+    using Array = typename SerializationAPI::Array;
+    using Object = typename SerializationAPI::Object;
+    const Array *UnsafeBuffersData = Data.getArray(SummarySerializationKey.data());
+
+    if (!UnsafeBuffersData)
+      return llvm::createStringError("expected a json::Object with a key %s",
+                                     SummarySerializationKey.data());
+
+    EntityPointerLevelSet EPLs;
 
-struct UnsafeBufferUsageJSONFormatInfo : JSONFormat::FormatInfo {
-  UnsafeBufferUsageJSONFormatInfo()
-      : JSONFormat::FormatInfo(
-            UnsafeBufferUsageEntitySummary::summaryName(),
-            UnsafeBufferUsageEntitySummary::jsonSerializeFn,
-            UnsafeBufferUsageEntitySummary::jsonDeserializeFn) {}
+    for (auto &EltData : *UnsafeBuffersData) {
+      const Array *EltDataAsArr = EltData.getAsArray();
+
+      if (!EltDataAsArr || EltDataAsArr->size() != 2)
+        return llvm::createStringError("expected a json::Array of size 2");
+
+      const Object *IdData = (*EltDataAsArr)[0].getAsObject();
+      std::optional<uint64_t> PtrLvData = (*EltDataAsArr)[1].getAsInteger();
+
+      if (!IdData || !PtrLvData)
+        return llvm::createStringError(
+            "expected a json::Value of integer type");
+
+      llvm::Expected<EntityId> Id = SA.EntityIdFromFormat(*IdData);
+
+      if (!Id)
+        return Id.takeError();
+      EPLs.insert(EntityPointerLevel(Id.get(), *PtrLvData));
+    }
+    return std::make_unique<UnsafeBufferUsageEntitySummary>(
+        UnsafeBufferUsageEntitySummary(std::move(EPLs)));
+  }
 };
 } // namespace clang::ssaf
 
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
index 27bfde4f2829e..41b7c250773bf 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
@@ -7,79 +7,52 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
+#include "clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h" // IWYU pragma: keep
 #include "llvm/Support/Error.h"
 
-static constexpr llvm::StringLiteral UnsafeBuffersKey = "UnsafeBuffers";
-
-namespace clang::ssaf {
-using Object = llvm::json::Object;
-using Array = llvm::json::Array;
-
-llvm::json::Object UnsafeBufferUsageEntitySummary::jsonSerializeFn(
-    const EntitySummary &ES, JSONFormat::EntityIdToJSONFn EntityIdToJSON) {
-  // Writes a EntityPointerLevel as
-  // Array {
-  //   Object {
-  //     "@" : [entity-id]
-  //   },
-  //   [pointer-level]
-  // }
-  Array UnsafeBuffersData;
-
-  for (const auto &EPL :
-       static_cast<const UnsafeBufferUsageEntitySummary &>(ES).UnsafeBuffers)
-    UnsafeBuffersData.push_back(Array{// EntityId:
-                                      EntityIdToJSON(EPL.getEntity()),
-                                      // PointerLevel:
-                                      EPL.getPointerLevel()});
-
-  Object Data;
-
-  Data[UnsafeBuffersKey] = std::move(UnsafeBuffersData);
-  return Data;
+using namespace clang;
+using namespace ssaf;
+
+namespace {
+struct JSONSerializationAPI {
+  using Object = llvm::json::Object;
+  using Array = llvm::json::Array;
+  using Value = llvm::json::Value;
+
+  JSONFormat::EntityIdToJSONFn EntityIdToFormat;
+  JSONFormat::EntityIdFromJSONFn EntityIdFromFormat;
+};
+} // namespace
+
+// Adapter functions to the current API:
+static llvm::json::Object jsonSerializeFn(const EntitySummary &Sum,
+                                          JSONFormat::EntityIdToJSONFn Fn) {
+  JSONSerializationAPI SA{Fn, {}};
+  return UnsafeBufferUsageEntitySummary::serialize(
+      SA, *static_cast<const UnsafeBufferUsageEntitySummary *>(&Sum));
 }
 
-llvm::Expected<std::unique_ptr<EntitySummary>>
-UnsafeBufferUsageEntitySummary::jsonDeserializeFn(
-    const llvm::json::Object &Data, EntityIdTable &,
-    JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) {
-  const Array *UnsafeBuffersData = Data.getArray(UnsafeBuffersKey);
-
-  if (!UnsafeBuffersData)
-    return llvm::createStringError("expected a json::Object with a key %s",
-                                   UnsafeBuffersKey);
-
-  EntityPointerLevelSet EPLs;
-
-  for (auto &EltData : *UnsafeBuffersData) {
-    const Array *EltDataAsArr = EltData.getAsArray();
-
-    if (!EltDataAsArr || EltDataAsArr->size() != 2)
-      return llvm::createStringError("expected a json::Array of size 2");
-
-    const Object *IdData = (*EltDataAsArr)[0].getAsObject();
-    std::optional<uint64_t> PtrLvData = (*EltDataAsArr)[1].getAsInteger();
-
-    if (!IdData || !PtrLvData)
-      return llvm::createStringError("expected a json::Value of integer type");
-
-    llvm::Expected<EntityId> Id = EntityIdFromJSON(*IdData);
-
-    if (!Id)
-      return Id.takeError();
-    EPLs.insert(EntityPointerLevel(Id.get(), *PtrLvData));
-  }
-  return std::make_unique<UnsafeBufferUsageEntitySummary>(
-      UnsafeBufferUsageEntitySummary(std::move(EPLs)));
+static llvm::Expected<std::unique_ptr<EntitySummary>>
+jsonDeserializeFn(const llvm::json::Object &Data, EntityIdTable &,
+                  JSONFormat::EntityIdFromJSONFn Fn) {
+  JSONSerializationAPI SA{{}, Fn};
+  return UnsafeBufferUsageEntitySummary::deserialize<JSONSerializationAPI>(
+      SA, Data);
 }
 
+struct UnsafeBufferUsageJSONFormatInfo : JSONFormat::FormatInfo {
+  UnsafeBufferUsageJSONFormatInfo()
+      : JSONFormat::FormatInfo(UnsafeBufferUsageEntitySummary::summaryName(),
+                               jsonSerializeFn, jsonDeserializeFn) {}
+};
+
 static llvm::Registry<JSONFormat::FormatInfo>::Add<
     UnsafeBufferUsageJSONFormatInfo>
     RegisterUnsafeBufferUsageJSONFormatInfo(
         "UnsafeBufferUsage",
         "JSON Format info for UnsafeBufferUsageEntitySummary");
 
-} // namespace clang::ssaf
-
 // NOLINTNEXTLINE(misc-use-internal-linkage)
 volatile int UnsafeBufferUsageSSAFJSONFormatAnchorSource = 0;
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-bad-element.json b/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/tu-summary-bad-element.json
similarity index 100%
rename from clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-bad-element.json
rename to clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/tu-summary-bad-element.json
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-bad-ptr-level.json b/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/tu-summary-bad-ptr-level.json
similarity index 100%
rename from clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-bad-ptr-level.json
rename to clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/tu-summary-bad-ptr-level.json
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-no-key.json b/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/tu-summary-no-key.json
similarity index 100%
rename from clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-no-key.json
rename to clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/tu-summary-no-key.json
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary.json b/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/tu-summary.json
similarity index 100%
rename from clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary.json
rename to clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/tu-summary.json
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test
index 3853dc2c2408a..6a12949f3bbb4 100644
--- a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test
+++ b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test
@@ -1,18 +1,18 @@
-// RUN: rm -f %t.json
-// RUN: clang-ssaf-format -type=tu  %S/tu-summary.json -o %t.json
-// RUN: diff %S/tu-summary.json %t.json
+// 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/tu-summary-no-key.json 2>&1 \
+// 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: expected a json::Object with a key UnsafeBuffers
 
 
-// RUN: not clang-ssaf-format -type=tu %S/tu-summary-bad-element.json 2>&1 \
+// RUN: not clang-ssaf-format -type=tu %S/Inputs/tu-summary-bad-element.json 2>&1 \
 // RUN: | FileCheck %s --check-prefix=CHECK-BAD-ELEMENT
 // CHECK-BAD-ELEMENT: expected a json::Array of size 2
 
-// RUN: not clang-ssaf-format -type=tu %S/tu-summary-bad-ptr-level.json 2>&1 \
+// RUN: not clang-ssaf-format -type=tu %S/Inputs/tu-summary-bad-ptr-level.json 2>&1 \
 // RUN:   | FileCheck %s --check-prefix=CHECK-BAD-PTR-LEVEL
 // CHECK-BAD-PTR-LEVEL: expected a json::Value of integer type
diff --git a/clang/test/Analysis/Scalable/ssaf-format/list.test b/clang/test/Analysis/Scalable/ssaf-format/list.test
index 28588a18230f9..766d6eba1821b 100644
--- a/clang/test/Analysis/Scalable/ssaf-format/list.test
+++ b/clang/test/Analysis/Scalable/ssaf-format/list.test
@@ -7,4 +7,4 @@
 // CHECK-EMPTY:
 // CHECK-NEXT:  1. json - JSON serialization format
 // CHECK-NEXT:     Analyses:
-// CHECK-NEXT:         1.1. UnsafeBufferUsage - JSON Format info for UnsafeBufferUsageEntitySummary
+// CHECK-DAG:        1.1. UnsafeBufferUsage - JSON Format info for UnsafeBufferUsageEntitySummary
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/MockSerialization.h b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/MockSerialization.h
new file mode 100644
index 0000000000000..dfe61f20bda71
--- /dev/null
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/MockSerialization.h
@@ -0,0 +1,96 @@
+//===- MockSerialization.h ------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+//  A mock implementation of SerializationAPI for format-independent testing of
+//  summary serialization
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_UNITTESTS_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_MOCKSERIALIZATION_H
+#define LLVM_CLANG_UNITTESTS_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_MOCKSERIALIZATION_H
+
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/raw_ostream.h"
+#include <initializer_list>
+#include <map>
+#include <optional>
+#include <variant>
+#include <vector>
+
+using namespace llvm;
+using namespace clang;
+using namespace ssaf;
+
+struct MockSerializationAPI {
+  struct Value;
+  using Array = std::vector<Value>;
+
+  struct Object : public std::map<StringRef, Value> {
+    Object(std::initializer_list<Object::value_type> IL)
+        : std::map<StringRef, Value>(std::move(IL)) {};
+
+    const Array *getArray(StringRef Key) const {
+      auto I = find(Key);
+      if (I == end())
+        return nullptr;
+      return I->second.getAsArray();
+    }
+  };
+
+  struct Value {
+  private:
+    std::variant<Object, Array, uint64_t> Data;
+
+  public:
+    Value(Object Data) : Data(Data) {}
+    Value(Array Data) : Data(Data) {}
+    Value(uint64_t Data) : Data(Data) {}
+
+    const Array *getAsArray() const { return std::get_if<Array>(&Data); };
+    const Object *getAsObject() const { return std::get_if<Object>(&Data); };
+    std::optional<uint64_t> getAsInteger() const {
+      if (auto *Int = std::get_if<uint64_t>(&Data))
+        return *Int;
+      return std::nullopt;
+    };
+
+    bool operator==(const Value &Other) const {
+      if (std::holds_alternative<Object>(Data))
+        return Other.getAsObject() &&
+               std::equal_to<std::map<StringRef, Value>>()(
+                   *getAsObject(), *Other.getAsObject());
+      if (std::holds_alternative<Array>(Data))
+        return Other.getAsArray() && *getAsArray() == *Other.getAsArray();
+      if (std::holds_alternative<uint64_t>(Data))
+        return Other.getAsInteger() && *getAsInteger() == *Other.getAsInteger();
+      return false;
+    }
+  };
+
+  Object EntityIdToFormat(EntityId Id) {
+    auto Iter = llvm::find(IDs, Id);
+
+    if (Iter == IDs.end())
+      Iter = IDs.insert(IDs.end(), Id);
+    return Object{{"@", uint64_t(Iter - IDs.begin())}};
+  }
+
+  llvm::Expected<EntityId> EntityIdFromFormat(const Object &Data) {
+    if (Data.count("@"))
+      if (auto Idx = Data.at("@").getAsInteger())
+        return IDs[*Idx];
+    return llvm::createStringError("failed to convert Data to EntityId");
+  }
+
+private:
+  std::vector<EntityId> IDs;
+};
+#endif // LLVM_CLANG_UNITTESTS_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_MOCKSERIALIZATION_H
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index d40ff295ea940..fa12f7e924bc3 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -7,19 +7,22 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "Analyses/MockSerialization.h"
 #include "clang/AST/DynamicRecursiveASTVisitor.h"
 #include "clang/Frontend/ASTUnit.h"
 #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.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/TUSummary.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
 #include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Error.h"
 #include "llvm/Testing/Support/Error.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include <initializer_list>
 
 using namespace clang;
 using namespace ssaf;
@@ -187,59 +190,32 @@ TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageEntityPointerLevelSetTest) {
 }
 
 //////////////////////////////////////////////////////////////
-//                     JSON Tests                           //
+//   (De-)Serialization Tests                               //
 //////////////////////////////////////////////////////////////
-// Oracle JSON output for the example:
-// void foo(int ***p, int ****q, int x) {
-//   p[5][5][5];
-//   q[5][5][5][5];
-// }
-constexpr const char *const SerilizationTestOracle = R"cpp({
-  "UnsafeBuffers": [
-    [
-      {
-        "@": 42
-      },
-      1
-    ],
-    [
-      {
-        "@": 42
-      },
-      2
-    ],
-    [
-      {
-        "@": 42
-      },
-      3
-    ],
-    [
-      {
-        "@": 108
-      },
-      1
-    ],
-    [
-      {
-        "@": 108
-      },
-      2
-    ],
-    [
-      {
-        "@": 108
-      },
-      3
-    ],
-    [
-      {
-        "@": 108
-      },
-      4
-    ]
-  ]
-})cpp";
+
+static MockSerializationAPI::Object SerilizedOracle(EntityId P, EntityId Q,
+                                                    MockSerializationAPI &MSA) {
+  // Oracle serialization data for the example:
+  // void foo(int ***p, int ****q, int x) {
+  //   p[5][5][5];
+  //   q[5][5][5][5];
+  // }
+  using Array = MockSerializationAPI::Array;
+  using Object = MockSerializationAPI::Object;
+
+  Object PData = MSA.EntityIdToFormat(P);
+  Object QData = MSA.EntityIdToFormat(Q);
+
+  return Object{std::pair{"UnsafeBuffers", Array{
+                                               Array{PData, 1U},
+                                               Array{PData, 2U},
+                                               Array{PData, 3U},
+                                               Array{QData, 1U},
+                                               Array{QData, 2U},
+                                               Array{QData, 3U},
+                                               Array{QData, 4U},
+                                           }}};
+}
 
 TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageSerializeTest) {
   auto Sum = setUpTest(R"cpp(
@@ -258,16 +234,15 @@ TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageSerializeTest) {
                                      {"q", 3U},
                                      {"q", 4U}}));
 
-  using Object = llvm::json::Object;
+  MockSerializationAPI MSA;
+  MockSerializationAPI::Object JData =
+      UnsafeBufferUsageEntitySummary::serialize<MockSerializationAPI>(MSA,
+                                                                      *Sum);
 
-  std::map<EntityId, uint64_t> DummyTable{{*getEntityId("p"), 42},
-                                          {*getEntityId("q"), 108}};
-  Object JData = UnsafeBufferUsageEntitySummary::jsonSerializeFn(
-      *Sum,
-      [&DummyTable](EntityId Id) { return Object{{"@", DummyTable[Id]}}; });
+  auto EntityP = getEntityId("p");
+  auto EntityQ = getEntityId("q");
 
-  EXPECT_EQ(llvm::formatv("{0:2}", llvm::json::Value(std::move(JData))).str(),
-            SerilizationTestOracle);
+  EXPECT_EQ(JData, SerilizedOracle(*EntityP, *EntityQ, MSA));
 }
 
 TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageDeserializeTest) {
@@ -287,25 +262,17 @@ TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageDeserializeTest) {
                                      {"q", 3U},
                                      {"q", 4U}}));
 
-  using Object = llvm::json::Object;
-  using Value = llvm::json::Value;
-  std::map<uint64_t, EntityId> DummyTable{{42, *getEntityId("p")},
-                                          {108, *getEntityId("q")}};
-  Expected<Value> ParsedJSON = llvm::json::parse(SerilizationTestOracle);
-
-  ASSERT_THAT_EXPECTED(ParsedJSON, llvm::Succeeded());
-  ASSERT_NE(ParsedJSON->getAsObject(), nullptr);
-
-  EntityIdTable Ignored;
-  auto ParsedSum = UnsafeBufferUsageEntitySummary::jsonDeserializeFn(
-      *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<UnsafeBufferUsageEntitySummary *>(ParsedSum->get()),
-            *Sum);
+  MockSerializationAPI MSA;
+  auto EntityP = getEntityId("p");
+  auto EntityQ = getEntityId("q");
+  auto DeserializedSum =
+      UnsafeBufferUsageEntitySummary::deserialize<MockSerializationAPI>(
+          MSA, SerilizedOracle(*EntityP, *EntityQ, MSA));
+
+  ASSERT_THAT_ERROR(DeserializedSum.takeError(), llvm::Succeeded());
+  EXPECT_EQ(
+      *static_cast<UnsafeBufferUsageEntitySummary *>(DeserializedSum->get()),
+      *Sum);
 }
 
 //////////////////////////////////////////////////////////////

>From 0e7f715a4ad9e4a7cd2ab7b222bfc411340982c9 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Mon, 30 Mar 2026 16:34:13 -0700
Subject: [PATCH 18/30] address comments

---
 .../UnsafeBufferUsage/UnsafeBufferUsage.h     |  69 +----------
 .../UnsafeBufferUsage/UnsafeBufferUsage.cpp   | 111 ++++++++++++++----
 .../Analyses/MockSerialization.h              |  96 ---------------
 .../UnsafeBufferUsageTest.cpp                 |  94 +++++----------
 4 files changed, 127 insertions(+), 243 deletions(-)
 delete mode 100644 clang/unittests/ScalableStaticAnalysisFramework/Analyses/MockSerialization.h

diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
index faf48b55bfaaf..ca42be4f5a899 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
@@ -11,9 +11,8 @@
 
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/SummaryName.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
-#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/iterator_range.h"
 #include <set>
 
 namespace clang::ssaf {
@@ -41,6 +40,7 @@ class EntityPointerLevel {
 
   friend class UnsafeBufferUsageEntitySummary;
   friend class UnsafeBufferUsageTUSummaryExtractor;
+  friend EntityPointerLevel buildEntityPointerLevel(EntityId, unsigned);
 
   EntityPointerLevel(EntityId Entity, unsigned PointerLevel)
       : Entity(Entity), PointerLevel(PointerLevel) {}
@@ -89,13 +89,14 @@ class UnsafeBufferUsageEntitySummary final : public EntitySummary {
   const EntityPointerLevelSet UnsafeBuffers;
 
   friend class UnsafeBufferUsageTUSummaryExtractor;
+  friend UnsafeBufferUsageEntitySummary
+      buildUnsafeBufferUsageEntitySummary(EntityPointerLevelSet);
+  friend llvm::iterator_range<EntityPointerLevelSet::const_iterator>
+  getUnsafeBuffers(const UnsafeBufferUsageEntitySummary &);
 
   UnsafeBufferUsageEntitySummary(EntityPointerLevelSet UnsafeBuffers)
       : EntitySummary(), UnsafeBuffers(std::move(UnsafeBuffers)) {}
 
-  static constexpr llvm::StringLiteral SummarySerializationKey =
-      "UnsafeBuffers";
-
 public:
   SummaryName getSummaryName() const override { return summaryName(); };
 
@@ -110,64 +111,6 @@ class UnsafeBufferUsageEntitySummary final : public EntitySummary {
   bool empty() const { return UnsafeBuffers.empty(); }
 
   static SummaryName summaryName() { return SummaryName{"UnsafeBufferUsage"}; }
-
-  // A SerializationAPI implementation for a specific Format needs to implement:
-  //
-  // 1) Abstract data types, Object (map), Array, Value, ..., etc. with
-  //   supported operations, and
-  // 2) EntityIdToFormatFn, and
-  // 3) EntityIdFromFotmatFn.
-
-  // Format-independent (de-)serialization functions:
-  template <typename SerializationAPI>
-  typename SerializationAPI::Object static serialize(
-      SerializationAPI &SA, const UnsafeBufferUsageEntitySummary &S) {
-    using Array = typename SerializationAPI::Array;
-    using Object = typename SerializationAPI::Object;
-    Array UnsafeBuffersData;
-
-    UnsafeBuffersData.reserve(S.UnsafeBuffers.size());
-    for (const auto &EPL : S.UnsafeBuffers)
-      UnsafeBuffersData.push_back(
-          Array{SA.EntityIdToFormat(EPL.getEntity()), EPL.getPointerLevel()});
-    return Object{{SummarySerializationKey.data(), std::move(UnsafeBuffersData)}};
-  }
-
-  template <typename SerializationAPI>
-  llvm::Expected<std::unique_ptr<EntitySummary>> static deserialize(
-      SerializationAPI &SA, const typename SerializationAPI::Object &Data) {
-    using Array = typename SerializationAPI::Array;
-    using Object = typename SerializationAPI::Object;
-    const Array *UnsafeBuffersData = Data.getArray(SummarySerializationKey.data());
-
-    if (!UnsafeBuffersData)
-      return llvm::createStringError("expected a json::Object with a key %s",
-                                     SummarySerializationKey.data());
-
-    EntityPointerLevelSet EPLs;
-
-    for (auto &EltData : *UnsafeBuffersData) {
-      const Array *EltDataAsArr = EltData.getAsArray();
-
-      if (!EltDataAsArr || EltDataAsArr->size() != 2)
-        return llvm::createStringError("expected a json::Array of size 2");
-
-      const Object *IdData = (*EltDataAsArr)[0].getAsObject();
-      std::optional<uint64_t> PtrLvData = (*EltDataAsArr)[1].getAsInteger();
-
-      if (!IdData || !PtrLvData)
-        return llvm::createStringError(
-            "expected a json::Value of integer type");
-
-      llvm::Expected<EntityId> Id = SA.EntityIdFromFormat(*IdData);
-
-      if (!Id)
-        return Id.takeError();
-      EPLs.insert(EntityPointerLevel(Id.get(), *PtrLvData));
-    }
-    return std::make_unique<UnsafeBufferUsageEntitySummary>(
-        UnsafeBufferUsageEntitySummary(std::move(EPLs)));
-  }
 };
 } // namespace clang::ssaf
 
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
index 41b7c250773bf..903812afb49ff 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
@@ -11,41 +11,88 @@
 #include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
 #include "clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h" // IWYU pragma: keep
 #include "llvm/Support/Error.h"
+#include "llvm/Support/JSON.h"
+#include <cstdint>
 
 using namespace clang;
 using namespace ssaf;
+using Array = llvm::json::Array;
+using Object = llvm::json::Object;
 
 namespace {
-struct JSONSerializationAPI {
-  using Object = llvm::json::Object;
-  using Array = llvm::json::Array;
-  using Value = llvm::json::Value;
-
-  JSONFormat::EntityIdToJSONFn EntityIdToFormat;
-  JSONFormat::EntityIdFromJSONFn EntityIdFromFormat;
-};
+static constexpr llvm::StringLiteral SummarySerializationKey = "UnsafeBuffers";
 } // namespace
 
-// Adapter functions to the current API:
-static llvm::json::Object jsonSerializeFn(const EntitySummary &Sum,
-                                          JSONFormat::EntityIdToJSONFn Fn) {
-  JSONSerializationAPI SA{Fn, {}};
-  return UnsafeBufferUsageEntitySummary::serialize(
-      SA, *static_cast<const UnsafeBufferUsageEntitySummary *>(&Sum));
+namespace clang::ssaf {
+EntityPointerLevel buildEntityPointerLevel(EntityId Id, unsigned PtrLv) {
+  return EntityPointerLevel(Id, PtrLv);
+}
+
+UnsafeBufferUsageEntitySummary
+buildUnsafeBufferUsageEntitySummary(EntityPointerLevelSet UnsafeBuffers) {
+  return UnsafeBufferUsageEntitySummary(std::move(UnsafeBuffers));
+}
+
+llvm::iterator_range<EntityPointerLevelSet::const_iterator>
+getUnsafeBuffers(const UnsafeBufferUsageEntitySummary &S) {
+  return llvm::make_range(S.UnsafeBuffers.begin(), S.UnsafeBuffers.end());
+}
+} // namespace clang::ssaf
+
+Object static serialize(const EntitySummary &S,
+                        JSONFormat::EntityIdToJSONFn Fn) {
+  const UnsafeBufferUsageEntitySummary *SS =
+      static_cast<const UnsafeBufferUsageEntitySummary *>(&S);
+  Array UnsafeBuffersData;
+
+  for (const auto &EPL : getUnsafeBuffers(*SS))
+    UnsafeBuffersData.push_back(
+        Array{Fn(EPL.getEntity()), EPL.getPointerLevel()});
+  return Object{{SummarySerializationKey.data(), std::move(UnsafeBuffersData)}};
+}
+
+llvm::Expected<std::unique_ptr<EntitySummary>> static deserializeImpl(
+    const Object &Data, JSONFormat::EntityIdFromJSONFn Fn) {
+  const Array *UnsafeBuffersData =
+      Data.getArray(SummarySerializationKey.data());
+
+  if (!UnsafeBuffersData)
+    return llvm::createStringError("expected a json::Object with a key %s",
+                                   SummarySerializationKey.data());
+
+  EntityPointerLevelSet EPLs;
+
+  for (auto &EltData : *UnsafeBuffersData) {
+    const Array *EltDataAsArr = EltData.getAsArray();
+
+    if (!EltDataAsArr || EltDataAsArr->size() != 2)
+      return llvm::createStringError("expected a json::Array of size 2");
+
+    const Object *IdData = (*EltDataAsArr)[0].getAsObject();
+    std::optional<uint64_t> PtrLvData = (*EltDataAsArr)[1].getAsInteger();
+
+    if (!IdData || !PtrLvData)
+      return llvm::createStringError("expected a json::Value of integer type");
+
+    llvm::Expected<EntityId> Id = Fn(*IdData);
+
+    if (!Id)
+      return Id.takeError();
+    EPLs.insert(buildEntityPointerLevel(Id.get(), *PtrLvData));
+  }
+  return std::make_unique<UnsafeBufferUsageEntitySummary>(
+      buildUnsafeBufferUsageEntitySummary(std::move(EPLs)));
 }
 
-static llvm::Expected<std::unique_ptr<EntitySummary>>
-jsonDeserializeFn(const llvm::json::Object &Data, EntityIdTable &,
-                  JSONFormat::EntityIdFromJSONFn Fn) {
-  JSONSerializationAPI SA{{}, Fn};
-  return UnsafeBufferUsageEntitySummary::deserialize<JSONSerializationAPI>(
-      SA, Data);
+llvm::Expected<std::unique_ptr<EntitySummary>> static deserialize(
+    const Object &Data, EntityIdTable &, JSONFormat::EntityIdFromJSONFn Fn) {
+  return deserializeImpl(Data, Fn);
 }
 
 struct UnsafeBufferUsageJSONFormatInfo : JSONFormat::FormatInfo {
   UnsafeBufferUsageJSONFormatInfo()
       : JSONFormat::FormatInfo(UnsafeBufferUsageEntitySummary::summaryName(),
-                               jsonSerializeFn, jsonDeserializeFn) {}
+                               serialize, deserialize) {}
 };
 
 static llvm::Registry<JSONFormat::FormatInfo>::Add<
@@ -56,3 +103,25 @@ static llvm::Registry<JSONFormat::FormatInfo>::Add<
 
 // NOLINTNEXTLINE(misc-use-internal-linkage)
 volatile int UnsafeBufferUsageSSAFJSONFormatAnchorSource = 0;
+
+// For unit test:
+extern llvm::Expected<std::unique_ptr<EntitySummary>>
+serializeDeserializeRoundTrip(
+    const UnsafeBufferUsageEntitySummary &S,
+    std::function<uint64_t(EntityId)> IdToIntFn,
+    std::function<llvm::Expected<EntityId>(uint64_t)> IdFromIntFn) {
+
+  auto IdToJson = [&IdToIntFn](EntityId Id) -> Object {
+    return Object({{"@", IdToIntFn(Id)}});
+  };
+  auto IdFromJson =
+      [&IdFromIntFn](const Object &O) -> llvm::Expected<EntityId> {
+    const auto *Int = O.get("@");
+
+    if (Int && Int->getAsUINT64())
+      return IdFromIntFn(*Int->getAsUINT64());
+    return llvm::createStringError("failed to get EntityId from Object");
+  };
+
+  return deserializeImpl(serialize(S, IdToJson), IdFromJson);
+}
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/MockSerialization.h b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/MockSerialization.h
deleted file mode 100644
index dfe61f20bda71..0000000000000
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/MockSerialization.h
+++ /dev/null
@@ -1,96 +0,0 @@
-//===- MockSerialization.h ------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-//
-//  A mock implementation of SerializationAPI for format-independent testing of
-//  summary serialization
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_CLANG_UNITTESTS_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_MOCKSERIALIZATION_H
-#define LLVM_CLANG_UNITTESTS_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_MOCKSERIALIZATION_H
-
-#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
-#include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/Support/Error.h"
-#include "llvm/Support/raw_ostream.h"
-#include <initializer_list>
-#include <map>
-#include <optional>
-#include <variant>
-#include <vector>
-
-using namespace llvm;
-using namespace clang;
-using namespace ssaf;
-
-struct MockSerializationAPI {
-  struct Value;
-  using Array = std::vector<Value>;
-
-  struct Object : public std::map<StringRef, Value> {
-    Object(std::initializer_list<Object::value_type> IL)
-        : std::map<StringRef, Value>(std::move(IL)) {};
-
-    const Array *getArray(StringRef Key) const {
-      auto I = find(Key);
-      if (I == end())
-        return nullptr;
-      return I->second.getAsArray();
-    }
-  };
-
-  struct Value {
-  private:
-    std::variant<Object, Array, uint64_t> Data;
-
-  public:
-    Value(Object Data) : Data(Data) {}
-    Value(Array Data) : Data(Data) {}
-    Value(uint64_t Data) : Data(Data) {}
-
-    const Array *getAsArray() const { return std::get_if<Array>(&Data); };
-    const Object *getAsObject() const { return std::get_if<Object>(&Data); };
-    std::optional<uint64_t> getAsInteger() const {
-      if (auto *Int = std::get_if<uint64_t>(&Data))
-        return *Int;
-      return std::nullopt;
-    };
-
-    bool operator==(const Value &Other) const {
-      if (std::holds_alternative<Object>(Data))
-        return Other.getAsObject() &&
-               std::equal_to<std::map<StringRef, Value>>()(
-                   *getAsObject(), *Other.getAsObject());
-      if (std::holds_alternative<Array>(Data))
-        return Other.getAsArray() && *getAsArray() == *Other.getAsArray();
-      if (std::holds_alternative<uint64_t>(Data))
-        return Other.getAsInteger() && *getAsInteger() == *Other.getAsInteger();
-      return false;
-    }
-  };
-
-  Object EntityIdToFormat(EntityId Id) {
-    auto Iter = llvm::find(IDs, Id);
-
-    if (Iter == IDs.end())
-      Iter = IDs.insert(IDs.end(), Id);
-    return Object{{"@", uint64_t(Iter - IDs.begin())}};
-  }
-
-  llvm::Expected<EntityId> EntityIdFromFormat(const Object &Data) {
-    if (Data.count("@"))
-      if (auto Idx = Data.at("@").getAsInteger())
-        return IDs[*Idx];
-    return llvm::createStringError("failed to convert Data to EntityId");
-  }
-
-private:
-  std::vector<EntityId> IDs;
-};
-#endif // LLVM_CLANG_UNITTESTS_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_MOCKSERIALIZATION_H
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index fa12f7e924bc3..fc90bf93087de 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -7,7 +7,6 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
-#include "Analyses/MockSerialization.h"
 #include "clang/AST/DynamicRecursiveASTVisitor.h"
 #include "clang/Frontend/ASTUnit.h"
 #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h"
@@ -22,12 +21,19 @@
 #include "llvm/Testing/Support/Error.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
-#include <initializer_list>
+#include <memory>
 
 using namespace clang;
 using namespace ssaf;
 using testing::UnorderedElementsAre;
 
+// Declare the test proxy function defined in UnsafeBufferUsage.cpp:
+extern llvm::Expected<std::unique_ptr<EntitySummary>>
+serializeDeserializeRoundTrip(
+    const UnsafeBufferUsageEntitySummary &S,
+    std::function<uint64_t(EntityId)> IdToIntFn,
+    std::function<llvm::Expected<EntityId>(uint64_t)> IdFromIntFn);
+
 namespace {
 
 template <typename SomeDecl = NamedDecl>
@@ -193,30 +199,6 @@ TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageEntityPointerLevelSetTest) {
 //   (De-)Serialization Tests                               //
 //////////////////////////////////////////////////////////////
 
-static MockSerializationAPI::Object SerilizedOracle(EntityId P, EntityId Q,
-                                                    MockSerializationAPI &MSA) {
-  // Oracle serialization data for the example:
-  // void foo(int ***p, int ****q, int x) {
-  //   p[5][5][5];
-  //   q[5][5][5][5];
-  // }
-  using Array = MockSerializationAPI::Array;
-  using Object = MockSerializationAPI::Object;
-
-  Object PData = MSA.EntityIdToFormat(P);
-  Object QData = MSA.EntityIdToFormat(Q);
-
-  return Object{std::pair{"UnsafeBuffers", Array{
-                                               Array{PData, 1U},
-                                               Array{PData, 2U},
-                                               Array{PData, 3U},
-                                               Array{QData, 1U},
-                                               Array{QData, 2U},
-                                               Array{QData, 3U},
-                                               Array{QData, 4U},
-                                           }}};
-}
-
 TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageSerializeTest) {
   auto Sum = setUpTest(R"cpp(
     void foo(int ***p, int ****q, int x) {
@@ -234,45 +216,31 @@ TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageSerializeTest) {
                                      {"q", 3U},
                                      {"q", 4U}}));
 
-  MockSerializationAPI MSA;
-  MockSerializationAPI::Object JData =
-      UnsafeBufferUsageEntitySummary::serialize<MockSerializationAPI>(MSA,
-                                                                      *Sum);
-
-  auto EntityP = getEntityId("p");
-  auto EntityQ = getEntityId("q");
-
-  EXPECT_EQ(JData, SerilizedOracle(*EntityP, *EntityQ, MSA));
-}
-
-TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageDeserializeTest) {
-  auto Sum = setUpTest(R"cpp(
-    void foo(int ***p, int ****q, int x) {
-      p[5][5][5];
-      q[5][5][5][5];
+  std::vector<EntityId> Table;
+  std::function<uint64_t(EntityId)> IdToIntFn =
+      [&Table](EntityId Id) -> uint64_t {
+    auto I = llvm::find(Table, Id);
+    if (I == Table.end()) {
+      Table.push_back(Id);
+      return Table.size() - 1;
     }
-  )cpp",
-                       "foo");
-  ASSERT_NE(Sum, nullptr);
-  EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U},
-                                     {"p", 2U},
-                                     {"p", 3U},
-                                     {"q", 1U},
-                                     {"q", 2U},
-                                     {"q", 3U},
-                                     {"q", 4U}}));
+    return I - Table.begin();
+  };
+  std::function<llvm::Expected<EntityId>(uint64_t)> IdFromIntFn =
+      [&Table](uint64_t Int) -> llvm::Expected<EntityId> {
+    if (0 <= Int && Int < Table.size())
+      return Table[Int];
+    return llvm::createStringError(
+        "unrecognized dummy index %d for an EntityId", Int);
+  };
+
+  auto RoundTripResult =
+      serializeDeserializeRoundTrip(*Sum, IdToIntFn, IdFromIntFn);
 
-  MockSerializationAPI MSA;
-  auto EntityP = getEntityId("p");
-  auto EntityQ = getEntityId("q");
-  auto DeserializedSum =
-      UnsafeBufferUsageEntitySummary::deserialize<MockSerializationAPI>(
-          MSA, SerilizedOracle(*EntityP, *EntityQ, MSA));
-
-  ASSERT_THAT_ERROR(DeserializedSum.takeError(), llvm::Succeeded());
-  EXPECT_EQ(
-      *static_cast<UnsafeBufferUsageEntitySummary *>(DeserializedSum->get()),
-      *Sum);
+  EXPECT_THAT_ERROR(RoundTripResult.takeError(), llvm::Succeeded());
+  ASSERT_NE(*RoundTripResult, nullptr);
+  EXPECT_EQ(*Sum, *static_cast<const UnsafeBufferUsageEntitySummary *>(
+                      RoundTripResult->get()));
 }
 
 //////////////////////////////////////////////////////////////

>From e4d0b9ea812b941059ce3d87066212097e59f2e3 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Tue, 31 Mar 2026 18:44:11 -0700
Subject: [PATCH 19/30] address comments

---
 .../UnsafeBufferUsage/UnsafeBufferUsage.cpp   | 29 +++++++----------
 .../Analysis/Scalable/ssaf-format/list.test   |  2 +-
 .../UnsafeBufferUsageTest.cpp                 | 31 ++++++++++---------
 3 files changed, 29 insertions(+), 33 deletions(-)

diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
index 903812afb49ff..73c20e935d417 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
@@ -9,7 +9,6 @@
 #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
-#include "clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h" // IWYU pragma: keep
 #include "llvm/Support/Error.h"
 #include "llvm/Support/JSON.h"
 #include <cstdint>
@@ -19,40 +18,35 @@ using namespace ssaf;
 using Array = llvm::json::Array;
 using Object = llvm::json::Object;
 
-namespace {
 static constexpr llvm::StringLiteral SummarySerializationKey = "UnsafeBuffers";
-} // namespace
 
-namespace clang::ssaf {
-EntityPointerLevel buildEntityPointerLevel(EntityId Id, unsigned PtrLv) {
+EntityPointerLevel ssaf::buildEntityPointerLevel(EntityId Id, unsigned PtrLv) {
   return EntityPointerLevel(Id, PtrLv);
 }
 
 UnsafeBufferUsageEntitySummary
-buildUnsafeBufferUsageEntitySummary(EntityPointerLevelSet UnsafeBuffers) {
+ssaf::buildUnsafeBufferUsageEntitySummary(EntityPointerLevelSet UnsafeBuffers) {
   return UnsafeBufferUsageEntitySummary(std::move(UnsafeBuffers));
 }
 
 llvm::iterator_range<EntityPointerLevelSet::const_iterator>
-getUnsafeBuffers(const UnsafeBufferUsageEntitySummary &S) {
+ssaf::getUnsafeBuffers(const UnsafeBufferUsageEntitySummary &S) {
   return llvm::make_range(S.UnsafeBuffers.begin(), S.UnsafeBuffers.end());
 }
-} // namespace clang::ssaf
 
-Object static serialize(const EntitySummary &S,
+static Object serialize(const EntitySummary &S,
                         JSONFormat::EntityIdToJSONFn Fn) {
-  const UnsafeBufferUsageEntitySummary *SS =
-      static_cast<const UnsafeBufferUsageEntitySummary *>(&S);
+  const auto &SS = static_cast<const UnsafeBufferUsageEntitySummary &>(S);
   Array UnsafeBuffersData;
 
-  for (const auto &EPL : getUnsafeBuffers(*SS))
+  for (const auto &EPL : getUnsafeBuffers(SS))
     UnsafeBuffersData.push_back(
         Array{Fn(EPL.getEntity()), EPL.getPointerLevel()});
   return Object{{SummarySerializationKey.data(), std::move(UnsafeBuffersData)}};
 }
 
-llvm::Expected<std::unique_ptr<EntitySummary>> static deserializeImpl(
-    const Object &Data, JSONFormat::EntityIdFromJSONFn Fn) {
+static llvm::Expected<std::unique_ptr<EntitySummary>>
+deserializeImpl(const Object &Data, JSONFormat::EntityIdFromJSONFn Fn) {
   const Array *UnsafeBuffersData =
       Data.getArray(SummarySerializationKey.data());
 
@@ -62,7 +56,7 @@ llvm::Expected<std::unique_ptr<EntitySummary>> static deserializeImpl(
 
   EntityPointerLevelSet EPLs;
 
-  for (auto &EltData : *UnsafeBuffersData) {
+  for (const auto &EltData : *UnsafeBuffersData) {
     const Array *EltDataAsArr = EltData.getAsArray();
 
     if (!EltDataAsArr || EltDataAsArr->size() != 2)
@@ -84,8 +78,9 @@ llvm::Expected<std::unique_ptr<EntitySummary>> static deserializeImpl(
       buildUnsafeBufferUsageEntitySummary(std::move(EPLs)));
 }
 
-llvm::Expected<std::unique_ptr<EntitySummary>> static deserialize(
-    const Object &Data, EntityIdTable &, JSONFormat::EntityIdFromJSONFn Fn) {
+static llvm::Expected<std::unique_ptr<EntitySummary>>
+deserialize(const Object &Data, EntityIdTable &,
+            JSONFormat::EntityIdFromJSONFn Fn) {
   return deserializeImpl(Data, Fn);
 }
 
diff --git a/clang/test/Analysis/Scalable/ssaf-format/list.test b/clang/test/Analysis/Scalable/ssaf-format/list.test
index 766d6eba1821b..e7ac9196a25f4 100644
--- a/clang/test/Analysis/Scalable/ssaf-format/list.test
+++ b/clang/test/Analysis/Scalable/ssaf-format/list.test
@@ -7,4 +7,4 @@
 // CHECK-EMPTY:
 // CHECK-NEXT:  1. json - JSON serialization format
 // CHECK-NEXT:     Analyses:
-// CHECK-DAG:        1.1. UnsafeBufferUsage - JSON Format info for UnsafeBufferUsageEntitySummary
+// CHECK-DAG:        {{.*}}. UnsafeBufferUsage - JSON Format info for UnsafeBufferUsageEntitySummary
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index fc90bf93087de..009af529e6a7a 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -7,11 +7,13 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "TestFixture.h"
 #include "clang/AST/DynamicRecursiveASTVisitor.h"
 #include "clang/Frontend/ASTUnit.h"
 #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityIdTable.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummary.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
@@ -22,6 +24,7 @@
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include <memory>
+#include <optional>
 
 using namespace clang;
 using namespace ssaf;
@@ -69,7 +72,7 @@ const FunctionDecl *findFnByName(StringRef Name, ASTContext &Ctx) {
 constexpr inline auto buildEntityPointerLevel =
     UnsafeBufferUsageTUSummaryExtractor::buildEntityPointerLevel;
 
-class UnsafeBufferUsageTest : public testing::Test {
+class UnsafeBufferUsageTest : public TestFixture {
 protected:
   TUSummary TUSum;
   TUSummaryBuilder Builder;
@@ -216,22 +219,20 @@ TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageSerializeTest) {
                                      {"q", 3U},
                                      {"q", 4U}}));
 
-  std::vector<EntityId> Table;
-  std::function<uint64_t(EntityId)> IdToIntFn =
-      [&Table](EntityId Id) -> uint64_t {
-    auto I = llvm::find(Table, Id);
-    if (I == Table.end()) {
-      Table.push_back(Id);
-      return Table.size() - 1;
-    }
-    return I - Table.begin();
+  std::function<uint64_t(EntityId)> IdToIntFn = [](EntityId Id) -> uint64_t {
+    return getIndex(Id);
   };
   std::function<llvm::Expected<EntityId>(uint64_t)> IdFromIntFn =
-      [&Table](uint64_t Int) -> llvm::Expected<EntityId> {
-    if (0 <= Int && Int < Table.size())
-      return Table[Int];
-    return llvm::createStringError(
-        "unrecognized dummy index %d for an EntityId", Int);
+      [this](uint64_t Int) -> llvm::Expected<EntityId> {
+    std::optional<EntityId> Result = std::nullopt;
+
+    getIdTable(TUSum).forEach([&Int, &Result](const EntityName &, EntityId Id) {
+      if (getIndex(Id) == Int)
+        Result = Id;
+    });
+    if (Result)
+      return *Result;
+    return llvm::createStringError("failed to convert %d to an EntityId", Int);
   };
 
   auto RoundTripResult =

>From 54e480ee4d9dbff32c902f1c7f384d81ea537bf5 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Wed, 1 Apr 2026 16:27:34 -0700
Subject: [PATCH 20/30] address comments

---
 .../UnsafeBufferUsage/UnsafeBufferUsage.h     |  5 +++-
 .../UnsafeBufferUsage/UnsafeBufferUsageTest.h | 27 +++++++++++++++++++
 .../UnsafeBufferUsage/UnsafeBufferUsage.cpp   |  7 ++---
 .../UnsafeBufferUsageTest.cpp                 |  8 +-----
 4 files changed, 36 insertions(+), 11 deletions(-)
 create mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h

diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
index ca42be4f5a899..9a561b61c3b1a 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
@@ -12,6 +12,7 @@
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/SummaryName.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
+#include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/iterator_range.h"
 #include <set>
 
@@ -98,6 +99,8 @@ class UnsafeBufferUsageEntitySummary final : public EntitySummary {
       : EntitySummary(), UnsafeBuffers(std::move(UnsafeBuffers)) {}
 
 public:
+  static constexpr llvm::StringLiteral Name = "UnsafeBufferUsage";
+
   SummaryName getSummaryName() const override { return summaryName(); };
 
   bool operator==(const EntityPointerLevelSet &Other) const {
@@ -110,7 +113,7 @@ class UnsafeBufferUsageEntitySummary final : public EntitySummary {
 
   bool empty() const { return UnsafeBuffers.empty(); }
 
-  static SummaryName summaryName() { return SummaryName{"UnsafeBufferUsage"}; }
+  static SummaryName summaryName() { return SummaryName{Name.data()}; }
 };
 } // namespace clang::ssaf
 
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
new file mode 100644
index 0000000000000..883f57c7cfa2c
--- /dev/null
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
@@ -0,0 +1,27 @@
+//===- UnsafeBufferUsageTest.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
+//
+//===----------------------------------------------------------------------===//
+//
+// Functions and data structures that help UnsafeBufferUsage unit tests
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGETEST_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGETEST_H
+
+#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
+#include "llvm/Support/Error.h"
+namespace clang::ssaf {
+
+llvm::Expected<std::unique_ptr<EntitySummary>> serializeDeserializeRoundTrip(
+    const UnsafeBufferUsageEntitySummary &S,
+    std::function<uint64_t(EntityId)> IdToIntFn,
+    std::function<llvm::Expected<EntityId>(uint64_t)> IdFromIntFn);
+
+} // namespace clang::ssaf
+
+#endif
\ No newline at end of file
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
index 73c20e935d417..d325e8df79c20 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
 #include "llvm/Support/Error.h"
@@ -93,15 +94,15 @@ struct UnsafeBufferUsageJSONFormatInfo : JSONFormat::FormatInfo {
 static llvm::Registry<JSONFormat::FormatInfo>::Add<
     UnsafeBufferUsageJSONFormatInfo>
     RegisterUnsafeBufferUsageJSONFormatInfo(
-        "UnsafeBufferUsage",
+        UnsafeBufferUsageEntitySummary::Name,
         "JSON Format info for UnsafeBufferUsageEntitySummary");
 
 // NOLINTNEXTLINE(misc-use-internal-linkage)
 volatile int UnsafeBufferUsageSSAFJSONFormatAnchorSource = 0;
 
 // For unit test:
-extern llvm::Expected<std::unique_ptr<EntitySummary>>
-serializeDeserializeRoundTrip(
+llvm::Expected<std::unique_ptr<EntitySummary>>
+ssaf::serializeDeserializeRoundTrip(
     const UnsafeBufferUsageEntitySummary &S,
     std::function<uint64_t(EntityId)> IdToIntFn,
     std::function<llvm::Expected<EntityId>(uint64_t)> IdFromIntFn) {
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index 009af529e6a7a..91a55700b1356 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h"
 #include "TestFixture.h"
 #include "clang/AST/DynamicRecursiveASTVisitor.h"
 #include "clang/Frontend/ASTUnit.h"
@@ -30,13 +31,6 @@ using namespace clang;
 using namespace ssaf;
 using testing::UnorderedElementsAre;
 
-// Declare the test proxy function defined in UnsafeBufferUsage.cpp:
-extern llvm::Expected<std::unique_ptr<EntitySummary>>
-serializeDeserializeRoundTrip(
-    const UnsafeBufferUsageEntitySummary &S,
-    std::function<uint64_t(EntityId)> IdToIntFn,
-    std::function<llvm::Expected<EntityId>(uint64_t)> IdFromIntFn);
-
 namespace {
 
 template <typename SomeDecl = NamedDecl>

>From 68eddbb18fa01a2d70f18294d60eadeea9c070b0 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Thu, 2 Apr 2026 10:29:41 -0700
Subject: [PATCH 21/30] Update
 clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Balázs Benics <benicsbalazs at gmail.com>
---
 .../Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h          | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
index 883f57c7cfa2c..f198d7624ff82 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
@@ -1,4 +1,4 @@
-//===- UnsafeBufferUsageTest.h -----------------------------*- C++ -*-===//
+//===- UnsafeBufferUsageTest.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.

>From 70c1962df47f436d51f6b96c67f5fe8292293509 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Thu, 2 Apr 2026 10:31:40 -0700
Subject: [PATCH 22/30] Update
 clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Balázs Benics <benicsbalazs at gmail.com>
---
 .../Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h          | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
index f198d7624ff82..ccdee91182dc0 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 //
-// Functions and data structures that help UnsafeBufferUsage unit tests
+// Functions and data structures that help UnsafeBufferUsage unit tests.
 //
 //===----------------------------------------------------------------------===//
 #ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGETEST_H

>From 1b980e089b556c0c7adba88f7e9725bc515d73ca Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Thu, 2 Apr 2026 10:32:53 -0700
Subject: [PATCH 23/30] Update
 clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Balázs Benics <benicsbalazs at gmail.com>
---
 .../Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h       | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
index ccdee91182dc0..b62dcee553c8d 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
@@ -9,8 +9,9 @@
 // Functions and data structures that help UnsafeBufferUsage unit tests.
 //
 //===----------------------------------------------------------------------===//
-#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGETEST_H
-#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGETEST_H
+
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGETEST_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGETEST_H
 
 #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"

>From b220e30eab659495f3d05a156257bdb442444f53 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Thu, 2 Apr 2026 10:34:27 -0700
Subject: [PATCH 24/30] Update
 clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Balázs Benics <benicsbalazs at gmail.com>
---
 .../Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h           | 1 +
 1 file changed, 1 insertion(+)

diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
index b62dcee553c8d..b938a94a4348f 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
@@ -16,6 +16,7 @@
 #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
 #include "llvm/Support/Error.h"
+
 namespace clang::ssaf {
 
 llvm::Expected<std::unique_ptr<EntitySummary>> serializeDeserializeRoundTrip(

>From 9eebb20e9f0733da4b69bcd9cc6bfcd9887da799 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Thu, 2 Apr 2026 10:34:59 -0700
Subject: [PATCH 25/30] Update
 clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Balázs Benics <benicsbalazs at gmail.com>
---
 .../Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h          | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
index b938a94a4348f..6a2d1161246f2 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
@@ -26,4 +26,4 @@ llvm::Expected<std::unique_ptr<EntitySummary>> serializeDeserializeRoundTrip(
 
 } // namespace clang::ssaf
 
-#endif
\ No newline at end of file
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGETEST_H

>From 74863f2b2901b0205dc84db0e9bda40316852713 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Thu, 2 Apr 2026 10:35:35 -0700
Subject: [PATCH 26/30] Update
 clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Balázs Benics <benicsbalazs at gmail.com>
---
 .../Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h              | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
index 9a561b61c3b1a..4e217c2eb87ef 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
@@ -113,7 +113,7 @@ class UnsafeBufferUsageEntitySummary final : public EntitySummary {
 
   bool empty() const { return UnsafeBuffers.empty(); }
 
-  static SummaryName summaryName() { return SummaryName{Name.data()}; }
+  static SummaryName summaryName() { return SummaryName{Name.str()}; }
 };
 } // namespace clang::ssaf
 

>From ce07c3cefef5c5875f6bbb340f2abcf4ab47e9b9 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Thu, 2 Apr 2026 10:36:37 -0700
Subject: [PATCH 27/30] Update
 clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Balázs Benics <benicsbalazs at gmail.com>
---
 .../Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp        | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index 91a55700b1356..51d422c1921af 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -7,11 +7,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
-#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h"
 #include "TestFixture.h"
 #include "clang/AST/DynamicRecursiveASTVisitor.h"
 #include "clang/Frontend/ASTUnit.h"
 #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityIdTable.h"

>From b44a1107845fe00cc16fe2ef8cc8ab2fe7ce30ff Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Thu, 2 Apr 2026 11:26:08 -0700
Subject: [PATCH 28/30] [NFC][SSAF][UnsafeBufferUsage] Separate
 EntityPointerLevel and UnsafeBufferUsage

EntityPointerLevel as a common data structure will later be shared by
UnsafeBufferUsage and pointer assignments analysis. So this commit
makes them separate:
- EntityPointerLevel provides the data structure and translation
- UnsafeBufferUsage uses EntityPointerLevel to translate unsafe pointers to EPLs.
---
 .../Analyses/EntityPointerLevel.h             | 134 +++++++
 .../UnsafeBufferUsage/UnsafeBufferUsage.h     |  76 +---
 .../UnsafeBufferUsageExtractor.h              |   5 -
 .../Analyses/CMakeLists.txt                   |   1 +
 .../Analyses/EntityPointerLevel.cpp           | 332 ++++++++++++++++++
 .../UnsafeBufferUsageExtractor.cpp            | 225 +-----------
 .../UnsafeBufferUsageTest.cpp                 |   4 +-
 7 files changed, 480 insertions(+), 297 deletions(-)
 create mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
 create mode 100644 clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp

diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
new file mode 100644
index 0000000000000..945af7d6c9f3f
--- /dev/null
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
@@ -0,0 +1,134 @@
+//===- 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 an EntityPointerLevel (EPL) from a NamedDecl of a pointer/array type.
+///
+/// \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.
+/// \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,
+                        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);
+
+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/UnsafeBufferUsage/UnsafeBufferUsage.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
index 4e217c2eb87ef..9b85459a284eb 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
@@ -9,83 +9,13 @@
 #ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGE_H
 #define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGE_H
 
-#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/SummaryName.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/ADT/iterator_range.h"
-#include <set>
 
 namespace clang::ssaf {
-
-/// An EntityPointerLevel represents 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 UnsafeBufferUsageEntitySummary;
-  friend class UnsafeBufferUsageTUSummaryExtractor;
-  friend EntityPointerLevel buildEntityPointerLevel(EntityId, unsigned);
-
-  EntityPointerLevel(EntityId Entity, unsigned PointerLevel)
-      : Entity(Entity), PointerLevel(PointerLevel) {}
-
-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>;
-
-/// An UnsafeBufferUsageEntitySummary is an immutable set of unsafe buffers, in
-/// the form of EntityPointerLevel.
+/// An UnsafeBufferUsageEntitySummary contains a set of EntityPointerLevels
+/// extracted from unsafe buffer pointers contributed by an entity.
 class UnsafeBufferUsageEntitySummary final : public EntitySummary {
   const EntityPointerLevelSet UnsafeBuffers;
 
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h
index 765b2c37562ce..13d4e18b4e81f 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h
@@ -22,11 +22,6 @@ class UnsafeBufferUsageTUSummaryExtractor : public TUSummaryExtractor {
   UnsafeBufferUsageTUSummaryExtractor(TUSummaryBuilder &Builder)
       : TUSummaryExtractor(Builder) {}
 
-  static EntityPointerLevel buildEntityPointerLevel(EntityId Entity,
-                                                    unsigned PointerLevel) {
-    return {Entity, PointerLevel};
-  }
-
   EntityId addEntity(EntityName EN) { return SummaryBuilder.addEntity(EN); }
 
   std::unique_ptr<UnsafeBufferUsageEntitySummary>
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
index 926b610aa8dee..68107ce36ab99 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
@@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS
 add_clang_library(clangScalableStaticAnalysisFrameworkAnalyses 
   CallGraph/CallGraphExtractor.cpp
   CallGraph/CallGraphJSONFormat.cpp
+  EntityPointerLevel.cpp
   UnsafeBufferUsage/UnsafeBufferUsage.cpp
   UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
 
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp
new file mode 100644
index 0000000000000..e607529342351
--- /dev/null
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp
@@ -0,0 +1,332 @@
+//===----------------- EntityPointerLevel.cpp -------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
+#include <optional>
+
+namespace {
+using namespace clang;
+template <typename NodeTy, typename... Ts>
+static inline llvm::Error strErrAtNode(ASTContext &Ctx, const NodeTy &N,
+                                       StringRef Fmt, const Ts &...Args) {
+  std::string LocStr = N.getBeginLoc().printToString(Ctx.getSourceManager());
+  llvm::SmallVector<char> FmtData;
+
+  (Fmt + " at %s").toStringRef(FmtData);
+  return llvm::createStringError(FmtData.data(), Args..., LocStr.c_str());
+}
+
+static inline llvm::Error entityNameErrFor(ASTContext &Ctx,
+                                           const NamedDecl &D) {
+  return strErrAtNode(Ctx, D, "failed to create entity name for %s",
+                      D.getNameAsString().data());
+}
+
+template <typename DeclOrExpr>
+static bool hasPtrOrArrType(const DeclOrExpr &E) {
+  return llvm::isa<PointerType>(E.getType().getCanonicalType()) ||
+         llvm::isa<ArrayType>(E.getType().getCanonicalType());
+}
+
+template <typename... Ts>
+static inline llvm::Error makeErrorSawButExpected(const llvm::json::Value &Saw,
+                                                  llvm::StringRef Expected,
+                                                  const Ts &...ExpectedArgs) {
+  return llvm::createStringError(
+      ("saw %s but expected " + Expected).str().c_str(),
+      llvm::formatv("{0:2}", Saw).str().data(), Expected.data(),
+      ExpectedArgs...);
+}
+} // namespace
+
+namespace clang::ssaf {
+// Translate a pointer 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.
+//
+// The translation is a process of traversing into the pointer 'E' until its
+// base address can be represented by an entity, with the number of dereferences
+// tracked by incrementing the pointer level.  Naturally, taking address of, as
+// the inverse operation of dereference, is tracked by decrementing the pointer
+// level.
+//
+// For example, suppose there are pointers and arrays declared as
+//   int *ptr, **p1, **p2;
+//   int arr[10][10];
+// , the translation of expressions involving these base addresses will be:
+//   Translate(ptr + 5)            -> {(ptr, 1)}
+//   Translate(arr[5])             -> {(arr, 2)}
+//   Translate(cond ? p1[5] : p2)  -> {(p1, 2), (p2, 1)}
+//   Translate(&arr[5])            -> {(arr, 1)}
+class EntityPointerLevelTranslator
+    : public ConstStmtVisitor<EntityPointerLevelTranslator,
+                              Expected<EntityPointerLevelSet>> {
+  friend class StmtVisitorBase;
+
+  // Fallback method for all unsupported expression kind:
+  llvm::Error fallback(const Stmt *E) {
+    return strErrAtNode(Ctx, *E,
+                        "attempt to translate %s to EntityPointerLevels",
+                        E->getStmtClassName());
+  }
+
+  EntityPointerLevel createEntityPointerLevelFor(const EntityName &Name) {
+    return EntityPointerLevel({AddEntity(Name), 1});
+  }
+
+  // The common helper function for Translate(*base):
+  // Translate(*base) -> Translate(base) with .pointerLevel + 1
+  Expected<EntityPointerLevelSet> translateDereferencePointer(const Expr *Ptr) {
+    assert(hasPtrOrArrType(*Ptr));
+
+    Expected<EntityPointerLevelSet> SubResult = Visit(Ptr);
+    if (!SubResult)
+      return SubResult.takeError();
+
+    auto Incremented = llvm::map_range(*SubResult, incrementPointerLevel);
+    return EntityPointerLevelSet{Incremented.begin(), Incremented.end()};
+  }
+
+  std::function<EntityId(EntityName EN)> AddEntity;
+  ASTContext &Ctx;
+
+public:
+  EntityPointerLevelTranslator(std::function<EntityId(EntityName EN)> AddEntity,
+                               ASTContext &Ctx)
+      : AddEntity(AddEntity), Ctx(Ctx) {}
+
+  Expected<EntityPointerLevelSet> translate(const Expr *E) { return Visit(E); }
+  Expected<EntityPointerLevel> translate(const NamedDecl *D, bool IsRet) {
+    if (IsRet && !isa<FunctionDecl>(D))
+      return strErrAtNode(
+          Ctx, *D,
+          "attempt to call getEntityNameForReturn on a NamedDecl of %s kind",
+          D->getDeclKindName());
+
+    std::optional<EntityName> EN =
+        IsRet ? getEntityNameForReturn(cast<FunctionDecl>(D))
+              : getEntityName(D);
+    if (EN)
+      return createEntityPointerLevelFor(*EN);
+    return entityNameErrFor(Ctx, *D);
+  }
+
+  static EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E) {
+    return EntityPointerLevel({E.getEntity(), E.getPointerLevel() + 1});
+  }
+
+  static EntityPointerLevel decrementPointerLevel(const EntityPointerLevel &E) {
+    assert(E.getPointerLevel() > 0);
+    return EntityPointerLevel({E.getEntity(), E.getPointerLevel() - 1});
+  }
+
+private:
+  Expected<EntityPointerLevelSet> VisitStmt(const Stmt *E) {
+    return fallback(E);
+  }
+
+  // Translate(base + x)           -> Translate(base)
+  // Translate(x + base)           -> Translate(base)
+  // Translate(base - x)           -> Translate(base)
+  // Translate(base {+=, -=, =} x) -> Translate(base)
+  // Translate(x, base)            -> Translate(base)
+  Expected<EntityPointerLevelSet> VisitBinaryOperator(const BinaryOperator *E) {
+    switch (E->getOpcode()) {
+    case clang::BO_Add:
+      if (hasPtrOrArrType(*E->getLHS()))
+        return Visit(E->getLHS());
+      return Visit(E->getRHS());
+    case clang::BO_Sub:
+    case clang::BO_AddAssign:
+    case clang::BO_SubAssign:
+    case clang::BO_Assign:
+      return Visit(E->getLHS());
+    case clang::BO_Comma:
+      return Visit(E->getRHS());
+    default:
+      return fallback(E);
+    }
+  }
+
+  // Translate({++, --}base)   -> Translate(base)
+  // Translate(base{++, --})   -> Translate(base)
+  // Translate(*base)          -> Translate(base) with .pointerLevel += 1
+  // Translate(&base)          -> {}, if Translate(base) is {}
+  //                           -> Translate(base) with .pointerLevel -= 1
+  Expected<EntityPointerLevelSet> VisitUnaryOperator(const UnaryOperator *E) {
+    switch (E->getOpcode()) {
+    case clang::UO_PostInc:
+    case clang::UO_PostDec:
+    case clang::UO_PreInc:
+    case clang::UO_PreDec:
+      return Visit(E->getSubExpr());
+    case clang::UO_AddrOf: {
+      Expected<EntityPointerLevelSet> SubResult = Visit(E->getSubExpr());
+      if (!SubResult)
+        return SubResult.takeError();
+
+      auto Decremented = llvm::map_range(*SubResult, decrementPointerLevel);
+      return EntityPointerLevelSet{Decremented.begin(), Decremented.end()};
+    }
+    case clang::UO_Deref:
+      return translateDereferencePointer(E->getSubExpr());
+    default:
+      return fallback(E);
+    }
+  }
+
+  // Translate((T*)base) -> Translate(p) if p has pointer type
+  //                     -> {} otherwise
+  Expected<EntityPointerLevelSet> VisitCastExpr(const CastExpr *E) {
+    if (hasPtrOrArrType(*E->getSubExpr()))
+      return Visit(E->getSubExpr());
+    return EntityPointerLevelSet{};
+  }
+
+  // Translate(f(...)) -> {} if it is an indirect call
+  //                   -> {(f_return, 1)}, otherwise
+  Expected<EntityPointerLevelSet> VisitCallExpr(const CallExpr *E) {
+    if (auto *FD = E->getDirectCallee())
+      if (auto FDEntityName = getEntityNameForReturn(FD))
+        return EntityPointerLevelSet{
+            createEntityPointerLevelFor(*FDEntityName)};
+    return EntityPointerLevelSet{};
+  }
+
+  // Translate(base[x]) -> Translate(*base)
+  Expected<EntityPointerLevelSet>
+  VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+    return translateDereferencePointer(E->getBase());
+  }
+
+  // Translate(cond ? base1 : base2) := Translate(base1) U Translate(base2)
+  Expected<EntityPointerLevelSet>
+  VisitAbstractConditionalOperator(const AbstractConditionalOperator *E) {
+    Expected<EntityPointerLevelSet> ReT = Visit(E->getTrueExpr());
+    Expected<EntityPointerLevelSet> ReF = Visit(E->getFalseExpr());
+
+    if (ReT && ReF) {
+      ReT->insert(ReF->begin(), ReF->end());
+      return ReT;
+    }
+    if (!ReF && !ReT)
+      return llvm::joinErrors(ReT.takeError(), ReF.takeError());
+    if (!ReF)
+      return ReF.takeError();
+    return ReT.takeError();
+  }
+
+  Expected<EntityPointerLevelSet> VisitParenExpr(const ParenExpr *E) {
+    return Visit(E->getSubExpr());
+  }
+
+  // Translate("string-literal") -> {}
+  // Buffer accesses on string literals are unsafe, but string literals are not
+  // entities so there is no EntityPointerLevel associated with it.
+  Expected<EntityPointerLevelSet> VisitStringLiteral(const StringLiteral *E) {
+    return EntityPointerLevelSet{};
+  }
+
+  // Translate(DRE) -> {(Decl, 1)}
+  Expected<EntityPointerLevelSet> VisitDeclRefExpr(const DeclRefExpr *E) {
+    if (auto EntityName = getEntityName(E->getDecl()))
+      return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
+    return entityNameErrFor(Ctx, *E->getDecl());
+  }
+
+  // Translate({., ->}f) -> {(MemberDecl, 1)}
+  Expected<EntityPointerLevelSet> VisitMemberExpr(const MemberExpr *E) {
+    if (auto EntityName = getEntityName(E->getMemberDecl()))
+      return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
+    return entityNameErrFor(Ctx, *E->getMemberDecl());
+  }
+
+  // Translate(`DefaultArg`) -> Translate(`DefaultArg->getExpr()`)
+  Expected<EntityPointerLevelSet>
+  VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E) {
+    return Visit(E->getExpr());
+  }
+
+  Expected<EntityPointerLevelSet>
+  VisitOpaqueValueExpr(const OpaqueValueExpr *S) {
+    return Visit(S->getSourceExpr());
+  }
+};
+
+Expected<EntityPointerLevelSet>
+translateEntityPointerLevel(const Expr *E, ASTContext &Ctx,
+                            std::function<EntityId(EntityName EN)> AddEntity) {
+  EntityPointerLevelTranslator Translator(AddEntity, Ctx);
+
+  return Translator.translate(E);
+}
+
+/// Create an EntityPointerLevel from a ValueDecl of a pointer type.
+Expected<EntityPointerLevel>
+creatEntityPointerLevel(const NamedDecl *D, ASTContext &Ctx,
+                        std::function<EntityId(EntityName EN)> AddEntity,
+                        bool IsFunRet) {
+  EntityPointerLevelTranslator Translator(AddEntity, Ctx);
+
+  return Translator.translate(D, IsFunRet);
+}
+
+EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E) {
+  return EntityPointerLevelTranslator::incrementPointerLevel(E);
+}
+
+// Writes an EntityPointerLevel as
+// Array [
+//   Object { "@" : [entity-id]},
+//   [pointer-level-integer]
+// ]
+llvm::json::Value
+entityPointerLevelToJSON(const EntityPointerLevel &EPL,
+                         JSONFormat::EntityIdToJSONFn EntityId2JSON) {
+  return llvm::json::Array{EntityId2JSON(EPL.getEntity()),
+                           llvm::json::Value(EPL.getPointerLevel())};
+}
+
+Expected<EntityPointerLevel>
+entityPointerLevelFromJSON(const llvm::json::Value &EPLData,
+                           JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) {
+  auto *AsArr = EPLData.getAsArray();
+
+  if (!AsArr || AsArr->size() != 2)
+    return makeErrorSawButExpected(
+        EPLData, "a JSON array of size 2: [EntityId, PointerLevel]");
+
+  auto *EntityIdObj = (*AsArr)[0].getAsObject();
+
+  if (!EntityIdObj)
+    return makeErrorSawButExpected((*AsArr)[0],
+                                   "a JSON object representing EntityID");
+
+  Expected<EntityId> Id = EntityIdFromJSON(*EntityIdObj);
+
+  if (!Id)
+    return Id.takeError();
+
+  std::optional<uint64_t> PtrLv = (*AsArr)[1].getAsInteger();
+
+  if (!PtrLv)
+    return makeErrorSawButExpected((*AsArr)[1],
+                                   "a JSON value representing an integer");
+
+  return EntityPointerLevel{std::tie(*Id, *PtrLv)};
+}
+
+EntityPointerLevel buildEntityPointerLevel(EntityId Id, unsigned PtrLv) {
+  return EntityPointerLevel({Id, PtrLv});
+}
+
+} // namespace clang::ssaf
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
index c609168e4dc7d..aeab9fc8867a3 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
@@ -9,14 +9,13 @@
 #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h"
 #include "clang/AST/ASTConsumer.h"
 #include "clang/AST/ASTContext.h"
-#include "clang/AST/Decl.h"
-#include "clang/AST/DynamicRecursiveASTVisitor.h"
-#include "clang/AST/StmtVisitor.h"
 #include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h"
 #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
 #include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/iterator_range.h"
 #include "llvm/Support/Error.h"
 #include <memory>
 
@@ -24,22 +23,6 @@ namespace {
 using namespace clang;
 using namespace ssaf;
 
-static bool hasPointerType(const Expr *E) {
-  auto Ty = E->getType();
-  return !Ty.isNull() && !Ty->isFunctionPointerType() &&
-         (Ty->isPointerType() || Ty->isArrayType());
-}
-
-constexpr inline auto buildEntityPointerLevel =
-    UnsafeBufferUsageTUSummaryExtractor::buildEntityPointerLevel;
-
-static llvm::Error makeUnsupportedStmtKindError(const Stmt *Unsupported) {
-  return llvm::createStringError(
-      "unsupported expression kind for translation to "
-      "EntityPointerLevel: %s",
-      Unsupported->getStmtClassName());
-}
-
 static llvm::Error makeCreateEntityNameError(const NamedDecl *FailedDecl,
                                              ASTContext &Ctx) {
   std::string LocStr = FailedDecl->getSourceRange().getBegin().printToString(
@@ -59,208 +42,17 @@ static llvm::Error makeAddEntitySummaryError(const NamedDecl *FailedContributor,
       FailedContributor->getNameAsString().c_str(), LocStr.c_str());
 }
 
-// Translate a pointer 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.
-//
-// The translation is a process of traversing into the pointer 'E' until its
-// base address can be represented by an entity, with the number of dereferences
-// tracked by incrementing the pointer level.  Naturally, taking address of, as
-// the inverse operation of dereference, is tracked by decrementing the pointer
-// level.
-//
-// For example, suppose there are pointers and arrays declared as
-//   int *ptr, **p1, **p2;
-//   int arr[10][10];
-// , the translation of expressions involving these base addresses will be:
-//   Translate(ptr + 5)            -> {(ptr, 1)}
-//   Translate(arr[5])             -> {(arr, 2)}
-//   Translate(cond ? p1[5] : p2)  -> {(p1, 2), (p2, 1)}
-//   Translate(&arr[5])            -> {(arr, 1)}
-class EntityPointerLevelTranslator
-    : ConstStmtVisitor<EntityPointerLevelTranslator,
-                       Expected<EntityPointerLevelSet>> {
-  friend class StmtVisitorBase;
-
-  // Fallback method for all unsupported expression kind:
-  llvm::Error fallback(const Stmt *E) {
-    return makeUnsupportedStmtKindError(E);
-  }
-
-  static EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E) {
-    return buildEntityPointerLevel(E.getEntity(), E.getPointerLevel() + 1);
-  }
-
-  static EntityPointerLevel decrementPointerLevel(const EntityPointerLevel &E) {
-    assert(E.getPointerLevel() > 0);
-    return buildEntityPointerLevel(E.getEntity(), E.getPointerLevel() - 1);
-  }
-
-  EntityPointerLevel createEntityPointerLevelFor(const EntityName &Name) {
-    return buildEntityPointerLevel(Extractor.addEntity(Name), 1);
-  }
-
-  // The common helper function for Translate(*base):
-  // Translate(*base) -> Translate(base) with .pointerLevel + 1
-  Expected<EntityPointerLevelSet> translateDereferencePointer(const Expr *Ptr) {
-    assert(hasPointerType(Ptr));
-
-    Expected<EntityPointerLevelSet> SubResult = Visit(Ptr);
-    if (!SubResult)
-      return SubResult.takeError();
-
-    auto Incremented = llvm::map_range(*SubResult, incrementPointerLevel);
-    return EntityPointerLevelSet{Incremented.begin(), Incremented.end()};
-  }
-
-  UnsafeBufferUsageTUSummaryExtractor &Extractor;
-  ASTContext &Ctx;
-
-public:
-  EntityPointerLevelTranslator(UnsafeBufferUsageTUSummaryExtractor &Extractor,
-                               ASTContext &Ctx)
-      : Extractor(Extractor), Ctx(Ctx) {}
-
-  Expected<EntityPointerLevelSet> translate(const Expr *E) { return Visit(E); }
-
-private:
-  Expected<EntityPointerLevelSet> VisitStmt(const Stmt *E) {
-    return fallback(E);
-  }
-
-  // Translate(base + x)           -> Translate(base)
-  // Translate(x + base)           -> Translate(base)
-  // Translate(base - x)           -> Translate(base)
-  // Translate(base {+=, -=, =} x) -> Translate(base)
-  // Translate(x, base)            -> Translate(base)
-  Expected<EntityPointerLevelSet> VisitBinaryOperator(const BinaryOperator *E) {
-    switch (E->getOpcode()) {
-    case clang::BO_Add:
-      if (hasPointerType(E->getLHS()))
-        return Visit(E->getLHS());
-      return Visit(E->getRHS());
-    case clang::BO_Sub:
-    case clang::BO_AddAssign:
-    case clang::BO_SubAssign:
-    case clang::BO_Assign:
-      return Visit(E->getLHS());
-    case clang::BO_Comma:
-      return Visit(E->getRHS());
-    default:
-      return fallback(E);
-    }
-  }
-
-  // Translate({++, --}base)   -> Translate(base)
-  // Translate(base{++, --})   -> Translate(base)
-  // Translate(*base)          -> Translate(base) with .pointerLevel += 1
-  // Translate(&base)          -> {}, if Translate(base) is {}
-  //                           -> Translate(base) with .pointerLevel -= 1
-  Expected<EntityPointerLevelSet> VisitUnaryOperator(const UnaryOperator *E) {
-    switch (E->getOpcode()) {
-    case clang::UO_PostInc:
-    case clang::UO_PostDec:
-    case clang::UO_PreInc:
-    case clang::UO_PreDec:
-      return Visit(E->getSubExpr());
-    case clang::UO_AddrOf: {
-      Expected<EntityPointerLevelSet> SubResult = Visit(E->getSubExpr());
-      if (!SubResult)
-        return SubResult.takeError();
-
-      auto Decremented = llvm::map_range(*SubResult, decrementPointerLevel);
-      return EntityPointerLevelSet{Decremented.begin(), Decremented.end()};
-    }
-    case clang::UO_Deref:
-      return translateDereferencePointer(E->getSubExpr());
-    default:
-      return fallback(E);
-    }
-  }
-
-  // Translate((T*)base) -> Translate(p) if p has pointer type
-  //                     -> {} otherwise
-  Expected<EntityPointerLevelSet> VisitCastExpr(const CastExpr *E) {
-    if (hasPointerType(E->getSubExpr()))
-      return Visit(E->getSubExpr());
-    return EntityPointerLevelSet{};
-  }
-
-  // Translate(f(...)) -> {} if it is an indirect call
-  //                   -> {(f_return, 1)}, otherwise
-  Expected<EntityPointerLevelSet> VisitCallExpr(const CallExpr *E) {
-    if (auto *FD = E->getDirectCallee())
-      if (auto FDEntityName = getEntityNameForReturn(FD))
-        return EntityPointerLevelSet{
-            createEntityPointerLevelFor(*FDEntityName)};
-    return EntityPointerLevelSet{};
-  }
-
-  // Translate(base[x]) -> Translate(*base)
-  Expected<EntityPointerLevelSet>
-  VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
-    return translateDereferencePointer(E->getBase());
-  }
-
-  // Translate(cond ? base1 : base2) := Translate(base1) U Translate(base2)
-  Expected<EntityPointerLevelSet>
-  VisitAbstractConditionalOperator(const AbstractConditionalOperator *E) {
-    Expected<EntityPointerLevelSet> ReT = Visit(E->getTrueExpr());
-    Expected<EntityPointerLevelSet> ReF = Visit(E->getFalseExpr());
-
-    if (ReT && ReF) {
-      ReT->insert(ReF->begin(), ReF->end());
-      return ReT;
-    }
-    if (!ReF && !ReT)
-      return llvm::joinErrors(ReT.takeError(), ReF.takeError());
-    if (!ReF)
-      return ReF.takeError();
-    return ReT.takeError();
-  }
-
-  Expected<EntityPointerLevelSet> VisitParenExpr(const ParenExpr *E) {
-    return Visit(E->getSubExpr());
-  }
-
-  // Translate("string-literal") -> {}
-  // Buffer accesses on string literals are unsafe, but string literals are not
-  // entities so there is no EntityPointerLevel associated with it.
-  Expected<EntityPointerLevelSet> VisitStringLiteral(const StringLiteral *E) {
-    return EntityPointerLevelSet{};
-  }
-
-  // Translate(DRE) -> {(Decl, 1)}
-  Expected<EntityPointerLevelSet> VisitDeclRefExpr(const DeclRefExpr *E) {
-    if (auto EntityName = getEntityName(E->getDecl()))
-      return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
-    return makeCreateEntityNameError(E->getDecl(), Ctx);
-  }
-
-  // Translate({., ->}f) -> {(MemberDecl, 1)}
-  Expected<EntityPointerLevelSet> VisitMemberExpr(const MemberExpr *E) {
-    if (auto EntityName = getEntityName(E->getMemberDecl()))
-      return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
-    return makeCreateEntityNameError(E->getMemberDecl(), Ctx);
-  }
-
-  Expected<EntityPointerLevelSet>
-  VisitOpaqueValueExpr(const OpaqueValueExpr *S) {
-    return Visit(S->getSourceExpr());
-  }
-};
-
 Expected<EntityPointerLevelSet>
 buildEntityPointerLevels(std::set<const Expr *> &&UnsafePointers,
                          UnsafeBufferUsageTUSummaryExtractor &Extractor,
-                         ASTContext &Ctx) {
+                         ASTContext &Ctx,
+                         std::function<EntityId(EntityName)> AddEntity) {
   EntityPointerLevelSet Result{};
-  EntityPointerLevelTranslator Translator{Extractor, Ctx};
   llvm::Error AllErrors = llvm::ErrorSuccess();
 
   for (const Expr *Ptr : UnsafePointers) {
-    Expected<EntityPointerLevelSet> Translation = Translator.translate(Ptr);
+    Expected<EntityPointerLevelSet> Translation =
+        translateEntityPointerLevel(Ptr, Ctx, AddEntity);
 
     if (Translation) {
       // Filter out those temporary invalid EntityPointerLevels associated with
@@ -297,8 +89,9 @@ static std::set<const Expr *> findUnsafePointersInContributor(const Decl *D) {
 std::unique_ptr<UnsafeBufferUsageEntitySummary>
 UnsafeBufferUsageTUSummaryExtractor::extractEntitySummary(
     const Decl *Contributor, ASTContext &Ctx, llvm::Error &Error) {
+  auto AddEntity = [this](EntityName EN) { return addEntity(EN); };
   Expected<EntityPointerLevelSet> EPLs = buildEntityPointerLevels(
-      findUnsafePointersInContributor(Contributor), *this, Ctx);
+      findUnsafePointersInContributor(Contributor), *this, Ctx, AddEntity);
 
   if (EPLs)
     return std::make_unique<UnsafeBufferUsageEntitySummary>(
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index 51d422c1921af..f14f3caa1e016 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -10,6 +10,7 @@
 #include "TestFixture.h"
 #include "clang/AST/DynamicRecursiveASTVisitor.h"
 #include "clang/Frontend/ASTUnit.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h"
 #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h"
 #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
@@ -63,9 +64,6 @@ const FunctionDecl *findFnByName(StringRef Name, ASTContext &Ctx) {
   return findDeclByName<FunctionDecl>(Name, Ctx);
 }
 
-constexpr inline auto buildEntityPointerLevel =
-    UnsafeBufferUsageTUSummaryExtractor::buildEntityPointerLevel;
-
 class UnsafeBufferUsageTest : public TestFixture {
 protected:
   TUSummary TUSum;

>From f3c29ee58bbc464b98389296eec5e0c899007324 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Wed, 1 Apr 2026 18:59:48 -0700
Subject: [PATCH 29/30] rebase

---
 .../EntityPointerLevel.h                      | 26 +++----
 .../EntityPointerLevelFormat.h                | 67 +++++++++++++++++++
 .../UnsafeBufferUsage/UnsafeBufferUsage.h     |  2 +-
 .../Analyses/CMakeLists.txt                   |  2 +-
 .../EntityPointerLevel.cpp                    | 44 +-----------
 .../UnsafeBufferUsage/UnsafeBufferUsage.cpp   | 33 +++------
 .../UnsafeBufferUsageExtractor.cpp            |  2 +-
 .../tu-summary-serialization.test             | 16 +++--
 .../UnsafeBufferUsageTest.cpp                 |  2 +-
 9 files changed, 103 insertions(+), 91 deletions(-)
 rename clang/include/clang/ScalableStaticAnalysisFramework/Analyses/{ => EntityPointerLevel}/EntityPointerLevel.h (87%)
 create mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
 rename clang/lib/ScalableStaticAnalysisFramework/Analyses/{ => EntityPointerLevel}/EntityPointerLevel.cpp (89%)

diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h
similarity index 87%
rename from clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
rename to clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h
index 945af7d6c9f3f..752b3caaad29d 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h
@@ -6,12 +6,12 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_H
-#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_H
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_ENTITYPOINTERLEVEL_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_ENTITYPOINTERLEVEL_H
 
 #include "clang/AST/Decl.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
 #include <set>
 
 namespace clang::ssaf {
@@ -39,9 +39,6 @@ class EntityPointerLevel {
   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);
 
@@ -114,21 +111,14 @@ creatEntityPointerLevel(const NamedDecl *ND, ASTContext &Ctx,
                         std::function<EntityId(EntityName EN)> AddEntity,
                         bool IsFunRet = false);
 
+/// Creates a `EntityPointerLevel` from a pair of an EntityId and a pointer
+/// level:
+EntityPointerLevel buildEntityPointerLevel(EntityId, unsigned);
+
 /// 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);
-
-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
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_ENTITYPOINTERLEVEL_H
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
new file mode 100644
index 0000000000000..f8b30f8aab9d6
--- /dev/null
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
@@ -0,0 +1,67 @@
+//===- EntityPointerLevelFormat.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_ENTITYPOINTERLEVELFORMAT_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_ENTITYPOINTERLEVELFORMAT_H
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
+
+template <typename... Ts>
+static inline llvm::Error makeSawButExpectedError(const llvm::json::Value &Saw,
+                                                  llvm::StringRef Expected,
+                                                  const Ts &...ExpectedArgs) {
+  std::string Fmt = ("saw %s but expected " + Expected).str();
+  std::string SawStr = llvm::formatv("{0:2}", Saw).str();
+
+  return llvm::createStringError(Fmt.c_str(), SawStr.c_str(), ExpectedArgs...);
+}
+
+namespace clang::ssaf {
+// Writes an EntityPointerLevel as
+// Array [
+//   Object { "@" : [entity-id]},
+//   [pointer-level-integer]
+// ]
+static inline llvm::json::Value
+entityPointerLevelToJSON(const EntityPointerLevel &EPL,
+                         JSONFormat::EntityIdToJSONFn EntityId2JSON) {
+  return llvm::json::Array{EntityId2JSON(EPL.getEntity()),
+                           llvm::json::Value(EPL.getPointerLevel())};
+}
+
+Expected<EntityPointerLevel> static inline entityPointerLevelFromJSON(
+    const llvm::json::Value &EPLData,
+    JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) {
+  auto *AsArr = EPLData.getAsArray();
+
+  if (!AsArr || AsArr->size() != 2)
+    return makeSawButExpectedError(
+        EPLData, "an array with exactly two elements representing "
+                 "EntityId and PointerLevel, respectively");
+
+  auto *EntityIdObj = (*AsArr)[0].getAsObject();
+
+  if (!EntityIdObj)
+    return makeSawButExpectedError((*AsArr)[0],
+                                   "an object representing EntityId");
+
+  Expected<EntityId> Id = EntityIdFromJSON(*EntityIdObj);
+
+  if (!Id)
+    return Id.takeError();
+
+  std::optional<uint64_t> PtrLv = (*AsArr)[1].getAsInteger();
+
+  if (!PtrLv)
+    return makeSawButExpectedError((*AsArr)[1],
+                                   "an integer representing PointerLevel");
+
+  return buildEntityPointerLevel(*Id, *PtrLv);
+}
+} // namespace clang::ssaf
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_ENTITYPOINTERLEVELFORMAT_H
\ No newline at end of file
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
index 9b85459a284eb..27bda9f773085 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
@@ -9,7 +9,7 @@
 #ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGE_H
 #define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGE_H
 
-#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/SummaryName.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
 
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
index 68107ce36ab99..c15ff3b3c42e7 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
@@ -5,7 +5,7 @@ set(LLVM_LINK_COMPONENTS
 add_clang_library(clangScalableStaticAnalysisFrameworkAnalyses 
   CallGraph/CallGraphExtractor.cpp
   CallGraph/CallGraphJSONFormat.cpp
-  EntityPointerLevel.cpp
+  EntityPointerLevel/EntityPointerLevel.cpp
   UnsafeBufferUsage/UnsafeBufferUsage.cpp
   UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
 
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
similarity index 89%
rename from clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp
rename to clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
index e607529342351..b5bf17530f9f2 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
@@ -5,11 +5,12 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
+#include "llvm/Support/JSON.h"
 #include <optional>
 
 namespace {
@@ -284,47 +285,6 @@ EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E) {
   return EntityPointerLevelTranslator::incrementPointerLevel(E);
 }
 
-// Writes an EntityPointerLevel as
-// Array [
-//   Object { "@" : [entity-id]},
-//   [pointer-level-integer]
-// ]
-llvm::json::Value
-entityPointerLevelToJSON(const EntityPointerLevel &EPL,
-                         JSONFormat::EntityIdToJSONFn EntityId2JSON) {
-  return llvm::json::Array{EntityId2JSON(EPL.getEntity()),
-                           llvm::json::Value(EPL.getPointerLevel())};
-}
-
-Expected<EntityPointerLevel>
-entityPointerLevelFromJSON(const llvm::json::Value &EPLData,
-                           JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) {
-  auto *AsArr = EPLData.getAsArray();
-
-  if (!AsArr || AsArr->size() != 2)
-    return makeErrorSawButExpected(
-        EPLData, "a JSON array of size 2: [EntityId, PointerLevel]");
-
-  auto *EntityIdObj = (*AsArr)[0].getAsObject();
-
-  if (!EntityIdObj)
-    return makeErrorSawButExpected((*AsArr)[0],
-                                   "a JSON object representing EntityID");
-
-  Expected<EntityId> Id = EntityIdFromJSON(*EntityIdObj);
-
-  if (!Id)
-    return Id.takeError();
-
-  std::optional<uint64_t> PtrLv = (*AsArr)[1].getAsInteger();
-
-  if (!PtrLv)
-    return makeErrorSawButExpected((*AsArr)[1],
-                                   "a JSON value representing an integer");
-
-  return EntityPointerLevel{std::tie(*Id, *PtrLv)};
-}
-
 EntityPointerLevel buildEntityPointerLevel(EntityId Id, unsigned PtrLv) {
   return EntityPointerLevel({Id, PtrLv});
 }
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
index d325e8df79c20..84afb93239ffc 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
@@ -7,8 +7,9 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h"
 #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/JSON.h"
@@ -21,10 +22,6 @@ using Object = llvm::json::Object;
 
 static constexpr llvm::StringLiteral SummarySerializationKey = "UnsafeBuffers";
 
-EntityPointerLevel ssaf::buildEntityPointerLevel(EntityId Id, unsigned PtrLv) {
-  return EntityPointerLevel(Id, PtrLv);
-}
-
 UnsafeBufferUsageEntitySummary
 ssaf::buildUnsafeBufferUsageEntitySummary(EntityPointerLevelSet UnsafeBuffers) {
   return UnsafeBufferUsageEntitySummary(std::move(UnsafeBuffers));
@@ -41,8 +38,7 @@ static Object serialize(const EntitySummary &S,
   Array UnsafeBuffersData;
 
   for (const auto &EPL : getUnsafeBuffers(SS))
-    UnsafeBuffersData.push_back(
-        Array{Fn(EPL.getEntity()), EPL.getPointerLevel()});
+    UnsafeBuffersData.push_back(entityPointerLevelToJSON(EPL, Fn));
   return Object{{SummarySerializationKey.data(), std::move(UnsafeBuffersData)}};
 }
 
@@ -52,28 +48,19 @@ deserializeImpl(const Object &Data, JSONFormat::EntityIdFromJSONFn Fn) {
       Data.getArray(SummarySerializationKey.data());
 
   if (!UnsafeBuffersData)
-    return llvm::createStringError("expected a json::Object with a key %s",
+    return makeSawButExpectedError(Object(Data),
+                                   "an Object with a key %s",
                                    SummarySerializationKey.data());
 
   EntityPointerLevelSet EPLs;
 
   for (const auto &EltData : *UnsafeBuffersData) {
-    const Array *EltDataAsArr = EltData.getAsArray();
-
-    if (!EltDataAsArr || EltDataAsArr->size() != 2)
-      return llvm::createStringError("expected a json::Array of size 2");
-
-    const Object *IdData = (*EltDataAsArr)[0].getAsObject();
-    std::optional<uint64_t> PtrLvData = (*EltDataAsArr)[1].getAsInteger();
-
-    if (!IdData || !PtrLvData)
-      return llvm::createStringError("expected a json::Value of integer type");
-
-    llvm::Expected<EntityId> Id = Fn(*IdData);
+    llvm::Expected<EntityPointerLevel> EPL =
+        entityPointerLevelFromJSON(EltData, Fn);
 
-    if (!Id)
-      return Id.takeError();
-    EPLs.insert(buildEntityPointerLevel(Id.get(), *PtrLvData));
+    if (!EPL)
+      return EPL.takeError();
+    EPLs.insert(*EPL);
   }
   return std::make_unique<UnsafeBufferUsageEntitySummary>(
       buildUnsafeBufferUsageEntitySummary(std::move(EPLs)));
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
index aeab9fc8867a3..b342f9299e71b 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
@@ -10,7 +10,7 @@
 #include "clang/AST/ASTConsumer.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
-#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
 #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test
index 6a12949f3bbb4..55357acb1db08 100644
--- a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test
+++ b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test
@@ -6,13 +6,21 @@
 
 // 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: expected a json::Object with a key UnsafeBuffers
-
+// CHECK-NO-KEY: saw {
+// CHECK-NO-KEY:   "NotUnsafeBuffers": [
+// CHECK-NO-KEY:     [
+// CHECK-NO-KEY:       {
+// CHECK-NO-KEY:         "@": 0
+// CHECK-NO-KEY:       },
+// CHECK-NO-KEY:       1
+// CHECK-NO-KEY:     ]
+// CHECK-NO-KEY:   ]
+// CHECK-NO-KEY: } but expected an Object with a key UnsafeBuffers
 
 // RUN: not clang-ssaf-format -type=tu %S/Inputs/tu-summary-bad-element.json 2>&1 \
 // RUN: | FileCheck %s --check-prefix=CHECK-BAD-ELEMENT
-// CHECK-BAD-ELEMENT: expected a json::Array of size 2
+// CHECK-BAD-ELEMENT: saw 42 but expected an array with exactly two elements representing EntityId and PointerLevel, respectively 
 
 // RUN: not clang-ssaf-format -type=tu %S/Inputs/tu-summary-bad-ptr-level.json 2>&1 \
 // RUN:   | FileCheck %s --check-prefix=CHECK-BAD-PTR-LEVEL
-// CHECK-BAD-PTR-LEVEL: expected a json::Value of integer type
+// CHECK-BAD-PTR-LEVEL: saw "not-an-integer" but expected an integer representing PointerLevel
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index f14f3caa1e016..d2c513b7c70a2 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -10,7 +10,7 @@
 #include "TestFixture.h"
 #include "clang/AST/DynamicRecursiveASTVisitor.h"
 #include "clang/Frontend/ASTUnit.h"
-#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
 #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h"
 #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"

>From 1cf2c6a6ef56421136af0b0e1c63b858de54f2fa Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Wed, 1 Apr 2026 23:23:41 -0700
Subject: [PATCH 30/30] clean up

---
 .../EntityPointerLevelFormat.h                |  44 +------
 .../EntityPointerLevel/EntityPointerLevel.cpp | 109 +++++++++++-------
 .../UnsafeBufferUsage/UnsafeBufferUsage.cpp   |   3 +-
 .../UnsafeBufferUsageExtractor.cpp            |   1 +
 4 files changed, 77 insertions(+), 80 deletions(-)

diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
index f8b30f8aab9d6..766e425338e96 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
@@ -22,46 +22,12 @@ static inline llvm::Error makeSawButExpectedError(const llvm::json::Value &Saw,
 }
 
 namespace clang::ssaf {
-// Writes an EntityPointerLevel as
-// Array [
-//   Object { "@" : [entity-id]},
-//   [pointer-level-integer]
-// ]
-static inline llvm::json::Value
+llvm::json::Value
 entityPointerLevelToJSON(const EntityPointerLevel &EPL,
-                         JSONFormat::EntityIdToJSONFn EntityId2JSON) {
-  return llvm::json::Array{EntityId2JSON(EPL.getEntity()),
-                           llvm::json::Value(EPL.getPointerLevel())};
-}
-
-Expected<EntityPointerLevel> static inline entityPointerLevelFromJSON(
-    const llvm::json::Value &EPLData,
-    JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) {
-  auto *AsArr = EPLData.getAsArray();
-
-  if (!AsArr || AsArr->size() != 2)
-    return makeSawButExpectedError(
-        EPLData, "an array with exactly two elements representing "
-                 "EntityId and PointerLevel, respectively");
-
-  auto *EntityIdObj = (*AsArr)[0].getAsObject();
-
-  if (!EntityIdObj)
-    return makeSawButExpectedError((*AsArr)[0],
-                                   "an object representing EntityId");
+                         JSONFormat::EntityIdToJSONFn EntityId2JSON);
 
-  Expected<EntityId> Id = EntityIdFromJSON(*EntityIdObj);
-
-  if (!Id)
-    return Id.takeError();
-
-  std::optional<uint64_t> PtrLv = (*AsArr)[1].getAsInteger();
-
-  if (!PtrLv)
-    return makeSawButExpectedError((*AsArr)[1],
-                                   "an integer representing PointerLevel");
-
-  return buildEntityPointerLevel(*Id, *PtrLv);
-}
+Expected<EntityPointerLevel>
+entityPointerLevelFromJSON(const llvm::json::Value &EPLData,
+                           JSONFormat::EntityIdFromJSONFn EntityIdFromJSON);
 } // namespace clang::ssaf
 #endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_ENTITYPOINTERLEVELFORMAT_H
\ No newline at end of file
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
index b5bf17530f9f2..e1346c2ff32c6 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
@@ -1,4 +1,4 @@
-//===----------------- EntityPointerLevel.cpp -------------------*- C++ -*-===//
+//===- EntityPointerLevel.cpp ----------------------------------*- C++ -*-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -6,18 +6,21 @@
 //
 //===----------------------------------------------------------------------===//
 #include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
+#include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
-#include "llvm/Support/JSON.h"
 #include <optional>
 
-namespace {
 using namespace clang;
+using namespace ssaf;
+
 template <typename NodeTy, typename... Ts>
-static inline llvm::Error strErrAtNode(ASTContext &Ctx, const NodeTy &N,
-                                       StringRef Fmt, const Ts &...Args) {
+static inline llvm::Error makeErrAtNode(ASTContext &Ctx, const NodeTy &N,
+                                        StringRef Fmt, const Ts &...Args) {
   std::string LocStr = N.getBeginLoc().printToString(Ctx.getSourceManager());
   llvm::SmallVector<char> FmtData;
 
@@ -25,10 +28,10 @@ static inline llvm::Error strErrAtNode(ASTContext &Ctx, const NodeTy &N,
   return llvm::createStringError(FmtData.data(), Args..., LocStr.c_str());
 }
 
-static inline llvm::Error entityNameErrFor(ASTContext &Ctx,
-                                           const NamedDecl &D) {
-  return strErrAtNode(Ctx, D, "failed to create entity name for %s",
-                      D.getNameAsString().data());
+static inline llvm::Error makeEntityNameErr(ASTContext &Ctx,
+                                            const NamedDecl &D) {
+  return makeErrAtNode(Ctx, D, "failed to create entity name for %s",
+                       D.getNameAsString().data());
 }
 
 template <typename DeclOrExpr>
@@ -37,18 +40,6 @@ static bool hasPtrOrArrType(const DeclOrExpr &E) {
          llvm::isa<ArrayType>(E.getType().getCanonicalType());
 }
 
-template <typename... Ts>
-static inline llvm::Error makeErrorSawButExpected(const llvm::json::Value &Saw,
-                                                  llvm::StringRef Expected,
-                                                  const Ts &...ExpectedArgs) {
-  return llvm::createStringError(
-      ("saw %s but expected " + Expected).str().c_str(),
-      llvm::formatv("{0:2}", Saw).str().data(), Expected.data(),
-      ExpectedArgs...);
-}
-} // namespace
-
-namespace clang::ssaf {
 // Translate a pointer 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
@@ -68,16 +59,16 @@ namespace clang::ssaf {
 //   Translate(arr[5])             -> {(arr, 2)}
 //   Translate(cond ? p1[5] : p2)  -> {(p1, 2), (p2, 1)}
 //   Translate(&arr[5])            -> {(arr, 1)}
-class EntityPointerLevelTranslator
+class clang::ssaf::EntityPointerLevelTranslator
     : public ConstStmtVisitor<EntityPointerLevelTranslator,
                               Expected<EntityPointerLevelSet>> {
   friend class StmtVisitorBase;
 
   // Fallback method for all unsupported expression kind:
   llvm::Error fallback(const Stmt *E) {
-    return strErrAtNode(Ctx, *E,
-                        "attempt to translate %s to EntityPointerLevels",
-                        E->getStmtClassName());
+    return makeErrAtNode(Ctx, *E,
+                         "attempt to translate %s to EntityPointerLevels",
+                         E->getStmtClassName());
   }
 
   EntityPointerLevel createEntityPointerLevelFor(const EntityName &Name) {
@@ -108,7 +99,7 @@ class EntityPointerLevelTranslator
   Expected<EntityPointerLevelSet> translate(const Expr *E) { return Visit(E); }
   Expected<EntityPointerLevel> translate(const NamedDecl *D, bool IsRet) {
     if (IsRet && !isa<FunctionDecl>(D))
-      return strErrAtNode(
+      return makeErrAtNode(
           Ctx, *D,
           "attempt to call getEntityNameForReturn on a NamedDecl of %s kind",
           D->getDeclKindName());
@@ -118,7 +109,7 @@ class EntityPointerLevelTranslator
               : getEntityName(D);
     if (EN)
       return createEntityPointerLevelFor(*EN);
-    return entityNameErrFor(Ctx, *D);
+    return makeEntityNameErr(Ctx, *D);
   }
 
   static EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E) {
@@ -241,14 +232,14 @@ class EntityPointerLevelTranslator
   Expected<EntityPointerLevelSet> VisitDeclRefExpr(const DeclRefExpr *E) {
     if (auto EntityName = getEntityName(E->getDecl()))
       return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
-    return entityNameErrFor(Ctx, *E->getDecl());
+    return makeEntityNameErr(Ctx, *E->getDecl());
   }
 
   // Translate({., ->}f) -> {(MemberDecl, 1)}
   Expected<EntityPointerLevelSet> VisitMemberExpr(const MemberExpr *E) {
     if (auto EntityName = getEntityName(E->getMemberDecl()))
       return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
-    return entityNameErrFor(Ctx, *E->getMemberDecl());
+    return makeEntityNameErr(Ctx, *E->getMemberDecl());
   }
 
   // Translate(`DefaultArg`) -> Translate(`DefaultArg->getExpr()`)
@@ -263,30 +254,70 @@ class EntityPointerLevelTranslator
   }
 };
 
-Expected<EntityPointerLevelSet>
-translateEntityPointerLevel(const Expr *E, ASTContext &Ctx,
-                            std::function<EntityId(EntityName EN)> AddEntity) {
+Expected<EntityPointerLevelSet> clang::ssaf::translateEntityPointerLevel(
+    const Expr *E, ASTContext &Ctx,
+    std::function<EntityId(EntityName EN)> AddEntity) {
   EntityPointerLevelTranslator Translator(AddEntity, Ctx);
 
   return Translator.translate(E);
 }
 
 /// Create an EntityPointerLevel from a ValueDecl of a pointer type.
-Expected<EntityPointerLevel>
-creatEntityPointerLevel(const NamedDecl *D, ASTContext &Ctx,
-                        std::function<EntityId(EntityName EN)> AddEntity,
-                        bool IsFunRet) {
+Expected<EntityPointerLevel> clang::ssaf::creatEntityPointerLevel(
+    const NamedDecl *D, ASTContext &Ctx,
+    std::function<EntityId(EntityName EN)> AddEntity, bool IsFunRet) {
   EntityPointerLevelTranslator Translator(AddEntity, Ctx);
 
   return Translator.translate(D, IsFunRet);
 }
 
-EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E) {
+EntityPointerLevel
+clang::ssaf::incrementPointerLevel(const EntityPointerLevel &E) {
   return EntityPointerLevelTranslator::incrementPointerLevel(E);
 }
 
-EntityPointerLevel buildEntityPointerLevel(EntityId Id, unsigned PtrLv) {
+EntityPointerLevel clang::ssaf::buildEntityPointerLevel(EntityId Id,
+                                                        unsigned PtrLv) {
   return EntityPointerLevel({Id, PtrLv});
 }
 
-} // namespace clang::ssaf
+// Writes an EntityPointerLevel as
+// Array [
+//   Object { "@" : [entity-id]},
+//   [pointer-level-integer]
+// ]
+llvm::json::Value clang::ssaf::entityPointerLevelToJSON(
+    const EntityPointerLevel &EPL, JSONFormat::EntityIdToJSONFn EntityId2JSON) {
+  return llvm::json::Array{EntityId2JSON(EPL.getEntity()),
+                           llvm::json::Value(EPL.getPointerLevel())};
+}
+
+Expected<EntityPointerLevel> clang::ssaf::entityPointerLevelFromJSON(
+    const llvm::json::Value &EPLData,
+    JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) {
+  auto *AsArr = EPLData.getAsArray();
+
+  if (!AsArr || AsArr->size() != 2)
+    return makeSawButExpectedError(
+        EPLData, "an array with exactly two elements representing "
+                 "EntityId and PointerLevel, respectively");
+
+  auto *EntityIdObj = (*AsArr)[0].getAsObject();
+
+  if (!EntityIdObj)
+    return makeSawButExpectedError((*AsArr)[0],
+                                   "an object representing EntityId");
+
+  Expected<EntityId> Id = EntityIdFromJSON(*EntityIdObj);
+
+  if (!Id)
+    return Id.takeError();
+
+  std::optional<uint64_t> PtrLv = (*AsArr)[1].getAsInteger();
+
+  if (!PtrLv)
+    return makeSawButExpectedError((*AsArr)[1],
+                                   "an integer representing PointerLevel");
+
+  return buildEntityPointerLevel(*Id, *PtrLv);
+}
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
index 84afb93239ffc..47e03476d1063 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
@@ -48,8 +48,7 @@ deserializeImpl(const Object &Data, JSONFormat::EntityIdFromJSONFn Fn) {
       Data.getArray(SummarySerializationKey.data());
 
   if (!UnsafeBuffersData)
-    return makeSawButExpectedError(Object(Data),
-                                   "an Object with a key %s",
+    return makeSawButExpectedError(Object(Data), "an Object with a key %s",
                                    SummarySerializationKey.data());
 
   EntityPointerLevelSet EPLs;
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
index b342f9299e71b..c412855028efb 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
@@ -9,6 +9,7 @@
 #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h"
 #include "clang/AST/ASTConsumer.h"
 #include "clang/AST/ASTContext.h"
+#include "clang/AST/DynamicRecursiveASTVisitor.h"
 #include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
 #include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
 #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"



More information about the llvm-branch-commits mailing list