[llvm-branch-commits] [clang] [SSAF][UnsafeBufferUsage] Implement AST visitor that respects the contribution model (PR #188652)

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


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

>From 50ab0797d809062257534c33918ebbbdeccc87a5 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Tue, 24 Mar 2026 17:31:01 -0700
Subject: [PATCH 01/10] [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     |  73 +---
 .../UnsafeBufferUsageExtractor.h              |   5 -
 .../Analyses/CMakeLists.txt                   |   3 +-
 .../Analyses/EntityPointerLevel.cpp           | 332 ++++++++++++++++++
 .../UnsafeBufferUsage/UnsafeBufferUsage.cpp   |  55 ++-
 .../UnsafeBufferUsageExtractor.cpp            | 225 +-----------
 .../UnsafeBufferUsageTest.cpp                 |   4 +-
 8 files changed, 506 insertions(+), 325 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..f2a2c73a29a7a
--- /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 2b36c47fe67a5..0b9a4170f653d 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
@@ -9,81 +9,14 @@
 #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/Serialization/JSONFormat.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.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 UnsafeBufferUsageTUSummaryExtractor;
-  friend class UnsafeBufferUsageEntitySummary;
-
-  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 c85fa044c1e9f..c8544d073d276 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
@@ -3,8 +3,9 @@ set(LLVM_LINK_COMPONENTS
   )
 
 add_clang_library(clangScalableStaticAnalysisFrameworkAnalyses
+  EntityPointerLevel.cpp
   UnsafeBufferUsage/UnsafeBufferUsage.cpp
-  UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp  
+  UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
 
   LINK_LIBS
   clangAST
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/UnsafeBufferUsage.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
index 44eaa20c74236..fd73647dc958c 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
@@ -7,10 +7,20 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h"
 #include "clang/ScalableStaticAnalysisFramework/SSAFForceLinker.h" // IWYU pragma: keep
 
 namespace {
 constexpr const char *const UnsafeBuffersKey = "UnsafeBuffers";
+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 {
@@ -18,24 +28,14 @@ using Object = llvm::json::Object;
 using Array = llvm::json::Array;
 using Value = llvm::json::Value;
 
+// Writes the summary into an array of EntityPointerLevels:
 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())}));
+    UnsafeBuffersData.push_back(entityPointerLevelToJSON(EPL, EntityId2JSON));
 
   Object Data;
 
@@ -47,31 +47,26 @@ 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";
+  const Value *UnsafeBuffersData = Data.get(UnsafeBuffersKey);
 
   if (!UnsafeBuffersData)
-    return llvm::createStringError(ErrMsg);
-
-  EntityPointerLevelSet UnsafeBuffers;
+    return makeErrorSawButExpected(
+        Object(Data), "a JSON object with the key: %s", UnsafeBuffersKey);
 
-  for (auto &EltData : *UnsafeBuffersData) {
-    const Array *EltDataAsArr = EltData.getAsArray();
+  const auto *AsArr = UnsafeBuffersData->getAsArray();
 
-    if (!EltDataAsArr || EltDataAsArr->size() != 2)
-      return llvm::createStringError(ErrMsg);
+  if (!AsArr)
+    return makeErrorSawButExpected(*UnsafeBuffersData,
+                                   "a JSON array of EntityPointerLevels");
 
-    const Object *IdData = (*EltDataAsArr)[0].getAsObject();
-    std::optional<uint64_t> PtrLvData = (*EltDataAsArr)[1].getAsInteger();
-
-    if (!IdData || !PtrLvData)
-      return llvm::createStringError(ErrMsg);
+  EntityPointerLevelSet UnsafeBuffers;
 
-    llvm::Expected<EntityId> Id = EntityIdFromJSON(*IdData);
+  for (auto &UnsafeBufferData : *AsArr) {
+    auto EPL = entityPointerLevelFromJSON(UnsafeBufferData, EntityIdFromJSON);
 
-    if (!Id)
-      return Id.takeError();
-    UnsafeBuffers.insert(EntityPointerLevel(Id.get(), *PtrLvData));
+    if (!EPL)
+      return EPL.takeError();
+    UnsafeBuffers.insert(*EPL);
   }
   return std::make_unique<UnsafeBufferUsageEntitySummary>(
       UnsafeBufferUsageEntitySummary(std::move(UnsafeBuffers)));
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 8b5b76b6e58b6..9efdec64ea30c 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -9,6 +9,7 @@
 #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.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/Core/ASTEntityMapping.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
@@ -57,9 +58,6 @@ const FunctionDecl *findFnByName(StringRef Name, ASTContext &Ctx) {
   return findDeclByName<FunctionDecl>(Name, Ctx);
 }
 
-constexpr inline auto buildEntityPointerLevel =
-    UnsafeBufferUsageTUSummaryExtractor::buildEntityPointerLevel;
-
 class UnsafeBufferUsageTest : public testing::Test {
 protected:
   TUSummary TUSum;

>From 5e830286290b4cd1b993e2257efa1091b6ade837 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Tue, 24 Mar 2026 17:59:49 -0700
Subject: [PATCH 02/10] [SSAF][UnsafeBufferUsage] Implement AST visitor that
 respects the constribution model and refactor

Previously, the UnsafeBufferUsage Extractor relied on the
`-Wunsafe-buffer-usage` API to traverse ASTs. The traversal did not
fully respect the contribution model of SSAF---RecordDecls inside
functions were not treated as contributors. Their fields were counted
as contributions of the enclosing function.

This commit adds an AST visitor that respects the contribution model
and will be shared by SSAF analyses.  The UnsafeBufferUsage Extractor
still relies on `-Wunsafe-buffer-usage` to provide the unsafe pointer
matching function.

In addition, this commit
- Factors common code in analyses to 'lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h'.
- Registers the UnsafeBufferUsage extractor.
- Removes 'UnsafeBufferUsageExtractor.h' since it is useless except
  for the unit test. The unit test now directly uses proxy functions
  defined in 'UnsafeBufferUsageExtractor.cpp'.
---
 .../Analysis/Analyses/UnsafeBufferUsage.h     |  12 +-
 .../UnsafeBufferUsage.h                       |  12 +-
 .../UnsafeBufferUsageExtractor.h              |  35 ---
 .../SSAFBuiltinForceLinker.h                  |   5 +
 clang/lib/Analysis/UnsafeBufferUsage.cpp      | 117 ++++-----
 .../Analyses/EntityPointerLevel.cpp           |  37 +--
 .../Analyses/SSAFAnalysesCommon.h             | 146 +++++++++++
 .../UnsafeBufferUsage/UnsafeBufferUsage.cpp   |  19 +-
 .../UnsafeBufferUsageExtractor.cpp            | 235 ++++++++----------
 .../UnsafeBufferUsageTest.cpp                 |  65 +++--
 .../CMakeLists.txt                            |   2 +-
 .../SummaryExtractorRegistryTest.cpp          |   1 +
 12 files changed, 384 insertions(+), 302 deletions(-)
 rename clang/include/clang/ScalableStaticAnalysisFramework/Analyses/{UnsafeBufferUsage => }/UnsafeBufferUsage.h (85%)
 delete mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h
 create mode 100644 clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
 rename clang/unittests/ScalableStaticAnalysisFramework/Analyses/{UnsafeBufferUsage => }/UnsafeBufferUsageTest.cpp (91%)

diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
index e0d583c735e61..65b79cfc1c2ff 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
@@ -201,14 +201,10 @@ bool anyConflict(const llvm::SmallVectorImpl<FixItHint> &FixIts,
                  const SourceManager &SM);
 } // namespace internal
 
-/// 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);
+/// \return true iff `N` is an unsafe buffer usage and populates the unsafe
+/// pointers in `UnsafePointers`
+bool matchUnsafePointers(const DynTypedNode &N, ASTContext &Ctx,
+                         std::set<const Expr *> &UnsafePointers);
 } // 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.h
similarity index 85%
rename from clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
rename to clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage.h
index 0b9a4170f653d..16180db20787f 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage.h
@@ -39,12 +39,12 @@ class UnsafeBufferUsageEntitySummary final : public EntitySummary {
   bool empty() const { return UnsafeBuffers.empty(); }
 
   static llvm::json::Object
-  jsonSerializeFn(const EntitySummary &ES,
-                  JSONFormat::EntityIdToJSONFn EntityId2JSON);
+  summaryToJSON(const EntitySummary &ES,
+                JSONFormat::EntityIdToJSONFn EntityId2JSON);
 
   static llvm::Expected<std::unique_ptr<EntitySummary>>
-  jsonDeserializeFn(const llvm::json::Object &Data, EntityIdTable &,
-                    JSONFormat::EntityIdFromJSONFn EntityIdFromJSON);
+  summaryFromJSON(const llvm::json::Object &Data, EntityIdTable &,
+                  JSONFormat::EntityIdFromJSONFn EntityIdFromJSON);
 
   static SummaryName summaryName() { return SummaryName{"UnsafeBufferUsage"}; }
 };
@@ -53,8 +53,8 @@ struct UnsafeBufferUsageJSONFormatInfo : JSONFormat::FormatInfo {
   UnsafeBufferUsageJSONFormatInfo()
       : JSONFormat::FormatInfo(
             UnsafeBufferUsageEntitySummary::summaryName(),
-            UnsafeBufferUsageEntitySummary::jsonSerializeFn,
-            UnsafeBufferUsageEntitySummary::jsonDeserializeFn) {}
+            UnsafeBufferUsageEntitySummary::summaryToJSON,
+            UnsafeBufferUsageEntitySummary::summaryFromJSON) {}
 };
 } // namespace clang::ssaf
 
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h
deleted file mode 100644
index 13d4e18b4e81f..0000000000000
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h
+++ /dev/null
@@ -1,35 +0,0 @@
-//===- 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) {}
-
-  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/SSAFBuiltinForceLinker.h b/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
index e0a394da1a921..670b80e49eadc 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
@@ -29,4 +29,9 @@ extern volatile int UnsafeBufferUsageSSAFJSONFormatAnchorSource;
 [[maybe_unused]] static int UnsafeBufferUsageSSAFJSONFormatAnchorDestination =
     UnsafeBufferUsageSSAFJSONFormatAnchorSource;
 
+extern volatile int UnsafeBufferUsageTUSummaryExtractorAnchorSource;
+[[maybe_unused]] static int
+    UnsafeBufferUsageTUSummaryExtractorAnchorDestination =
+        UnsafeBufferUsageSSAFJSONFormatAnchorSource;
+
 #endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_SSAFBUILTINFORCELINKER_H
diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 5a9241acbee36..5369774db1ba5 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -2974,63 +2974,6 @@ static void populateStmtsForFindingGadgets(SmallVector<const Stmt *> &Stmts,
   }
 }
 
-std::set<const Expr *> clang::findUnsafePointers(const Decl *D) {
-  class MockReporter : public UnsafeBufferUsageHandler {
-  public:
-    MockReporter() {}
-    void handleUnsafeOperation(const Stmt *, bool, ASTContext &) override {}
-    void handleUnsafeLibcCall(const CallExpr *, unsigned, ASTContext &,
-                              const Expr *UnsafeArg = nullptr) override {}
-    void handleUnsafeOperationInContainer(const Stmt *, bool,
-                                          ASTContext &) override {}
-    void handleUnsafeVariableGroup(const VarDecl *,
-                                   const VariableGroupsManager &, FixItList &&,
-                                   const Decl *,
-                                   const FixitStrategy &) override {}
-    void handleUnsafeUniquePtrArrayAccess(const DynTypedNode &Node,
-                                          bool IsRelatedToDecl,
-                                          ASTContext &Ctx) override {}
-    bool ignoreUnsafeBufferInContainer(const SourceLocation &) const override {
-      return false;
-    }
-    bool isSafeBufferOptOut(const SourceLocation &) const override {
-      return false;
-    }
-    bool ignoreUnsafeBufferInLibcCall(const SourceLocation &) const override {
-      return false;
-    }
-    bool ignoreUnsafeBufferInStaticSizedArray(
-        const SourceLocation &Loc) const override {
-      return false;
-    }
-    std::string getUnsafeBufferUsageAttributeTextAt(
-        SourceLocation, StringRef WSSuffix = "") const override {
-      return "";
-    }
-  };
-
-  FixableGadgetList FixableGadgets;
-  WarningGadgetList WarningGadgets;
-  DeclUseTracker Tracker;
-  MockReporter IgnoreHandler;
-  ASTContext &Ctx = D->getASTContext();
-  SmallVector<const Stmt *> Stmts;
-
-  populateStmtsForFindingGadgets(Stmts, D);
-  for (auto *Stmt : Stmts)
-    findGadgets(Stmt, Ctx, IgnoreHandler, false, FixableGadgets, WarningGadgets,
-                Tracker);
-
-  std::set<const Expr *> Result;
-  for (auto &G : WarningGadgets) {
-    for (const Expr *E : G->getUnsafePtrs()) {
-      Result.insert(E);
-    }
-  }
-
-  return Result;
-}
-
 struct WarningGadgetSets {
   std::map<const VarDecl *, std::set<const WarningGadget *>,
            // To keep keys sorted by their locations in the map so that the
@@ -4748,3 +4691,63 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
   applyGadgets(D, std::move(FixableGadgets), std::move(WarningGadgets),
                std::move(Tracker), Handler, EmitSuggestions);
 }
+
+bool clang::matchUnsafePointers(const DynTypedNode &N, ASTContext &Ctx,
+                                std::set<const Expr *> &UnsafePointers) {
+  class MockReporter : public UnsafeBufferUsageHandler {
+  public:
+    MockReporter() {}
+    void handleUnsafeOperation(const Stmt *, bool, ASTContext &) override {}
+    void handleUnsafeLibcCall(const CallExpr *, unsigned, ASTContext &,
+                              const Expr *UnsafeArg = nullptr) override {}
+    void handleUnsafeOperationInContainer(const Stmt *, bool,
+                                          ASTContext &) override {}
+    void handleUnsafeVariableGroup(const VarDecl *,
+                                   const VariableGroupsManager &, FixItList &&,
+                                   const Decl *,
+                                   const FixitStrategy &) override {}
+    void handleUnsafeUniquePtrArrayAccess(const DynTypedNode &Node,
+                                          bool IsRelatedToDecl,
+                                          ASTContext &Ctx) override {}
+    bool ignoreUnsafeBufferInContainer(const SourceLocation &) const override {
+      return false;
+    }
+    bool isSafeBufferOptOut(const SourceLocation &) const override {
+      return false;
+    }
+    bool ignoreUnsafeBufferInLibcCall(const SourceLocation &) const override {
+      return false;
+    }
+    bool ignoreUnsafeBufferInStaticSizedArray(
+        const SourceLocation &Loc) const override {
+      return false;
+    }
+    std::string getUnsafeBufferUsageAttributeTextAt(
+        SourceLocation, StringRef WSSuffix = "") const override {
+      return "";
+    }
+  } Handler;
+
+  const Stmt *S = N.get<Stmt>();
+  if (!S)
+    return false;
+
+  MatchResult Result;
+  WarningGadgetList WarningGadgets;
+  bool Matched = false;
+
+#define WARNING_GADGET(name)                                                   \
+  if (name##Gadget::matches(S, Ctx, Result))                                   \
+    WarningGadgets.push_back(std::make_unique<name##Gadget>(Result));
+#define WARNING_OPTIONAL_GADGET(name)                                          \
+  if (name##Gadget::matches(S, Ctx, &Handler, Result))                         \
+    WarningGadgets.push_back(std::make_unique<name##Gadget>(Result));
+#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
+
+  for (auto &WG : WarningGadgets)
+    for (auto *E : WG->getUnsafePtrs()) {
+      UnsafePointers.insert(E);
+      Matched = true;
+    }
+  return Matched;
+}
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp
index e607529342351..ddbc45301ac4b 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp
@@ -5,48 +5,15 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
+
 #include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h"
+#include "SSAFAnalysesCommon.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
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
new file mode 100644
index 0000000000000..61e104bc8a0eb
--- /dev/null
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
@@ -0,0 +1,146 @@
+//===------------------ SSAFAnalysesCommon.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
+//
+//===----------------------------------------------------------------------===//
+//
+//  Common code in SSAF analyses implementations
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_SSAFANALYSESCOMMON_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_SSAFANALYSESCOMMON_H
+
+#include "clang/AST/Decl.h"
+#include "clang/AST/ParentMapContext.h"
+#include "clang/Basic/SourceLocation.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/JSON.h"
+
+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());
+}
+
+static inline llvm::Error failedToAddEntitySummaryFor(ASTContext &Ctx,
+                                                      const NamedDecl *D) {
+  std::string LocStr = D->getBeginLoc().printToString(Ctx.getSourceManager());
+
+  return llvm::createStringError("failed to add entity summary for %s at %s",
+                                 D->getNameAsString().data(), LocStr.c_str());
+}
+
+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...);
+}
+
+template <typename DeclOrExpr>
+static bool hasPtrOrArrType(const DeclOrExpr &E) {
+  return llvm::isa<PointerType>(E.getType().getCanonicalType()) ||
+         llvm::isa<ArrayType>(E.getType().getCanonicalType());
+}
+
+/// Traverses the AST and finds contributors:
+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;
+  }
+};
+
+/// An AST visitor that skips callable decl and record decl strict-descendant
+/// because those are separate contributors.
+///
+/// The visitor calls
+/// `MatcherTy::matchFact(DynTypedNode &, ASTContext &, const NamedDecl
+/// *Contributor)` on each visited Decl or Stmt node to collect facts in the
+/// `Contributor`.
+template <typename MatcherTy>
+class ContributorFactFinder : public DynamicRecursiveASTVisitor {
+  ASTContext &Ctx;
+  MatcherTy &Matcher;
+  const NamedDecl *RootDecl;
+
+  template <typename T> bool match(const T &Node) {
+    return Matcher.matches(DynTypedNode::create(Node), Ctx, RootDecl);
+  }
+
+public:
+  ContributorFactFinder(ASTContext &Ctx, MatcherTy &Matcher)
+      : Ctx(Ctx), Matcher(Matcher) {
+    ShouldVisitTemplateInstantiations = true;
+    ShouldVisitImplicitCode = false;
+  }
+
+  /// The entry point:
+  void findMatches(const NamedDecl *Contributor) {
+    RootDecl = Contributor;
+    TraverseDecl(const_cast<NamedDecl *>(Contributor));
+  }
+
+  bool TraverseDecl(Decl *Node) override {
+    if (!Node)
+      return true;
+    // To skip callables:
+    if (Node != RootDecl && isa<FunctionDecl, CXXConstructorDecl, BlockDecl,
+                                ObjCMethodDecl, RecordDecl>(Node))
+      return true;
+    match(*Node);
+    return DynamicRecursiveASTVisitor::TraverseDecl(Node);
+  }
+
+  bool TraverseStmt(Stmt *Node) override {
+    if (!Node)
+      return true;
+    match(*Node);
+    return DynamicRecursiveASTVisitor::TraverseStmt(Node);
+  }
+
+  bool TraverseLambdaExpr(LambdaExpr *L) override {
+    // TODO: lambda captures of pointer variables (by copy or by reference)
+    // are currently not tracked. Each capture initializes an implicit closure
+    // field from the captured variable, which constitutes a pointer assignment
+    // edge that should be recorded here.
+    return true; // skip lambda as it is a callable
+  }
+};
+} // namespace
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_SSAFANALYSESCOMMON_H
\ 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 fd73647dc958c..37e00b61ae2f4 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
@@ -6,21 +6,14 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage.h"
+#include "SSAFAnalysesCommon.h"
 #include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h"
 #include "clang/ScalableStaticAnalysisFramework/SSAFForceLinker.h" // IWYU pragma: keep
+#include "llvm/Support/JSON.h"
 
 namespace {
 constexpr const char *const UnsafeBuffersKey = "UnsafeBuffers";
-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 {
@@ -29,7 +22,7 @@ using Array = llvm::json::Array;
 using Value = llvm::json::Value;
 
 // Writes the summary into an array of EntityPointerLevels:
-llvm::json::Object UnsafeBufferUsageEntitySummary::jsonSerializeFn(
+llvm::json::Object UnsafeBufferUsageEntitySummary::summaryToJSON(
     const EntitySummary &ES, JSONFormat::EntityIdToJSONFn EntityId2JSON) {
   Array UnsafeBuffersData;
 
@@ -44,8 +37,8 @@ llvm::json::Object UnsafeBufferUsageEntitySummary::jsonSerializeFn(
 }
 
 llvm::Expected<std::unique_ptr<EntitySummary>>
-UnsafeBufferUsageEntitySummary::jsonDeserializeFn(
-    const llvm::json::Object &Data, EntityIdTable &,
+UnsafeBufferUsageEntitySummary::summaryFromJSON(
+    const Object &Data, EntityIdTable &,
     JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) {
   const Value *UnsafeBuffersData = Data.get(UnsafeBuffersKey);
 
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
index aeab9fc8867a3..c023e737bac9c 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
@@ -6,157 +6,142 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h"
+#include "SSAFAnalysesCommon.h"
 #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/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/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/Support/Error.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.h"
+#include "clang/ScalableStaticAnalysisFramework/SSAFForceLinker.h" // IWYU pragma: keep
 #include <memory>
 
 namespace {
 using namespace clang;
-using namespace ssaf;
-
-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());
-}
+struct UnsafePointerMatcher {
+  std::set<const Expr *> UnsafePointers;
 
-Expected<EntityPointerLevelSet>
-buildEntityPointerLevels(std::set<const Expr *> &&UnsafePointers,
-                         UnsafeBufferUsageTUSummaryExtractor &Extractor,
-                         ASTContext &Ctx,
-                         std::function<EntityId(EntityName)> AddEntity) {
-  EntityPointerLevelSet Result{};
-  llvm::Error AllErrors = llvm::ErrorSuccess();
-
-  for (const Expr *Ptr : UnsafePointers) {
-    Expected<EntityPointerLevelSet> Translation =
-        translateEntityPointerLevel(Ptr, Ctx, AddEntity);
-
-    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());
+  bool matches(const DynTypedNode &N, ASTContext &Ctx,
+               const NamedDecl *Contributor) {
+    return matchUnsafePointers(N, Ctx, UnsafePointers);
   }
-  if (AllErrors)
-    return AllErrors;
-  return Result;
-}
-} // 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;
+void findFactsInContributor(const NamedDecl *Contributor, ASTContext &Ctx,
+                            std::set<const Expr *> &UnsafePointers) {
+  UnsafePointerMatcher Matcher;
+  ContributorFactFinder<UnsafePointerMatcher> Finder(Ctx, Matcher);
 
-    for (const FieldDecl *FD : RD->fields()) {
-      Result.merge(findUnsafePointers(FD));
-    }
-    return Result;
-  }
-  return {};
+  Finder.findMatches(Contributor);
+  UnsafePointers.merge(Matcher.UnsafePointers);
 }
+} // namespace
 
-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, AddEntity);
+namespace clang::ssaf {
 
-  if (EPLs)
-    return std::make_unique<UnsafeBufferUsageEntitySummary>(
-        UnsafeBufferUsageEntitySummary(std::move(*EPLs)));
-  Error = EPLs.takeError();
-  return nullptr;
-}
+class UnsafeBufferUsageTUSummaryExtractor : public TUSummaryExtractor {
+public:
+  UnsafeBufferUsageTUSummaryExtractor(TUSummaryBuilder &Builder)
+      : TUSummaryExtractor(Builder) {}
 
-void UnsafeBufferUsageTUSummaryExtractor::HandleTranslationUnit(
-    ASTContext &Ctx) {
+  EntityId addEntity(EntityName EN) { return SummaryBuilder.addEntity(EN); }
 
-  // FIXME: I suppose finding contributor Decls is commonly needed by all/many
-  // extractors
-  class ContributorFinder : public DynamicRecursiveASTVisitor {
-  public:
-    std::vector<const NamedDecl *> Contributors;
+  Expected<std::unique_ptr<UnsafeBufferUsageEntitySummary>>
+  extractEntitySummary(const NamedDecl *Contributor, ASTContext &Ctx) {
+    std::set<const Expr *> UnsafePointers;
+    EntityPointerLevelSet Results;
 
-    bool VisitFunctionDecl(FunctionDecl *D) override {
-      Contributors.push_back(D);
-      return true;
-    }
+    findFactsInContributor(Contributor, Ctx, UnsafePointers);
+    for (const Expr *Ptr : UnsafePointers) {
+      Expected<EntityPointerLevelSet> Translation =
+          translateEntityPointerLevel(Ptr, Ctx, [this](const EntityName &EN) {
+            return SummaryBuilder.addEntity(EN);
+          });
 
-    bool VisitRecordDecl(RecordDecl *D) override {
-      Contributors.push_back(D);
-      return true;
+      if (Translation) {
+        // Filter out those temporary invalid EntityPointerLevels associated
+        // with
+        // `&E` pointers. They need no transformation of entities:
+        auto FilteredTranslation = llvm::make_filter_range(
+            *Translation, [](const EntityPointerLevel &E) -> bool {
+              return E.getPointerLevel() > 0;
+            });
+        Results.insert(FilteredTranslation.begin(), FilteredTranslation.end());
+        continue;
+      }
+      return Translation.takeError();
     }
+    return std::make_unique<UnsafeBufferUsageEntitySummary>(
+        UnsafeBufferUsageEntitySummary(std::move(Results)));
+  }
 
-    bool VisitVarDecl(VarDecl *D) override {
-      DeclContext *DC = D->getDeclContext();
-
-      if (DC->isFileContext() || DC->isNamespace())
-        Contributors.push_back(D);
-      return false;
+  void HandleTranslationUnit(ASTContext &Ctx) override {
+    llvm::Error Errors = llvm::ErrorSuccess();
+    auto addError = [&Errors](llvm::Error Err) {
+      Errors = llvm::joinErrors(std::move(Errors), std::move(Err));
+    };
+    ContributorFinder ContributorFinder;
+
+    ContributorFinder.VisitTranslationUnitDecl(Ctx.getTranslationUnitDecl());
+    for (auto *CD : ContributorFinder.Contributors) {
+      auto EntitySummary = extractEntitySummary(CD, Ctx);
+
+      if (!EntitySummary) {
+        addError(EntitySummary.takeError());
+        continue;
+      }
+      assert(*EntitySummary &&
+             "std::unique_ptr<EntitySummary> should not be null");
+      if ((*EntitySummary)->empty())
+        continue;
+
+      auto ContributorName = getEntityName(CD);
+
+      if (!ContributorName) {
+        addError(entityNameErrFor(Ctx, *CD));
+        continue;
+      }
+
+      auto [EntitySummaryPtr, Success] = SummaryBuilder.addSummary(
+          addEntity(*ContributorName), std::move(*EntitySummary));
+
+      if (!Success)
+        addError(failedToAddEntitySummaryFor(Ctx, CD));
     }
-  } 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;
+    // FIXME: handle errors!
+    llvm::consumeError(std::move(Errors));
+  }
+};
 
-    auto ContributorName = getEntityName(CD);
+// Proxy functions for unit tests:
+extern Expected<std::unique_ptr<UnsafeBufferUsageEntitySummary>>
+extractEntitySummary(UnsafeBufferUsageTUSummaryExtractor &Extractor,
+                     const NamedDecl *Contributor, ASTContext &Ctx) {
+  return Extractor.extractEntitySummary(Contributor, Ctx);
+}
 
-    if (!ContributorName) {
-      addError(makeCreateEntityNameError(CD, Ctx));
-      continue;
-    }
+extern UnsafeBufferUsageTUSummaryExtractor *
+createUnsafeBufferUsageTUSummaryExtractor(TUSummaryBuilder &Builder) {
+  return new UnsafeBufferUsageTUSummaryExtractor(Builder);
+}
 
-    auto [EntitySummaryPtr, Success] = SummaryBuilder.addSummary(
-        addEntity(*ContributorName), std::move(EntitySummary));
+extern void destroyUnsafeBufferUsageTUSummaryExtractor(
+    UnsafeBufferUsageTUSummaryExtractor *Extractor) {
+  delete Extractor;
+}
 
-    if (!Success)
-      addError(makeAddEntitySummaryError(CD, Ctx));
-  }
-  // FIXME: handle errors!
-  llvm::consumeError(std::move(Errors));
+extern EntityId addEntity(UnsafeBufferUsageTUSummaryExtractor &Extractor,
+                          const EntityName &EN) {
+  return Extractor.addEntity(EN);
 }
+} // namespace clang::ssaf
+
+volatile int UnsafeBufferUsageTUSummaryExtractorAnchorSource = 0;
+static clang::ssaf::TUSummaryExtractorRegistry::Add<
+    ssaf::UnsafeBufferUsageTUSummaryExtractor>
+    RegisterExtractor("UnsafeBufferUsageTUSummaryExtractor",
+                      "The TUSummaryExtractor for unsafe buffer pointers");
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsageTest.cpp
similarity index 91%
rename from clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
rename to clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsageTest.cpp
index 9efdec64ea30c..57e9135f2e730 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsageTest.cpp
@@ -6,11 +6,9 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage.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/Core/ASTEntityMapping.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
@@ -21,13 +19,31 @@
 #include "llvm/Testing/Support/Error.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include <memory>
 
 using namespace clang;
 using namespace ssaf;
 using testing::UnorderedElementsAre;
 
-namespace {
+namespace clang::ssaf {
+// Proxy functions
+class UnsafeBufferUsageTUSummaryExtractor;
+
+extern Expected<std::unique_ptr<UnsafeBufferUsageEntitySummary>>
+extractEntitySummary(UnsafeBufferUsageTUSummaryExtractor &Extractor,
+                     const NamedDecl *Contributor, ASTContext &Ctx);
+
+extern UnsafeBufferUsageTUSummaryExtractor *
+createUnsafeBufferUsageTUSummaryExtractor(TUSummaryBuilder &Builder);
+
+extern void destroyUnsafeBufferUsageTUSummaryExtractor(
+    UnsafeBufferUsageTUSummaryExtractor *Extractor);
 
+extern EntityId addEntity(UnsafeBufferUsageTUSummaryExtractor &Extractor,
+                          const EntityName &EN);
+} // namespace clang::ssaf
+
+namespace {
 template <typename SomeDecl = NamedDecl>
 const SomeDecl *findDeclByName(StringRef Name, ASTContext &Ctx) {
   class NamedDeclFinder : public DynamicRecursiveASTVisitor {
@@ -62,12 +78,17 @@ class UnsafeBufferUsageTest : public testing::Test {
 protected:
   TUSummary TUSum;
   TUSummaryBuilder Builder;
-  UnsafeBufferUsageTUSummaryExtractor Extractor;
+  UnsafeBufferUsageTUSummaryExtractor *Extractor;
   std::unique_ptr<ASTUnit> AST;
 
   UnsafeBufferUsageTest()
       : TUSum(BuildNamespace(BuildNamespaceKind::CompilationUnit, "Mock.cpp")),
-        Builder(TUSum), Extractor(Builder) {}
+        Builder(TUSum),
+        Extractor(createUnsafeBufferUsageTUSummaryExtractor(Builder)) {}
+
+  ~UnsafeBufferUsageTest() {
+    destroyUnsafeBufferUsageTUSummaryExtractor(Extractor);
+  }
 
   template <typename ContributorDecl = NamedDecl>
   std::unique_ptr<UnsafeBufferUsageEntitySummary>
@@ -75,7 +96,7 @@ class UnsafeBufferUsageTest : public testing::Test {
     AST = tooling::buildASTFromCodeWithArgs(
         Code, {"-Wno-unused-value", "-Wno-int-to-pointer-cast"});
 
-    const auto *ContributorDefn =
+    auto *ContributorDefn =
         findDeclByName<ContributorDecl>(ContributorName, AST->getASTContext());
 
     if (!ContributorDefn)
@@ -86,28 +107,28 @@ class UnsafeBufferUsageTest : public testing::Test {
     if (!EN)
       return nullptr;
 
-    llvm::Error Error = llvm::ErrorSuccess();
-    auto Sum = Extractor.extractEntitySummary(ContributorDefn,
-                                              AST->getASTContext(), Error);
+    auto Sum =
+        extractEntitySummary(*Extractor, ContributorDefn, AST->getASTContext());
 
-    if (Error) {
-      llvm::consumeError(std::move(Error));
+    if (!Sum) {
+      llvm::consumeError(Sum.takeError());
       return nullptr;
     }
-    return Sum;
+    assert(*Sum);
+    return std::move(*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 addEntity(*Extractor, *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 addEntity(*Extractor, *EntityName);
     return std::nullopt;
   }
 
@@ -147,8 +168,8 @@ getSubsetOf(const EntityPointerLevelSet &Set, EntityId Entity) {
 }
 
 TEST_F(UnsafeBufferUsageTest, EntityPointerLevelComparison) {
-  EntityId E1 = Extractor.addEntity({"c:@F at foo", "", {}});
-  EntityId E2 = Extractor.addEntity({"c:@F at bar", "", {}});
+  EntityId E1 = addEntity(*Extractor, {"c:@F at foo", "", {}});
+  EntityId E2 = addEntity(*Extractor, {"c:@F at bar", "", {}});
 
   auto P1 = buildEntityPointerLevel(E1, 2);
   auto P2 = buildEntityPointerLevel(E1, 2);
@@ -166,9 +187,9 @@ TEST_F(UnsafeBufferUsageTest, EntityPointerLevelComparison) {
 }
 
 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", "", {}});
+  EntityId E1 = addEntity(*Extractor, {"c:@F at foo", "", {}});
+  EntityId E2 = addEntity(*Extractor, {"c:@F at bar", "", {}});
+  EntityId E3 = addEntity(*Extractor, {"c:@F at baz", "", {}});
 
   auto P1 = buildEntityPointerLevel(E1, 1);
   auto P2 = buildEntityPointerLevel(E1, 2);
@@ -260,7 +281,7 @@ TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageSerializeTest) {
   using Value = llvm::json::Value;
   std::map<EntityId, uint64_t> DummyTable{{*getEntityId("p"), 42},
                                           {*getEntityId("q"), 108}};
-  Object JData = UnsafeBufferUsageEntitySummary::jsonSerializeFn(
+  Object JData = UnsafeBufferUsageEntitySummary::summaryToJSON(
       *Sum, [&DummyTable](EntityId Id) {
         return Object{{"@", Value(DummyTable[Id])}};
       });
@@ -296,7 +317,7 @@ TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageDeserializeTest) {
   ASSERT_NE(ParsedJSON->getAsObject(), nullptr);
 
   EntityIdTable Ignored;
-  auto ParsedSum = UnsafeBufferUsageEntitySummary::jsonDeserializeFn(
+  auto ParsedSum = UnsafeBufferUsageEntitySummary::summaryFromJSON(
       *ParsedJSON->getAsObject(), Ignored,
       [&DummyTable](const Object &O) -> Expected<EntityId> {
         return DummyTable.at(O.getInteger("@").value());
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt b/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
index f541e81d42789..5da36d071be32 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
+++ b/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
@@ -1,5 +1,5 @@
 add_distinct_clang_unittest(ClangScalableAnalysisTests
-  Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+  Analyses/UnsafeBufferUsageTest.cpp
   ASTEntityMappingTest.cpp
   BuildNamespaceTest.cpp
   EntityIdTableTest.cpp
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Registries/SummaryExtractorRegistryTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Registries/SummaryExtractorRegistryTest.cpp
index 2018beebd53da..28f4d089b5764 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Registries/SummaryExtractorRegistryTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Registries/SummaryExtractorRegistryTest.cpp
@@ -42,6 +42,7 @@ TEST(SummaryExtractorRegistryTest, EnumeratingRegistryEntries) {
                              "MockSummaryExtractor1",
                              "MockSummaryExtractor2",
                              "NoOpExtractor",
+                             "UnsafeBufferUsageTUSummaryExtractor",
                          }));
 }
 

>From 4c681abfe034e3bea003fb510b97f31dbf85e2b2 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Thu, 2 Apr 2026 16:14:44 -0700
Subject: [PATCH 03/10] fix issues after rebase

---
 .../Analyses/EntityPointerLevel.cpp           | 299 ------------------
 1 file changed, 299 deletions(-)
 delete mode 100644 clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp

diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp
deleted file mode 100644
index ddbc45301ac4b..0000000000000
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp
+++ /dev/null
@@ -1,299 +0,0 @@
-//===----------------- 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 "SSAFAnalysesCommon.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 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

>From 036f9b1ce819e9a1e4d490562dc6e102d2a428e1 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Sat, 4 Apr 2026 23:39:25 -0700
Subject: [PATCH 04/10] remove proxy functions for unit-testing
 UnsafeBufferUsageExtractor

---
 .../EntityPointerLevelFormat.h                |  10 -
 .../EntityPointerLevel/EntityPointerLevel.cpp |  23 +-
 .../Analyses/SSAFAnalysesCommon.h             |  36 +-
 .../UnsafeBufferUsage/UnsafeBufferUsage.cpp   |   6 +-
 .../UnsafeBufferUsageExtractor.cpp            | 107 +-----
 .../UnsafeBufferUsageTest.cpp                 | 337 ++++++++++--------
 6 files changed, 223 insertions(+), 296 deletions(-)

diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
index 766e425338e96..a3914460c86ef 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
@@ -11,16 +11,6 @@
 #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 {
 llvm::json::Value
 entityPointerLevelToJSON(const EntityPointerLevel &EPL,
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
index e1346c2ff32c6..9a57a843b23eb 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 #include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
+#include "SSAFAnalysesCommon.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
@@ -18,28 +19,6 @@
 using namespace clang;
 using namespace ssaf;
 
-template <typename NodeTy, typename... Ts>
-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;
-
-  (Fmt + " at %s").toStringRef(FmtData);
-  return llvm::createStringError(FmtData.data(), Args..., LocStr.c_str());
-}
-
-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>
-static bool hasPtrOrArrType(const DeclOrExpr &E) {
-  return llvm::isa<PointerType>(E.getType().getCanonicalType()) ||
-         llvm::isa<ArrayType>(E.getType().getCanonicalType());
-}
-
 // 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
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
index 836fa33d1d8e1..07d6e5848f4e2 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
@@ -1,4 +1,4 @@
-//===------------------ SSAFAnalysesCommon.h --------------------*- C++ -*-===//
+//===-- SSAFAnalysesCommon.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.
@@ -12,9 +12,9 @@
 #ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_SSAFANALYSESCOMMON_H
 #define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_SSAFANALYSESCOMMON_H
 
+#include "clang/AST/ASTTypeTraits.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DynamicRecursiveASTVisitor.h"
-#include "clang/AST/ParentMapContext.h"
 #include "clang/Basic/SourceLocation.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/Twine.h"
@@ -24,8 +24,8 @@ namespace {
 using namespace clang;
 
 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;
 
@@ -33,14 +33,14 @@ 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());
 }
 
-static inline llvm::Error failedToAddEntitySummaryFor(ASTContext &Ctx,
-                                                      const NamedDecl *D) {
+static inline llvm::Error makeAddEntitySummaryErr(ASTContext &Ctx,
+                                                  const NamedDecl *D) {
   std::string LocStr = D->getBeginLoc().printToString(Ctx.getSourceManager());
 
   return llvm::createStringError("failed to add entity summary for %s at %s",
@@ -48,13 +48,13 @@ static inline llvm::Error failedToAddEntitySummaryFor(ASTContext &Ctx,
 }
 
 template <typename... Ts>
-static inline llvm::Error makeErrorSawButExpected(const llvm::json::Value &Saw,
+static inline llvm::Error makeSawButExpectedError(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...);
+  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...);
 }
 
 template <typename DeclOrExpr>
@@ -83,12 +83,12 @@ class ContributorFinder : public DynamicRecursiveASTVisitor {
 
     if (DC->isFileContext() || DC->isNamespace())
       Contributors.push_back(D);
-    return false;
+    return true;
   }
 };
 
-/// An AST visitor that skips callable decl and record decl strict-descendant
-/// because those are separate contributors.
+/// An AST visitor that skips the root node's strict-descendants that are
+/// callable Decls and record Decls, because those are separate contributors.
 ///
 /// The visitor calls
 /// `MatcherTy::matchFact(DynTypedNode &, ASTContext &, const NamedDecl
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
index 0e893d35e8eec..330664a52c863 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
@@ -54,12 +54,12 @@ deserializeImpl(const Object &Data, JSONFormat::EntityIdFromJSONFn Fn) {
 
   EntityPointerLevelSet EPLs;
 
-  for (auto &UnsafeBufferData : *AsArr) {
-    auto EPL = entityPointerLevelFromJSON(UnsafeBufferData, EntityIdFromJSON);
+  for (auto &EPLData : *UnsafeBuffersData) {
+    auto EPL = entityPointerLevelFromJSON(EPLData, Fn);
 
     if (!EPL)
       return EPL.takeError();
-    UnsafeBuffers.insert(*EPL);
+    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 430a7d9c7ae9d..044ea8bc47757 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
@@ -16,11 +16,16 @@
 #include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/Support/Error.h"
+#include "llvm/Support/ErrorHandling.h"
 
-namespace {
 using namespace clang;
+using namespace ssaf;
+namespace {
+
 struct UnsafePointerMatcher {
   std::set<const Expr *> UnsafePointers;
 
@@ -38,59 +43,10 @@ void findFactsInContributor(const NamedDecl *Contributor, ASTContext &Ctx,
   Finder.findMatches(Contributor);
   UnsafePointers.merge(Matcher.UnsafePointers);
 }
-
-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());
-}
-
-Expected<EntityPointerLevelSet>
-buildEntityPointerLevels(std::set<const Expr *> &&UnsafePointers,
-                         UnsafeBufferUsageTUSummaryExtractor &Extractor,
-                         ASTContext &Ctx,
-                         std::function<EntityId(EntityName)> AddEntity) {
-  EntityPointerLevelSet Result{};
-  llvm::Error AllErrors = llvm::ErrorSuccess();
-
-  for (const Expr *Ptr : UnsafePointers) {
-    Expected<EntityPointerLevelSet> Translation =
-        translateEntityPointerLevel(Ptr, Ctx, AddEntity);
-
-    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
 
-namespace clang::ssaf {
-
-class UnsafeBufferUsageTUSummaryExtractor : public TUSummaryExtractor {
+class clang::ssaf::UnsafeBufferUsageTUSummaryExtractor
+    : public TUSummaryExtractor {
 public:
   UnsafeBufferUsageTUSummaryExtractor(TUSummaryBuilder &Builder)
       : TUSummaryExtractor(Builder) {}
@@ -127,68 +83,39 @@ class UnsafeBufferUsageTUSummaryExtractor : public TUSummaryExtractor {
   }
 
   void HandleTranslationUnit(ASTContext &Ctx) override {
-    llvm::Error Errors = llvm::ErrorSuccess();
-    auto addError = [&Errors](llvm::Error Err) {
-      Errors = llvm::joinErrors(std::move(Errors), std::move(Err));
-    };
     ContributorFinder ContributorFinder;
 
-    ContributorFinder.VisitTranslationUnitDecl(Ctx.getTranslationUnitDecl());
+    ContributorFinder.TraverseAST(Ctx);
     for (auto *CD : ContributorFinder.Contributors) {
       auto EntitySummary = extractEntitySummary(CD, Ctx);
 
       if (!EntitySummary) {
-        addError(EntitySummary.takeError());
+        llvm::report_fatal_error(EntitySummary.takeError());
         continue;
       }
-      assert(*EntitySummary &&
-             "std::unique_ptr<EntitySummary> should not be null");
+      assert(*EntitySummary);
       if ((*EntitySummary)->empty())
         continue;
 
       auto ContributorName = getEntityName(CD);
 
       if (!ContributorName) {
-        addError(entityNameErrFor(Ctx, *CD));
+        llvm::report_fatal_error(makeEntityNameErr(Ctx, *CD));
         continue;
       }
 
-      auto [EntitySummaryPtr, Success] = SummaryBuilder.addSummary(
+      auto [Ignored, InsertionSucceeded] = SummaryBuilder.addSummary(
           addEntity(*ContributorName), std::move(*EntitySummary));
 
-      if (!Success)
-        addError(failedToAddEntitySummaryFor(Ctx, CD));
+      assert(InsertionSucceeded && "duplicated contributor extraction");
     }
-    // FIXME: handle errors!
-    llvm::consumeError(std::move(Errors));
   }
 };
 
-// Proxy functions for unit tests:
-extern Expected<std::unique_ptr<UnsafeBufferUsageEntitySummary>>
-extractEntitySummary(UnsafeBufferUsageTUSummaryExtractor &Extractor,
-                     const NamedDecl *Contributor, ASTContext &Ctx) {
-  return Extractor.extractEntitySummary(Contributor, Ctx);
-}
-
-extern UnsafeBufferUsageTUSummaryExtractor *
-createUnsafeBufferUsageTUSummaryExtractor(TUSummaryBuilder &Builder) {
-  return new UnsafeBufferUsageTUSummaryExtractor(Builder);
-}
-
-extern void destroyUnsafeBufferUsageTUSummaryExtractor(
-    UnsafeBufferUsageTUSummaryExtractor *Extractor) {
-  delete Extractor;
-}
-
-extern EntityId addEntity(UnsafeBufferUsageTUSummaryExtractor &Extractor,
-                          const EntityName &EN) {
-  return Extractor.addEntity(EN);
-}
-} // namespace clang::ssaf
-
+// NOLINTNEXTLINE(misc-use-internal-linkage)
 volatile int UnsafeBufferUsageTUSummaryExtractorAnchorSource = 0;
+
 static clang::ssaf::TUSummaryExtractorRegistry::Add<
     ssaf::UnsafeBufferUsageTUSummaryExtractor>
-    RegisterExtractor("UnsafeBufferUsageTUSummaryExtractor",
+    RegisterExtractor(UnsafeBufferUsageEntitySummary::Name,
                       "The TUSummaryExtractor for unsafe buffer pointers");
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index 5d2dc348e30e6..627bc88ea524b 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -6,28 +6,29 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage.h"
-#include "clang/AST/DynamicRecursiveASTVisitor.h"
-#include "clang/Frontend/ASTUnit.h"
 #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
 #include "TestFixture.h"
 #include "clang/AST/DynamicRecursiveASTVisitor.h"
 #include "clang/Frontend/ASTUnit.h"
 #include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
-#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.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"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummary.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.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 <cstddef>
+#include <initializer_list>
 #include <memory>
 #include <optional>
 
@@ -35,24 +36,6 @@ using namespace clang;
 using namespace ssaf;
 using testing::UnorderedElementsAre;
 
-namespace clang::ssaf {
-// Proxy functions
-class UnsafeBufferUsageTUSummaryExtractor;
-
-extern Expected<std::unique_ptr<UnsafeBufferUsageEntitySummary>>
-extractEntitySummary(UnsafeBufferUsageTUSummaryExtractor &Extractor,
-                     const NamedDecl *Contributor, ASTContext &Ctx);
-
-extern UnsafeBufferUsageTUSummaryExtractor *
-createUnsafeBufferUsageTUSummaryExtractor(TUSummaryBuilder &Builder);
-
-extern void destroyUnsafeBufferUsageTUSummaryExtractor(
-    UnsafeBufferUsageTUSummaryExtractor *Extractor);
-
-extern EntityId addEntity(UnsafeBufferUsageTUSummaryExtractor &Extractor,
-                          const EntityName &EN);
-} // namespace clang::ssaf
-
 namespace {
 template <typename SomeDecl = NamedDecl>
 const SomeDecl *findDeclByName(StringRef Name, ASTContext &Ctx) {
@@ -88,57 +71,82 @@ class UnsafeBufferUsageTest : public TestFixture {
 protected:
   TUSummary TUSum;
   TUSummaryBuilder Builder;
-  UnsafeBufferUsageTUSummaryExtractor *Extractor;
+  std::unique_ptr<TUSummaryExtractor> Extractor;
   std::unique_ptr<ASTUnit> AST;
 
   UnsafeBufferUsageTest()
       : TUSum(BuildNamespace(BuildNamespaceKind::CompilationUnit, "Mock.cpp")),
-        Builder(TUSum),
-        Extractor(createUnsafeBufferUsageTUSummaryExtractor(Builder)) {}
+        Builder(TUSum), Extractor(nullptr) {}
 
-  ~UnsafeBufferUsageTest() {
-    destroyUnsafeBufferUsageTUSummaryExtractor(Extractor);
-  }
-
-  template <typename ContributorDecl = NamedDecl>
-  std::unique_ptr<UnsafeBufferUsageEntitySummary>
-  setUpTest(StringRef Code, StringRef ContributorName) {
+  bool setUpTest(StringRef Code) {
     AST = tooling::buildASTFromCodeWithArgs(
         Code, {"-Wno-unused-value", "-Wno-int-to-pointer-cast"});
 
-    auto *ContributorDefn =
-        findDeclByName<ContributorDecl>(ContributorName, AST->getASTContext());
+    for (auto &E : clang::ssaf::TUSummaryExtractorRegistry::entries()) {
+      if (E.getName() == UnsafeBufferUsageEntitySummary::Name) {
+        Extractor = E.instantiate(Builder);
+        break;
+      }
+    }
+
+    if (!Extractor) {
+      ADD_FAILURE() << "failed to find UnsafeBufferUsageTUSummaryExtractor";
+      return false;
+    }
+    Extractor->HandleTranslationUnit(AST->getASTContext());
+    return true;
+  }
 
-    if (!ContributorDefn)
+  template <typename ContributorDecl = NamedDecl>
+  const UnsafeBufferUsageEntitySummary *
+  getEntitySummary(StringRef ContributoEntityrName) {
+    auto *ContributorDefn = findDeclByName<ContributorDecl>(
+        ContributoEntityrName, AST->getASTContext());
+
+    if (!ContributorDefn) {
+      ADD_FAILURE() << "failed to find Decl of \"" << ContributoEntityrName
+                    << "\"";
       return nullptr;
+    }
 
     std::optional<EntityName> EN = getEntityName(ContributorDefn);
 
-    if (!EN)
+    if (!EN) {
+      ADD_FAILURE() << "failed to get EntityName for contributor \""
+                    << ContributoEntityrName << "\"";
       return nullptr;
+    }
 
-    auto Sum =
-        extractEntitySummary(*Extractor, ContributorDefn, AST->getASTContext());
+    EntityId ContributorEntityId = Builder.addEntity(*EN);
+    auto &TUSumData = getData(TUSum);
+    auto EntitiesSumIter =
+        TUSumData.find(UnsafeBufferUsageEntitySummary::summaryName());
 
-    if (!Sum) {
-      llvm::consumeError(Sum.takeError());
+    // If none entity summary was collected, it may not be an entry in
+    // `TUSumData`:
+    if (EntitiesSumIter == TUSumData.end())
       return nullptr;
-    }
-    assert(*Sum);
-    return std::move(*Sum);
+
+    auto EntitySumIter = EntitiesSumIter->second.find(ContributorEntityId);
+
+    // If entity summary is empty, it may not exist:
+    if (EntitySumIter == EntitiesSumIter->second.end())
+      return nullptr;
+    return static_cast<const UnsafeBufferUsageEntitySummary *>(
+        EntitySumIter->second.get());
   }
 
   std::optional<EntityId> getEntityId(StringRef Name) {
     if (const auto *D = findDeclByName(Name, AST->getASTContext()))
       if (auto EntityName = getEntityName(D))
-        return addEntity(*Extractor, *EntityName);
+        return Builder.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 addEntity(*Extractor, *EntityName);
+        return Builder.addEntity(*EntityName);
     return std::nullopt;
   }
 
@@ -178,8 +186,8 @@ getSubsetOf(const EntityPointerLevelSet &Set, EntityId Entity) {
 }
 
 TEST_F(UnsafeBufferUsageTest, EntityPointerLevelComparison) {
-  EntityId E1 = addEntity(*Extractor, {"c:@F at foo", "", {}});
-  EntityId E2 = addEntity(*Extractor, {"c:@F at bar", "", {}});
+  EntityId E1 = Builder.addEntity({"c:@F at foo", "", {}});
+  EntityId E2 = Builder.addEntity({"c:@F at bar", "", {}});
 
   auto P1 = buildEntityPointerLevel(E1, 2);
   auto P2 = buildEntityPointerLevel(E1, 2);
@@ -197,9 +205,9 @@ TEST_F(UnsafeBufferUsageTest, EntityPointerLevelComparison) {
 }
 
 TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageEntityPointerLevelSetTest) {
-  EntityId E1 = addEntity(*Extractor, {"c:@F at foo", "", {}});
-  EntityId E2 = addEntity(*Extractor, {"c:@F at bar", "", {}});
-  EntityId E3 = addEntity(*Extractor, {"c:@F at baz", "", {}});
+  EntityId E1 = Builder.addEntity({"c:@F at foo", "", {}});
+  EntityId E2 = Builder.addEntity({"c:@F at bar", "", {}});
+  EntityId E3 = Builder.addEntity({"c:@F at baz", "", {}});
 
   auto P1 = buildEntityPointerLevel(E1, 1);
   auto P2 = buildEntityPointerLevel(E1, 2);
@@ -220,13 +228,14 @@ TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageEntityPointerLevelSetTest) {
 //////////////////////////////////////////////////////////////
 
 TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageSerializeTest) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     void foo(int ***p, int ****q, int x) {
       p[5][5][5];
       q[5][5][5][5];
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+  const auto *Sum = getEntitySummary("foo");
   ASSERT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U},
                                      {"p", 2U},
@@ -266,40 +275,43 @@ TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageSerializeTest) {
 //////////////////////////////////////////////////////////////
 
 TEST_F(UnsafeBufferUsageTest, SimpleFunctionWithUnsafePointer) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     void foo(int *p) {
       p[5];
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+  const auto *Sum = getEntitySummary("foo");
 
   EXPECT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}}));
 }
 
 TEST_F(UnsafeBufferUsageTest, PointerArithmetic) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     void foo(int *p, int *q) {
       *(p + 5);
       *(q - 3);
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+  const auto *Sum = getEntitySummary("foo");
 
   EXPECT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}}));
 }
 
 TEST_F(UnsafeBufferUsageTest, PointerIncrementDecrement) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     void foo(int *p, int *q, int *r, int *s) {
       (++p)[5];
       (q++)[5];
       (--r)[5];
       (s--)[5];
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+  const auto *Sum = getEntitySummary("foo");
 
   EXPECT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum,
@@ -307,40 +319,43 @@ TEST_F(UnsafeBufferUsageTest, PointerIncrementDecrement) {
 }
 
 TEST_F(UnsafeBufferUsageTest, PointerAssignment) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     void foo(int *p, int *q) {
       (p = q + 5)[5];
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+  const auto *Sum = getEntitySummary("foo");
 
   EXPECT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}}));
 }
 
 TEST_F(UnsafeBufferUsageTest, CompoundAssignment) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     void foo(int *p, int *q) {
       (p += 5)[5];
       (q -= 3)[5];
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+  const auto *Sum = getEntitySummary("foo");
 
   EXPECT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}}));
 }
 
 TEST_F(UnsafeBufferUsageTest, MultiLevelPointer) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     void foo(int **p, int **q, int **r) {
       (*p)[5];
       *(*q);
       *(q[5]);
       r[5][5];
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+  const auto *Sum = getEntitySummary("foo");
 
   EXPECT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum,
@@ -348,13 +363,14 @@ TEST_F(UnsafeBufferUsageTest, MultiLevelPointer) {
 }
 
 TEST_F(UnsafeBufferUsageTest, ConditionalOperator) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     void foo(int **p, int **q, int cond) {
       (cond ? *p : *q)[5];
       cond ? p[5] : q[5];
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+  const auto *Sum = getEntitySummary("foo");
 
   EXPECT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum,
@@ -362,78 +378,84 @@ TEST_F(UnsafeBufferUsageTest, ConditionalOperator) {
 }
 
 TEST_F(UnsafeBufferUsageTest, CastExpression) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     void foo(void *p, int q) {
       ((int*)p)[5];
       ((int*)q)[5];
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+  const auto *Sum = getEntitySummary("foo");
 
   EXPECT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}}));
 }
 
 TEST_F(UnsafeBufferUsageTest, CommaOperator) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     void foo(int *p, int x) {
       (x++, p)[5];
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+  const auto *Sum = getEntitySummary("foo");
 
   EXPECT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}}));
 }
 
 TEST_F(UnsafeBufferUsageTest, CommaOperator2) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     void foo(int **p, int **q, int x) {
       (p[x] = 0, q[x] = 0)[5];
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+  const auto *Sum = getEntitySummary("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(
+  ASSERT_EQ(setUpTest(R"cpp(
     void foo(int *p) {
       (((p)))[5];
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+  const auto *Sum = getEntitySummary("foo");
 
   EXPECT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}}));
 }
 
 TEST_F(UnsafeBufferUsageTest, ArrayParameter) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     void foo(int arr[], int arr2[][10]) {
       int n = 5;
       arr[100];
       arr2[5][n];
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+  const auto *Sum = getEntitySummary("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(
+  ASSERT_EQ(setUpTest(R"cpp(
     int ** (*fp)();
     int ** foo() {
       fp = &foo;
       foo()[5];
       (*fp())[5];
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+  const auto *Sum = getEntitySummary("foo");
 
   EXPECT_NE(Sum, nullptr);
   // No (foo, 2) becasue indirect calls are ignored.
@@ -441,7 +463,7 @@ TEST_F(UnsafeBufferUsageTest, FunctionCall) {
 }
 
 TEST_F(UnsafeBufferUsageTest, StructMemberAccess) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     struct S {
       int *ptr;
       int (*ptr_to_arr)[10];
@@ -451,206 +473,215 @@ TEST_F(UnsafeBufferUsageTest, StructMemberAccess) {
       obj.ptr[5];
       (*obj.ptr_to_arr)[n];
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+  const auto *Sum = getEntitySummary("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(
+  ASSERT_EQ(setUpTest(R"cpp(
     void foo() {
       "hello"[5];
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+  const auto *Sum = getEntitySummary("foo");
 
-  EXPECT_NE(Sum, nullptr);
   // String literals should not generate pointer kind variables
-  EXPECT_EQ(*Sum, makeSet(__LINE__, {}));
+  EXPECT_EQ(Sum, nullptr);
 }
 
 TEST_F(UnsafeBufferUsageTest, OpaqueValueExpr) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     void foo(int *p, int *q) {
        (p ?: q)[5];
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+  const auto *Sum = getEntitySummary("foo");
 
   EXPECT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}}));
 }
 
 TEST_F(UnsafeBufferUsageTest, AddressOfOperator) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(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__, {}));
+  )cpp"),
+            true);
+  const auto *Sum = getEntitySummary("foo");
+  // Address-of should not generate pointer kind variables for 'x':
+  EXPECT_EQ(Sum, nullptr);
 }
 
 TEST_F(UnsafeBufferUsageTest, AddressOfThenDereference) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     void foo(int *p, int *q) {
       (*(&p))[5];
       (&(*q))[5];
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+  const auto *Sum = getEntitySummary("foo");
 
   EXPECT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1}, {"q", 1}}));
 }
 
 TEST_F(UnsafeBufferUsageTest, PointerToArrayOfPointers) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     void foo() {
       int * arr[10];
       int * (*p)[10] = arr;
 
-      (*p)[5][5]; // '(*p)[5]' is unsafe 
+      (*p)[5][5]; // '(*p)[5]' is unsafe
                   // '(*p)' is fine because 5 < 10
     }
-  )cpp",
-                       "foo");
+  )cpp"),
+            true);
+  const auto *Sum = getEntitySummary("foo");
 
   EXPECT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 3}}));
 }
 
 TEST_F(UnsafeBufferUsageTest, UnsafePointerInGlobalVariableInitializer) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
       int *gp;
       int x = gp[5];
-    )cpp",
-                       {"x"});
+    )cpp"),
+            true);
+  const auto *Sum = getEntitySummary("x");
 
   EXPECT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
 }
 
 TEST_F(UnsafeBufferUsageTest, UnsafePointerInFieldInitializer) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
       int *gp;
       struct Foo {
         int field = gp[5];
       };
-    )cpp",
-                       {"Foo"});
+    )cpp"),
+            true);
+  const auto *Sum = getEntitySummary("Foo");
 
   EXPECT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
 }
 
 TEST_F(UnsafeBufferUsageTest, UnsafePointerInFieldInitializer2) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
       int *gp;
       union Foo {
         int field = gp[5];
         int x;
       };
-    )cpp",
-                       {"Foo"});
+    )cpp"),
+            true);
+  const auto *Sum = getEntitySummary("Foo");
 
   EXPECT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
 }
 
 TEST_F(UnsafeBufferUsageTest, InitializerList) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
       int *gp;
       struct Foo {
         int field;
         int x;
       };
       Foo FooObj{gp[5], 0};
-    )cpp",
-                       {"FooObj"});
+    )cpp"),
+            true);
+  const auto *Sum = getEntitySummary("FooObj");
 
   EXPECT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
 }
 
 TEST_F(UnsafeBufferUsageTest, UnsafePointerInCXXCtorInitializer) {
-  auto Sum = setUpTest<CXXConstructorDecl>(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
       struct Foo {
         int member;
         Foo(int *p) : member(p[5]) {}
       };
-    )cpp",
-                                           {"Foo"});
+    )cpp"),
+            true);
+  const auto *Sum = getEntitySummary<CXXConstructorDecl>("Foo");
 
   EXPECT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}}));
 }
 
 TEST_F(UnsafeBufferUsageTest, UnsafePointerInDefaultArg) {
-  auto Sum = setUpTest(R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     int * gp;
     void foo(int x = gp[5]);
-    )cpp",
-                       {"foo"});
+    )cpp"),
+            true);
+  const auto *Sum = getEntitySummary("foo");
 
   EXPECT_NE(Sum, nullptr);
   EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
 }
 
 TEST_F(UnsafeBufferUsageTest, NestedDefinitions) {
-  StringRef Code = R"cpp(
+  ASSERT_EQ(setUpTest(R"cpp(
     int * a = [](){
       struct Foo {
         void bar(int * ptr) { ptr[3] = 0; }
       };
       return nullptr;
     }();
-    )cpp";
-  auto Sum = setUpTest(Code, {"bar"});
+    )cpp"),
+            true);
+  const auto *Sum = getEntitySummary("bar");
 
   EXPECT_NE(Sum, nullptr);
   // The closest contributor owns the fact:
   EXPECT_EQ(*Sum, makeSet(__LINE__, {{"ptr", 1U}}));
 
-  Sum = setUpTest(Code, {"Foo"});
+  Sum = getEntitySummary("Foo");
 
-  EXPECT_NE(Sum, nullptr);
-  EXPECT_TRUE(Sum->empty());
+  EXPECT_EQ(Sum, nullptr);
 
-  Sum = setUpTest(Code, {"a"});
+  Sum = getEntitySummary("a");
 
-  EXPECT_NE(Sum, nullptr);
-  EXPECT_TRUE(Sum->empty());
+  EXPECT_EQ(Sum, nullptr);
 }
 
 TEST_F(UnsafeBufferUsageTest, NestedDefinitions2) {
-  StringRef Code = R"cpp(
+  bool SetupSuccess = setUpTest(R"cpp(
     int main(void) {
        struct Foo {
           void bar(int * ptr) { ptr[3] = 0; }
        };
     }
-    )cpp";
-  auto Sum = setUpTest(Code, {"bar"});
+    )cpp");
+
+  ASSERT_EQ(SetupSuccess, true);
+
+  const auto *Sum = getEntitySummary("bar");
 
   EXPECT_NE(Sum, nullptr);
   // The closest contributor owns the fact:
   EXPECT_EQ(*Sum, makeSet(__LINE__, {{"ptr", 1U}}));
 
-  Sum = setUpTest(Code, {"Foo"});
+  Sum = getEntitySummary("Foo");
 
-  EXPECT_NE(Sum, nullptr);
-  EXPECT_TRUE(Sum->empty());
+  EXPECT_EQ(Sum, nullptr);
 
-  Sum = setUpTest(Code, {"main"});
+  Sum = getEntitySummary("main");
 
-  EXPECT_NE(Sum, nullptr);
-  EXPECT_TRUE(Sum->empty());
+  EXPECT_EQ(Sum, nullptr);
 }
 
 } // namespace

>From 1a7d533991ea7f95f5258019412e20262e83b9f0 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Sat, 4 Apr 2026 23:43:10 -0700
Subject: [PATCH 05/10] fix clang-format issue

---
 .../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 330664a52c863..6bebea52a3768 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
-#include "SSAFAnalysesCommon.h"	
+#include "SSAFAnalysesCommon.h"
 #include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
 #include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h"
 #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h"

>From ae57606288acd7ef645a0526fb6a2cbee103acea Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Mon, 6 Apr 2026 14:38:34 -0700
Subject: [PATCH 06/10] fix build issue

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

diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
index 07d6e5848f4e2..9d9b445420d20 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
@@ -14,6 +14,7 @@
 
 #include "clang/AST/ASTTypeTraits.h"
 #include "clang/AST/Decl.h"
+#include "clang/AST/DeclObjC.h"
 #include "clang/AST/DynamicRecursiveASTVisitor.h"
 #include "clang/Basic/SourceLocation.h"
 #include "llvm/ADT/SmallVector.h"

>From 8e548903cf65f78bc4a79d6a7b788d222d14cdb6 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Mon, 6 Apr 2026 16:28:39 -0700
Subject: [PATCH 07/10] fix bugs

---
 .../ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h    | 2 +-
 .../Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp        | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h b/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
index 746e4776e4cbf..916ef1ce783c4 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
@@ -39,7 +39,7 @@ extern volatile int UnsafeBufferUsageSSAFJSONFormatAnchorSource;
 extern volatile int UnsafeBufferUsageTUSummaryExtractorAnchorSource;
 [[maybe_unused]] static int
     UnsafeBufferUsageTUSummaryExtractorAnchorDestination =
-        UnsafeBufferUsageSSAFJSONFormatAnchorSource;
+        UnsafeBufferUsageTUSummaryExtractorAnchorSource;
 
 // This anchor is used to force the linker to link the CallGraphExtractor.
 extern volatile int CallGraphExtractorAnchorSource;
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index 627bc88ea524b..879b7c9b6b316 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -537,7 +537,7 @@ TEST_F(UnsafeBufferUsageTest, PointerToArrayOfPointers) {
   ASSERT_EQ(setUpTest(R"cpp(
     void foo() {
       int * arr[10];
-      int * (*p)[10] = arr;
+      int * (*p)[10] = &arr;
 
       (*p)[5][5]; // '(*p)[5]' is unsafe
                   // '(*p)' is fine because 5 < 10

>From abc7647c5726d8e979c234183fdff0fc129f603f Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Mon, 6 Apr 2026 16:59:35 -0700
Subject: [PATCH 08/10] fix typo

---
 .../Analyses/EntityPointerLevel.h                    |  2 +-
 .../Analyses/SSAFAnalysesCommon.h                    |  6 +++---
 .../UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp | 12 ++++--------
 .../UnsafeBufferUsage/UnsafeBufferUsageTest.cpp      |  8 ++++----
 4 files changed, 12 insertions(+), 16 deletions(-)

diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
index f2a2c73a29a7a..edc62b0521ed7 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
@@ -110,7 +110,7 @@ translateEntityPointerLevel(const Expr *E, ASTContext &Ctx,
 /// \param IsFunRet true iff the created EPL is associated with the return type
 /// of a function entity.
 llvm::Expected<EntityPointerLevel>
-creatEntityPointerLevel(const NamedDecl *ND, ASTContext &Ctx,
+createEntityPointerLevel(const NamedDecl *ND, ASTContext &Ctx,
                         std::function<EntityId(EntityName EN)> AddEntity,
                         bool IsFunRet = false);
 
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
index 9d9b445420d20..861dac3af8724 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
@@ -21,7 +21,6 @@
 #include "llvm/ADT/Twine.h"
 #include "llvm/Support/JSON.h"
 
-namespace {
 using namespace clang;
 
 template <typename NodeTy, typename... Ts>
@@ -59,11 +58,12 @@ static inline llvm::Error makeSawButExpectedError(const llvm::json::Value &Saw,
 }
 
 template <typename DeclOrExpr>
-static bool hasPtrOrArrType(const DeclOrExpr &E) {
+static inline bool hasPtrOrArrType(const DeclOrExpr &E) {
   return llvm::isa<PointerType>(E.getType().getCanonicalType()) ||
          llvm::isa<ArrayType>(E.getType().getCanonicalType());
 }
 
+namespace clang::ssaf {
 /// Traverses the AST and finds contributors:
 class ContributorFinder : public DynamicRecursiveASTVisitor {
 public:
@@ -144,5 +144,5 @@ class ContributorFactFinder : public DynamicRecursiveASTVisitor {
     return true; // skip lambda as it is a callable
   }
 };
-} // namespace
+} // namespace clang::ssaf
 #endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_SSAFANALYSESCOMMON_H
\ 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 044ea8bc47757..e0bad1b8cb310 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
@@ -89,20 +89,16 @@ class clang::ssaf::UnsafeBufferUsageTUSummaryExtractor
     for (auto *CD : ContributorFinder.Contributors) {
       auto EntitySummary = extractEntitySummary(CD, Ctx);
 
-      if (!EntitySummary) {
-        llvm::report_fatal_error(EntitySummary.takeError());
-        continue;
-      }
+      if (!EntitySummary)
+        llvm::reportFatalInternalError(EntitySummary.takeError());
       assert(*EntitySummary);
       if ((*EntitySummary)->empty())
         continue;
 
       auto ContributorName = getEntityName(CD);
 
-      if (!ContributorName) {
-        llvm::report_fatal_error(makeEntityNameErr(Ctx, *CD));
-        continue;
-      }
+      if (!ContributorName)
+        llvm::reportFatalInternalError(makeEntityNameErr(Ctx, *CD));
 
       auto [Ignored, InsertionSucceeded] = SummaryBuilder.addSummary(
           addEntity(*ContributorName), std::move(*EntitySummary));
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index 879b7c9b6b316..151153ea856d6 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -99,12 +99,12 @@ class UnsafeBufferUsageTest : public TestFixture {
 
   template <typename ContributorDecl = NamedDecl>
   const UnsafeBufferUsageEntitySummary *
-  getEntitySummary(StringRef ContributoEntityrName) {
+  getEntitySummary(StringRef ContributorEntityName) {
     auto *ContributorDefn = findDeclByName<ContributorDecl>(
-        ContributoEntityrName, AST->getASTContext());
+        ContributorEntityName, AST->getASTContext());
 
     if (!ContributorDefn) {
-      ADD_FAILURE() << "failed to find Decl of \"" << ContributoEntityrName
+      ADD_FAILURE() << "failed to find Decl of \"" << ContributorEntityName
                     << "\"";
       return nullptr;
     }
@@ -113,7 +113,7 @@ class UnsafeBufferUsageTest : public TestFixture {
 
     if (!EN) {
       ADD_FAILURE() << "failed to get EntityName for contributor \""
-                    << ContributoEntityrName << "\"";
+                    << ContributorEntityName << "\"";
       return nullptr;
     }
 

>From 63be9b2606098b1704b74a8bec7c0904d959423e Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Mon, 6 Apr 2026 17:27:14 -0700
Subject: [PATCH 09/10] fix format

---
 .../Analyses/EntityPointerLevel.h                             | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
index edc62b0521ed7..aa4123dd372af 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
@@ -111,8 +111,8 @@ translateEntityPointerLevel(const Expr *E, ASTContext &Ctx,
 /// of a function entity.
 llvm::Expected<EntityPointerLevel>
 createEntityPointerLevel(const NamedDecl *ND, ASTContext &Ctx,
-                        std::function<EntityId(EntityName EN)> AddEntity,
-                        bool IsFunRet = false);
+                         std::function<EntityId(EntityName EN)> AddEntity,
+                         bool IsFunRet = false);
 
 /// Creates a new EntityPointerLevel (EPL) from `E` by incrementing `E`'s
 /// pointer level.

>From c3a66439730e843248d9ac569b3aaf999f297270 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Wed, 8 Apr 2026 17:06:27 -0700
Subject: [PATCH 10/10] Update
 clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.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/EntityPointerLevel.h                               | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
index aa4123dd372af..94bd086793a8b 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
@@ -1,4 +1,4 @@
-//===---------------- EntityPointerLevel.h ----------------*- C++ -*-===//
+//===- 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.



More information about the llvm-branch-commits mailing list