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

>From a4ccace9e00f6d08f5a00f17a12e1071fff6bd53 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <[email protected]>
Date: Thu, 9 Apr 2026 18:43:40 -0700
Subject: [PATCH 1/7] [NFC][SSAF] Move EntityPointerLevel to a separate folder

EntityPointerLevel will later be shared with other summaries besides
UnsafeBufferUsage. This commit moves it to a separate file.
---
 .../EntityPointerLevel/EntityPointerLevel.h   |  99 +++++++
 .../UnsafeBufferUsage/UnsafeBufferUsage.h     |  68 +----
 .../UnsafeBufferUsageExtractor.h              |   5 -
 .../Analyses/CMakeLists.txt                   |   1 +
 .../EntityPointerLevel/EntityPointerLevel.cpp | 244 ++++++++++++++++++
 .../UnsafeBufferUsage/UnsafeBufferUsage.cpp   |   4 -
 .../UnsafeBufferUsageExtractor.cpp            | 220 +---------------
 .../UnsafeBufferUsageTest.cpp                 |   4 +-
 8 files changed, 353 insertions(+), 292 deletions(-)
 create mode 100644 
clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h
 create mode 100644 
clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp

diff --git 
a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h
 
b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h
new file mode 100644
index 0000000000000..52caa52e1120d
--- /dev/null
+++ 
b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h
@@ -0,0 +1,99 @@
+//===- 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_ENTITYPOINTERLEVEL_H
+#define 
LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_ENTITYPOINTERLEVEL_H
+
+#include "clang/AST/Expr.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.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 EntityPointerLevelTranslator;
+  friend EntityPointerLevel buildEntityPointerLevel(EntityId, unsigned);
+
+  EntityPointerLevel(EntityId Entity, unsigned PointerLevel)
+      : Entity(Entity), PointerLevel(PointerLevel) {}
+
+public:
+  EntityId getEntity() const { return Entity; }
+  unsigned getPointerLevel() const { return PointerLevel; }
+
+  bool operator==(const EntityPointerLevel &Other) const {
+    return std::tie(Entity, PointerLevel) ==
+           std::tie(Other.Entity, Other.PointerLevel);
+  }
+
+  bool operator!=(const EntityPointerLevel &Other) const {
+    return !(*this == Other);
+  }
+
+  bool operator<(const EntityPointerLevel &Other) const {
+    return std::tie(Entity, PointerLevel) <
+           std::tie(Other.Entity, Other.PointerLevel);
+  }
+
+  /// Compares `EntityPointerLevel`s; additionally, partially compares
+  /// `EntityPointerLevel` with `EntityId`.
+  struct Comparator {
+    using is_transparent = void;
+    bool operator()(const EntityPointerLevel &L,
+                    const EntityPointerLevel &R) const {
+      return L < R;
+    }
+    bool operator()(const EntityId &L, const EntityPointerLevel &R) const {
+      return L < R.getEntity();
+    }
+    bool operator()(const EntityPointerLevel &L, const EntityId &R) const {
+      return L.getEntity() < R;
+    }
+  };
+};
+
+using EntityPointerLevelSet =
+    std::set<EntityPointerLevel, EntityPointerLevel::Comparator>;
+
+/// 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);
+
+EntityPointerLevel buildEntityPointerLevel(EntityId, unsigned);
+} // namespace clang::ssaf
+#endif // 
LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_ENTITYPOINTERLEVEL_H
diff --git 
a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
 
b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
index 4e217c2eb87ef..250bad5b72f75 100644
--- 
a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
+++ 
b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
@@ -9,7 +9,7 @@
 #ifndef 
LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGE_H
 #define 
LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGE_H
 
-#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
+#include 
"clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/SummaryName.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
 #include "llvm/ADT/StringRef.h"
@@ -18,72 +18,6 @@
 
 namespace clang::ssaf {
 
-/// An EntityPointerLevel represents a level of the declared pointer/array
-/// type of an entity.  In the fully-expanded spelling of the declared type, a
-/// EntityPointerLevel is associated with a '*' (or a '[]`) in that 
declaration.
-///
-/// For example, for 'int *p[10];', there are two EntityPointerLevels. One
-/// is associated with 'int *[10]' of 'p' and the other is associated with 'int
-/// *' of 'p'.
-///
-/// An EntityPointerLevel can be identified by an EntityId and an unsigned
-/// integer indicating the pointer level: '(EntityId, PointerLevel)'.
-/// An EntityPointerLevel 'P' is valid iff 'P.EntityId' has a pointer type with
-/// at least 'P.PointerLevel' levels (This implies 'P.PointerLevel > 0').
-///
-/// For the same example 'int *p[10];', the EntityPointerLevels below are 
valid:
-/// - '(p, 2)' is associated with the 'int *' part of the declared type of 'p';
-/// - '(p, 1)' is associated with the 'int *[10]' part of the declared type of
-/// 'p'.
-class EntityPointerLevel {
-  EntityId Entity;
-  unsigned PointerLevel;
-
-  friend class UnsafeBufferUsageEntitySummary;
-  friend class UnsafeBufferUsageTUSummaryExtractor;
-  friend EntityPointerLevel buildEntityPointerLevel(EntityId, unsigned);
-
-  EntityPointerLevel(EntityId Entity, unsigned PointerLevel)
-      : Entity(Entity), PointerLevel(PointerLevel) {}
-
-public:
-  EntityId getEntity() const { return Entity; }
-  unsigned getPointerLevel() const { return PointerLevel; }
-
-  bool operator==(const EntityPointerLevel &Other) const {
-    return std::tie(Entity, PointerLevel) ==
-           std::tie(Other.Entity, Other.PointerLevel);
-  }
-
-  bool operator!=(const EntityPointerLevel &Other) const {
-    return !(*this == Other);
-  }
-
-  bool operator<(const EntityPointerLevel &Other) const {
-    return std::tie(Entity, PointerLevel) <
-           std::tie(Other.Entity, Other.PointerLevel);
-  }
-
-  /// Compares `EntityPointerLevel`s; additionally, partially compares
-  /// `EntityPointerLevel` with `EntityId`.
-  struct Comparator {
-    using is_transparent = void;
-    bool operator()(const EntityPointerLevel &L,
-                    const EntityPointerLevel &R) const {
-      return L < R;
-    }
-    bool operator()(const EntityId &L, const EntityPointerLevel &R) const {
-      return L < R.getEntity();
-    }
-    bool operator()(const EntityPointerLevel &L, const EntityId &R) const {
-      return L.getEntity() < R;
-    }
-  };
-};
-
-using EntityPointerLevelSet =
-    std::set<EntityPointerLevel, EntityPointerLevel::Comparator>;
-
 /// An UnsafeBufferUsageEntitySummary is an immutable set of unsafe buffers, in
 /// the form of EntityPointerLevel.
 class UnsafeBufferUsageEntitySummary final : public EntitySummary {
diff --git 
a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h
 
b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h
index 765b2c37562ce..13d4e18b4e81f 100644
--- 
a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h
+++ 
b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h
@@ -22,11 +22,6 @@ class UnsafeBufferUsageTUSummaryExtractor : public 
TUSummaryExtractor {
   UnsafeBufferUsageTUSummaryExtractor(TUSummaryBuilder &Builder)
       : TUSummaryExtractor(Builder) {}
 
-  static EntityPointerLevel buildEntityPointerLevel(EntityId Entity,
-                                                    unsigned PointerLevel) {
-    return {Entity, PointerLevel};
-  }
-
   EntityId addEntity(EntityName EN) { return SummaryBuilder.addEntity(EN); }
 
   std::unique_ptr<UnsafeBufferUsageEntitySummary>
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt 
b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
index 926b610aa8dee..c15ff3b3c42e7 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
@@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS
 add_clang_library(clangScalableStaticAnalysisFrameworkAnalyses 
   CallGraph/CallGraphExtractor.cpp
   CallGraph/CallGraphJSONFormat.cpp
+  EntityPointerLevel/EntityPointerLevel.cpp
   UnsafeBufferUsage/UnsafeBufferUsage.cpp
   UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
 
diff --git 
a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
 
b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
new file mode 100644
index 0000000000000..49a135b13877a
--- /dev/null
+++ 
b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
@@ -0,0 +1,244 @@
+//===- 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/EntityPointerLevel.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
+
+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());
+}
+
+static llvm::Error makeUnsupportedStmtKindError(const Stmt *Unsupported) {
+  return llvm::createStringError(
+      "unsupported expression kind for translation to "
+      "EntityPointerLevel: %s",
+      Unsupported->getStmtClassName());
+}
+
+static llvm::Error makeCreateEntityNameError(const NamedDecl *FailedDecl,
+                                             ASTContext &Ctx) {
+  std::string LocStr = FailedDecl->getSourceRange().getBegin().printToString(
+      Ctx.getSourceManager());
+  return llvm::createStringError(
+      "failed to create entity name for %s declared at %s",
+      FailedDecl->getNameAsString().c_str(), LocStr.c_str());
+}
+
+// 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 ssaf::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 EntityPointerLevel(E.getEntity(), E.getPointerLevel() + 1);
+  }
+
+  static EntityPointerLevel decrementPointerLevel(const EntityPointerLevel &E) 
{
+    assert(E.getPointerLevel() > 0);
+    return EntityPointerLevel(E.getEntity(), E.getPointerLevel() - 1);
+  }
+
+  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(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()};
+  }
+
+  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); }
+
+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> clang::ssaf::translateEntityPointerLevel(
+    const Expr *E, ASTContext &Ctx,
+    std::function<EntityId(EntityName EN)> AddEntity) {
+  EntityPointerLevelTranslator Translator(AddEntity, Ctx);
+
+  return Translator.translate(E);
+}
+
+EntityPointerLevel clang::ssaf::buildEntityPointerLevel(EntityId Id,
+                                                        unsigned PtrLv) {
+  return EntityPointerLevel({Id, PtrLv});
+}
diff --git 
a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
 
b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
index d325e8df79c20..84f3f9cbb3852 100644
--- 
a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
+++ 
b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
@@ -21,10 +21,6 @@ using Object = llvm::json::Object;
 
 static constexpr llvm::StringLiteral SummarySerializationKey = "UnsafeBuffers";
 
-EntityPointerLevel ssaf::buildEntityPointerLevel(EntityId Id, unsigned PtrLv) {
-  return EntityPointerLevel(Id, PtrLv);
-}
-
 UnsafeBufferUsageEntitySummary
 ssaf::buildUnsafeBufferUsageEntitySummary(EntityPointerLevelSet UnsafeBuffers) 
{
   return UnsafeBufferUsageEntitySummary(std::move(UnsafeBuffers));
diff --git 
a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
 
b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
index c609168e4dc7d..b29eaa6b903d0 100644
--- 
a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
+++ 
b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
@@ -11,8 +11,8 @@
 #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/EntityPointerLevel.h"
 #include 
"clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
 #include "llvm/ADT/STLExtras.h"
@@ -24,22 +24,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 +43,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 +90,9 @@ static std::set<const Expr *> 
findUnsafePointersInContributor(const Decl *D) {
 std::unique_ptr<UnsafeBufferUsageEntitySummary>
 UnsafeBufferUsageTUSummaryExtractor::extractEntitySummary(
     const Decl *Contributor, ASTContext &Ctx, llvm::Error &Error) {
+  auto AddEntity = [this](EntityName EN) { return addEntity(EN); };
   Expected<EntityPointerLevelSet> EPLs = buildEntityPointerLevels(
-      findUnsafePointersInContributor(Contributor), *this, Ctx);
+      findUnsafePointersInContributor(Contributor), *this, Ctx, AddEntity);
 
   if (EPLs)
     return std::make_unique<UnsafeBufferUsageEntitySummary>(
diff --git 
a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
 
b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index 51d422c1921af..d2c513b7c70a2 100644
--- 
a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ 
b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -10,6 +10,7 @@
 #include "TestFixture.h"
 #include "clang/AST/DynamicRecursiveASTVisitor.h"
 #include "clang/Frontend/ASTUnit.h"
+#include 
"clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
 #include 
"clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h"
 #include 
"clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
@@ -63,9 +64,6 @@ const FunctionDecl *findFnByName(StringRef Name, ASTContext 
&Ctx) {
   return findDeclByName<FunctionDecl>(Name, Ctx);
 }
 
-constexpr inline auto buildEntityPointerLevel =
-    UnsafeBufferUsageTUSummaryExtractor::buildEntityPointerLevel;
-
 class UnsafeBufferUsageTest : public TestFixture {
 protected:
   TUSummary TUSum;

>From 3994ca430a89be4b890a340fcf4c8652fd04ce7e Mon Sep 17 00:00:00 2001
From: Ziqing Luo <[email protected]>
Date: Thu, 9 Apr 2026 19:01:09 -0700
Subject: [PATCH 2/7] [SSAF][UnsafeBufferUsage] Add APIs to the
 EntityPointerLevel module for UnsafeBufferUsage

- UnsafeBufferUsage serialization uses EntityPointerLevel's API to
  serialize EntityPointerLevels.
- Add APIs to EntityPointerLevel for creating EPLs from Decls and
  incrementing EPL's pointer level.
- Improve UnsafeBufferUsage serialization error messages with a test.
---
 .../EntityPointerLevel/EntityPointerLevel.h   |  31 +++-
 .../EntityPointerLevelFormat.h                |  33 ++++
 .../UnsafeBufferUsage/UnsafeBufferUsage.h     |   8 +-
 .../EntityPointerLevel/EntityPointerLevel.cpp | 141 +++++++++++++-----
 .../UnsafeBufferUsage/UnsafeBufferUsage.cpp   |  28 ++--
 .../UnsafeBufferUsageExtractor.cpp            |   4 +-
 .../tu-summary-serialization.test             |  16 +-
 7 files changed, 191 insertions(+), 70 deletions(-)
 create mode 100644 
clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h

diff --git 
a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h
 
b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h
index 52caa52e1120d..6f5687fd186c9 100644
--- 
a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h
+++ 
b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h
@@ -16,9 +16,10 @@
 
 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.
+/// 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
@@ -38,10 +39,11 @@ class EntityPointerLevel {
   unsigned PointerLevel;
 
   friend class EntityPointerLevelTranslator;
+  // 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; }
@@ -95,5 +97,24 @@ translateEntityPointerLevel(const Expr *E, ASTContext &Ctx,
                             std::function<EntityId(EntityName EN)> AddEntity);
 
 EntityPointerLevel buildEntityPointerLevel(EntityId, unsigned);
+
+/// 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);
 } // namespace clang::ssaf
 #endif // 
LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_ENTITYPOINTERLEVEL_H
diff --git 
a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
 
b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
new file mode 100644
index 0000000000000..766e425338e96
--- /dev/null
+++ 
b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
@@ -0,0 +1,33 @@
+//===- EntityPointerLevelFormat.h -------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef 
LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_ENTITYPOINTERLEVELFORMAT_H
+#define 
LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_ENTITYPOINTERLEVELFORMAT_H
+#include 
"clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
+#include 
"clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
+
+template <typename... Ts>
+static inline llvm::Error makeSawButExpectedError(const llvm::json::Value &Saw,
+                                                  llvm::StringRef Expected,
+                                                  const Ts &...ExpectedArgs) {
+  std::string Fmt = ("saw %s but expected " + Expected).str();
+  std::string SawStr = llvm::formatv("{0:2}", Saw).str();
+
+  return llvm::createStringError(Fmt.c_str(), SawStr.c_str(), ExpectedArgs...);
+}
+
+namespace clang::ssaf {
+llvm::json::Value
+entityPointerLevelToJSON(const EntityPointerLevel &EPL,
+                         JSONFormat::EntityIdToJSONFn EntityId2JSON);
+
+Expected<EntityPointerLevel>
+entityPointerLevelFromJSON(const llvm::json::Value &EPLData,
+                           JSONFormat::EntityIdFromJSONFn EntityIdFromJSON);
+} // namespace clang::ssaf
+#endif // 
LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_ENTITYPOINTERLEVELFORMAT_H
\ No newline at end of file
diff --git 
a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
 
b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
index 250bad5b72f75..27bda9f773085 100644
--- 
a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
+++ 
b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
@@ -12,14 +12,10 @@
 #include 
"clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/SummaryName.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/ADT/iterator_range.h"
-#include <set>
 
 namespace clang::ssaf {
-
-/// An 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/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
 
b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
index 49a135b13877a..78afdf956eb57 100644
--- 
a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
+++ 
b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
@@ -10,32 +10,34 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/StmtVisitor.h"
+#include 
"clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
+#include <optional>
 
 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());
+template <typename DeclOrExpr>
+static bool hasPtrOrArrType(const DeclOrExpr &E) {
+  return llvm::isa<PointerType>(E.getType().getCanonicalType()) ||
+         llvm::isa<ArrayType>(E.getType().getCanonicalType());
 }
 
-static llvm::Error makeUnsupportedStmtKindError(const Stmt *Unsupported) {
-  return llvm::createStringError(
-      "unsupported expression kind for translation to "
-      "EntityPointerLevel: %s",
-      Unsupported->getStmtClassName());
+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 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 inline llvm::Error makeEntityNameErr(ASTContext &Ctx,
+                                            const NamedDecl &D) {
+  return makeErrAtNode(Ctx, D, "failed to create entity name for %s",
+                       D.getNameAsString().data());
 }
 
 // Translate a pointer type expression 'E' to a (set of) EntityPointerLevel(s)
@@ -58,32 +60,25 @@ static llvm::Error makeCreateEntityNameError(const 
NamedDecl *FailedDecl,
 //   Translate(cond ? p1[5] : p2)  -> {(p1, 2), (p2, 1)}
 //   Translate(&arr[5])            -> {(arr, 1)}
 class ssaf::EntityPointerLevelTranslator
-    : ConstStmtVisitor<EntityPointerLevelTranslator,
-                       Expected<EntityPointerLevelSet>> {
+    : public 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 EntityPointerLevel(E.getEntity(), E.getPointerLevel() + 1);
-  }
-
-  static EntityPointerLevel decrementPointerLevel(const EntityPointerLevel &E) 
{
-    assert(E.getPointerLevel() > 0);
-    return EntityPointerLevel(E.getEntity(), E.getPointerLevel() - 1);
+    return makeErrAtNode(Ctx, *E,
+                         "attempt to translate %s to EntityPointerLevels",
+                         E->getStmtClassName());
   }
 
   EntityPointerLevel createEntityPointerLevelFor(const EntityName &Name) {
-    return EntityPointerLevel(AddEntity(Name), 1);
+    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(hasPointerType(Ptr));
+    assert(hasPtrOrArrType(*Ptr));
 
     Expected<EntityPointerLevelSet> SubResult = Visit(Ptr);
     if (!SubResult)
@@ -102,6 +97,29 @@ class ssaf::EntityPointerLevelTranslator
       : 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 makeErrAtNode(
+          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 makeEntityNameErr(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) {
@@ -116,7 +134,7 @@ class ssaf::EntityPointerLevelTranslator
   Expected<EntityPointerLevelSet> VisitBinaryOperator(const BinaryOperator *E) 
{
     switch (E->getOpcode()) {
     case clang::BO_Add:
-      if (hasPointerType(E->getLHS()))
+      if (hasPtrOrArrType(*E->getLHS()))
         return Visit(E->getLHS());
       return Visit(E->getRHS());
     case clang::BO_Sub:
@@ -161,7 +179,7 @@ class ssaf::EntityPointerLevelTranslator
   // Translate((T*)base) -> Translate(p) if p has pointer type
   //                     -> {} otherwise
   Expected<EntityPointerLevelSet> VisitCastExpr(const CastExpr *E) {
-    if (hasPointerType(E->getSubExpr()))
+    if (hasPtrOrArrType(*E->getSubExpr()))
       return Visit(E->getSubExpr());
     return EntityPointerLevelSet{};
   }
@@ -214,14 +232,14 @@ class ssaf::EntityPointerLevelTranslator
   Expected<EntityPointerLevelSet> VisitDeclRefExpr(const DeclRefExpr *E) {
     if (auto EntityName = getEntityName(E->getDecl()))
       return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
-    return makeCreateEntityNameError(E->getDecl(), Ctx);
+    return makeEntityNameErr(Ctx, *E->getDecl());
   }
 
   // Translate({., ->}f) -> {(MemberDecl, 1)}
   Expected<EntityPointerLevelSet> VisitMemberExpr(const MemberExpr *E) {
     if (auto EntityName = getEntityName(E->getMemberDecl()))
       return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
-    return makeCreateEntityNameError(E->getMemberDecl(), Ctx);
+    return makeEntityNameErr(Ctx, *E->getMemberDecl());
   }
 
   Expected<EntityPointerLevelSet>
@@ -238,7 +256,62 @@ Expected<EntityPointerLevelSet> 
clang::ssaf::translateEntityPointerLevel(
   return Translator.translate(E);
 }
 
+/// Create an EntityPointerLevel from a ValueDecl of a pointer type.
+Expected<EntityPointerLevel> clang::ssaf::creatEntityPointerLevel(
+    const NamedDecl *D, ASTContext &Ctx,
+    std::function<EntityId(EntityName EN)> AddEntity, bool IsFunRet) {
+  EntityPointerLevelTranslator Translator(AddEntity, Ctx);
+
+  return Translator.translate(D, IsFunRet);
+}
+
+EntityPointerLevel
+clang::ssaf::incrementPointerLevel(const EntityPointerLevel &E) {
+  return EntityPointerLevelTranslator::incrementPointerLevel(E);
+}
+
 EntityPointerLevel clang::ssaf::buildEntityPointerLevel(EntityId Id,
                                                         unsigned PtrLv) {
   return EntityPointerLevel({Id, PtrLv});
 }
+
+// Writes an EntityPointerLevel as
+// Array [
+//   Object { "@" : [entity-id]},
+//   [pointer-level-integer]
+// ]
+llvm::json::Value clang::ssaf::entityPointerLevelToJSON(
+    const EntityPointerLevel &EPL, JSONFormat::EntityIdToJSONFn EntityId2JSON) 
{
+  return llvm::json::Array{EntityId2JSON(EPL.getEntity()),
+                           llvm::json::Value(EPL.getPointerLevel())};
+}
+
+Expected<EntityPointerLevel> clang::ssaf::entityPointerLevelFromJSON(
+    const llvm::json::Value &EPLData,
+    JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) {
+  auto *AsArr = EPLData.getAsArray();
+
+  if (!AsArr || AsArr->size() != 2)
+    return makeSawButExpectedError(
+        EPLData, "an array with exactly two elements representing "
+                 "EntityId and PointerLevel, respectively");
+
+  auto *EntityIdObj = (*AsArr)[0].getAsObject();
+
+  if (!EntityIdObj)
+    return makeSawButExpectedError((*AsArr)[0],
+                                   "an object representing EntityId");
+
+  Expected<EntityId> Id = EntityIdFromJSON(*EntityIdObj);
+
+  if (!Id)
+    return Id.takeError();
+
+  std::optional<uint64_t> PtrLv = (*AsArr)[1].getAsInteger();
+
+  if (!PtrLv)
+    return makeSawButExpectedError((*AsArr)[1],
+                                   "an integer representing PointerLevel");
+
+  return buildEntityPointerLevel(*Id, *PtrLv);
+}
diff --git 
a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
 
b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
index 84f3f9cbb3852..47e03476d1063 100644
--- 
a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
+++ 
b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
@@ -7,8 +7,9 @@
 
//===----------------------------------------------------------------------===//
 
 #include 
"clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include 
"clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
+#include 
"clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h"
 #include 
"clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
 #include 
"clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/JSON.h"
@@ -37,8 +38,7 @@ static Object serialize(const EntitySummary &S,
   Array UnsafeBuffersData;
 
   for (const auto &EPL : getUnsafeBuffers(SS))
-    UnsafeBuffersData.push_back(
-        Array{Fn(EPL.getEntity()), EPL.getPointerLevel()});
+    UnsafeBuffersData.push_back(entityPointerLevelToJSON(EPL, Fn));
   return Object{{SummarySerializationKey.data(), 
std::move(UnsafeBuffersData)}};
 }
 
@@ -48,28 +48,18 @@ deserializeImpl(const Object &Data, 
JSONFormat::EntityIdFromJSONFn Fn) {
       Data.getArray(SummarySerializationKey.data());
 
   if (!UnsafeBuffersData)
-    return llvm::createStringError("expected a json::Object with a key %s",
+    return makeSawButExpectedError(Object(Data), "an Object with a key %s",
                                    SummarySerializationKey.data());
 
   EntityPointerLevelSet EPLs;
 
   for (const auto &EltData : *UnsafeBuffersData) {
-    const Array *EltDataAsArr = EltData.getAsArray();
+    llvm::Expected<EntityPointerLevel> EPL =
+        entityPointerLevelFromJSON(EltData, Fn);
 
-    if (!EltDataAsArr || EltDataAsArr->size() != 2)
-      return llvm::createStringError("expected a json::Array of size 2");
-
-    const Object *IdData = (*EltDataAsArr)[0].getAsObject();
-    std::optional<uint64_t> PtrLvData = (*EltDataAsArr)[1].getAsInteger();
-
-    if (!IdData || !PtrLvData)
-      return llvm::createStringError("expected a json::Value of integer type");
-
-    llvm::Expected<EntityId> Id = Fn(*IdData);
-
-    if (!Id)
-      return Id.takeError();
-    EPLs.insert(buildEntityPointerLevel(Id.get(), *PtrLvData));
+    if (!EPL)
+      return EPL.takeError();
+    EPLs.insert(*EPL);
   }
   return std::make_unique<UnsafeBufferUsageEntitySummary>(
       buildUnsafeBufferUsageEntitySummary(std::move(EPLs)));
diff --git 
a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
 
b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
index b29eaa6b903d0..c412855028efb 100644
--- 
a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
+++ 
b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
@@ -9,14 +9,14 @@
 #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/Analysis/Analyses/UnsafeBufferUsage.h"
 #include 
"clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
 #include 
"clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
+#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>
 
diff --git 
a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test 
b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test
index 6a12949f3bbb4..55357acb1db08 100644
--- 
a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test
+++ 
b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test
@@ -6,13 +6,21 @@
 
 // RUN: not clang-ssaf-format -type=tu %S/Inputs/tu-summary-no-key.json 2>&1 \
 // RUN: | FileCheck %s --check-prefix=CHECK-NO-KEY
-// CHECK-NO-KEY: expected a json::Object with a key UnsafeBuffers
-
+// CHECK-NO-KEY: saw {
+// CHECK-NO-KEY:   "NotUnsafeBuffers": [
+// CHECK-NO-KEY:     [
+// CHECK-NO-KEY:       {
+// CHECK-NO-KEY:         "@": 0
+// CHECK-NO-KEY:       },
+// CHECK-NO-KEY:       1
+// CHECK-NO-KEY:     ]
+// CHECK-NO-KEY:   ]
+// CHECK-NO-KEY: } but expected an Object with a key UnsafeBuffers
 
 // RUN: not clang-ssaf-format -type=tu %S/Inputs/tu-summary-bad-element.json 
2>&1 \
 // RUN: | FileCheck %s --check-prefix=CHECK-BAD-ELEMENT
-// CHECK-BAD-ELEMENT: expected a json::Array of size 2
+// CHECK-BAD-ELEMENT: saw 42 but expected an array with exactly two elements 
representing EntityId and PointerLevel, respectively 
 
 // RUN: not clang-ssaf-format -type=tu %S/Inputs/tu-summary-bad-ptr-level.json 
2>&1 \
 // RUN:   | FileCheck %s --check-prefix=CHECK-BAD-PTR-LEVEL
-// CHECK-BAD-PTR-LEVEL: expected a json::Value of integer type
+// CHECK-BAD-PTR-LEVEL: saw "not-an-integer" but expected an integer 
representing PointerLevel

>From 11bc5e09f34e30a9e1972631afd0071eda080851 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <[email protected]>
Date: Mon, 13 Apr 2026 10:49:09 -0700
Subject: [PATCH 3/7] Update
 
clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Balázs Benics <[email protected]>
---
 .../Analyses/EntityPointerLevel/EntityPointerLevel.cpp       | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git 
a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
 
b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
index 78afdf956eb57..ebbb060605a6c 100644
--- 
a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
+++ 
b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
@@ -28,10 +28,7 @@ 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());
+  return llvm::createStringError((Fmt + " at %s").str().c_str(), Args..., 
LocStr.c_str());
 }
 
 static inline llvm::Error makeEntityNameErr(ASTContext &Ctx,

>From 3b4bddf9b30a1db848bec33878c636e9ad768c27 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <[email protected]>
Date: Mon, 13 Apr 2026 11:08:46 -0700
Subject: [PATCH 4/7] Update
 
clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Balázs Benics <[email protected]>
---
 .../Analyses/EntityPointerLevel/EntityPointerLevelFormat.h     | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git 
a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
 
b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
index 766e425338e96..574e8d27f8356 100644
--- 
a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
+++ 
b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
@@ -30,4 +30,5 @@ Expected<EntityPointerLevel>
 entityPointerLevelFromJSON(const llvm::json::Value &EPLData,
                            JSONFormat::EntityIdFromJSONFn EntityIdFromJSON);
 } // namespace clang::ssaf
-#endif // 
LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_ENTITYPOINTERLEVELFORMAT_H
\ No newline at end of file
+
+#endif // 
LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_ENTITYPOINTERLEVELFORMAT_H

>From ab919452501d381fa197dac2c2da7391526da6d4 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <[email protected]>
Date: Mon, 13 Apr 2026 11:09:16 -0700
Subject: [PATCH 5/7] Update
 
clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Balázs Benics <[email protected]>
---
 .../Analyses/EntityPointerLevel/EntityPointerLevelFormat.h       | 1 +
 1 file changed, 1 insertion(+)

diff --git 
a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
 
b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
index 574e8d27f8356..4d62a58adfdd7 100644
--- 
a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
+++ 
b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
@@ -8,6 +8,7 @@
 
 #ifndef 
LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_ENTITYPOINTERLEVELFORMAT_H
 #define 
LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_ENTITYPOINTERLEVELFORMAT_H
+
 #include 
"clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
 #include 
"clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
 

>From d44fad76c7012ee394c664568aad66061358e8b0 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <[email protected]>
Date: Mon, 13 Apr 2026 12:11:52 -0700
Subject: [PATCH 6/7] address comments

---
 .../EntityPointerLevel/EntityPointerLevel.h   | 14 +++----
 .../EntityPointerLevelFormat.h                |  8 ++--
 .../EntityPointerLevel/EntityPointerLevel.cpp | 39 ++++++++++---------
 .../tu-summary-serialization.test             | 22 +++++------
 4 files changed, 43 insertions(+), 40 deletions(-)

diff --git 
a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h
 
b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h
index 6f5687fd186c9..ed8400f65e310 100644
--- 
a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h
+++ 
b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h
@@ -12,6 +12,7 @@
 #include "clang/AST/Expr.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
+#include "llvm/ADT/STLFunctionalExtras.h"
 #include <set>
 
 namespace clang::ssaf {
@@ -92,23 +93,22 @@ using EntityPointerLevelSet =
 /// \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);
+llvm::Expected<EntityPointerLevelSet> translateEntityPointerLevel(
+    const Expr *E, ASTContext &Ctx,
+    llvm::function_ref<EntityId(EntityName EN)> AddEntity);
 
 EntityPointerLevel buildEntityPointerLevel(EntityId, unsigned);
 
 /// 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 ND the NamedDecl of a pointer/array type.
 /// \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,
+creatEntityPointerLevel(const NamedDecl *ND,
+                        llvm::function_ref<EntityId(EntityName EN)> AddEntity,
                         bool IsFunRet = false);
 
 /// Creates a new EntityPointerLevel (EPL) from `E` by incrementing `E`'s
diff --git 
a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
 
b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
index 4d62a58adfdd7..4e7a9eff32d5c 100644
--- 
a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
+++ 
b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
@@ -12,15 +12,17 @@
 #include 
"clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
 #include 
"clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
 
+namespace {
 template <typename... Ts>
-static inline llvm::Error makeSawButExpectedError(const llvm::json::Value &Saw,
-                                                  llvm::StringRef Expected,
-                                                  const Ts &...ExpectedArgs) {
+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
 
 namespace clang::ssaf {
 llvm::json::Value
diff --git 
a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
 
b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
index ebbb060605a6c..22151c2de1c84 100644
--- 
a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
+++ 
b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
@@ -18,25 +18,25 @@
 using namespace clang;
 using namespace ssaf;
 
-template <typename DeclOrExpr>
-static bool hasPtrOrArrType(const DeclOrExpr &E) {
-  return llvm::isa<PointerType>(E.getType().getCanonicalType()) ||
-         llvm::isa<ArrayType>(E.getType().getCanonicalType());
+namespace {
+template <typename DeclOrExpr> bool hasPtrOrArrType(const DeclOrExpr *E) {
+  return llvm::isa<PointerType, ArrayType>(E->getType().getCanonicalType());
 }
 
 template <typename NodeTy, typename... Ts>
-static inline llvm::Error makeErrAtNode(ASTContext &Ctx, const NodeTy &N,
-                                        StringRef Fmt, const Ts &...Args) {
+llvm::Error makeErrAtNode(ASTContext &Ctx, const NodeTy &N, StringRef Fmt,
+                          const Ts &...Args) {
   std::string LocStr = N.getBeginLoc().printToString(Ctx.getSourceManager());
   return llvm::createStringError((Fmt + " at %s").str().c_str(), Args..., 
LocStr.c_str());
 }
 
-static inline llvm::Error makeEntityNameErr(ASTContext &Ctx,
-                                            const NamedDecl &D) {
+llvm::Error makeEntityNameErr(ASTContext &Ctx, const NamedDecl &D) {
   return makeErrAtNode(Ctx, D, "failed to create entity name for %s",
                        D.getNameAsString().data());
 }
+} // 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
@@ -56,9 +56,9 @@ static inline llvm::Error makeEntityNameErr(ASTContext &Ctx,
 //   Translate(arr[5])             -> {(arr, 2)}
 //   Translate(cond ? p1[5] : p2)  -> {(p1, 2), (p2, 1)}
 //   Translate(&arr[5])            -> {(arr, 1)}
-class ssaf::EntityPointerLevelTranslator
-    : public ConstStmtVisitor<EntityPointerLevelTranslator,
-                              Expected<EntityPointerLevelSet>> {
+class EntityPointerLevelTranslator
+    : ConstStmtVisitor<EntityPointerLevelTranslator,
+                       Expected<EntityPointerLevelSet>> {
   friend class StmtVisitorBase;
 
   // Fallback method for all unsupported expression kind:
@@ -75,7 +75,7 @@ class ssaf::EntityPointerLevelTranslator
   // The common helper function for Translate(*base):
   // Translate(*base) -> Translate(base) with .pointerLevel + 1
   Expected<EntityPointerLevelSet> translateDereferencePointer(const Expr *Ptr) 
{
-    assert(hasPtrOrArrType(*Ptr));
+    assert(hasPtrOrArrType(Ptr));
 
     Expected<EntityPointerLevelSet> SubResult = Visit(Ptr);
     if (!SubResult)
@@ -131,7 +131,7 @@ class ssaf::EntityPointerLevelTranslator
   Expected<EntityPointerLevelSet> VisitBinaryOperator(const BinaryOperator *E) 
{
     switch (E->getOpcode()) {
     case clang::BO_Add:
-      if (hasPtrOrArrType(*E->getLHS()))
+      if (hasPtrOrArrType(E->getLHS()))
         return Visit(E->getLHS());
       return Visit(E->getRHS());
     case clang::BO_Sub:
@@ -176,7 +176,7 @@ class ssaf::EntityPointerLevelTranslator
   // Translate((T*)base) -> Translate(p) if p has pointer type
   //                     -> {} otherwise
   Expected<EntityPointerLevelSet> VisitCastExpr(const CastExpr *E) {
-    if (hasPtrOrArrType(*E->getSubExpr()))
+    if (hasPtrOrArrType(E->getSubExpr()))
       return Visit(E->getSubExpr());
     return EntityPointerLevelSet{};
   }
@@ -244,10 +244,11 @@ class ssaf::EntityPointerLevelTranslator
     return Visit(S->getSourceExpr());
   }
 };
+} // namespace clang::ssaf
 
 Expected<EntityPointerLevelSet> clang::ssaf::translateEntityPointerLevel(
     const Expr *E, ASTContext &Ctx,
-    std::function<EntityId(EntityName EN)> AddEntity) {
+    llvm::function_ref<EntityId(EntityName EN)> AddEntity) {
   EntityPointerLevelTranslator Translator(AddEntity, Ctx);
 
   return Translator.translate(E);
@@ -255,11 +256,11 @@ Expected<EntityPointerLevelSet> 
clang::ssaf::translateEntityPointerLevel(
 
 /// Create an EntityPointerLevel from a ValueDecl of a pointer type.
 Expected<EntityPointerLevel> clang::ssaf::creatEntityPointerLevel(
-    const NamedDecl *D, ASTContext &Ctx,
-    std::function<EntityId(EntityName EN)> AddEntity, bool IsFunRet) {
-  EntityPointerLevelTranslator Translator(AddEntity, Ctx);
+    const NamedDecl *ND, llvm::function_ref<EntityId(EntityName EN)> AddEntity,
+    bool IsFunRet) {
+  EntityPointerLevelTranslator Translator(AddEntity, ND->getASTContext());
 
-  return Translator.translate(D, IsFunRet);
+  return Translator.translate(ND, IsFunRet);
 }
 
 EntityPointerLevel
diff --git 
a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test 
b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test
index 55357acb1db08..ff13aa7f8ca27 100644
--- 
a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test
+++ 
b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test
@@ -5,17 +5,17 @@
 // Negative tests:
 
 // RUN: not clang-ssaf-format -type=tu %S/Inputs/tu-summary-no-key.json 2>&1 \
-// RUN: | FileCheck %s --check-prefix=CHECK-NO-KEY
-// CHECK-NO-KEY: saw {
-// CHECK-NO-KEY:   "NotUnsafeBuffers": [
-// CHECK-NO-KEY:     [
-// CHECK-NO-KEY:       {
-// CHECK-NO-KEY:         "@": 0
-// CHECK-NO-KEY:       },
-// CHECK-NO-KEY:       1
-// CHECK-NO-KEY:     ]
-// CHECK-NO-KEY:   ]
-// CHECK-NO-KEY: } but expected an Object with a key UnsafeBuffers
+// RUN: | FileCheck %s --check-prefix=CHECK-MISSING-KEY
+// CHECK-MISSING-KEY: saw {
+// CHECK-MISSING-KEY-NEXT:   "NotUnsafeBuffers": [
+// CHECK-MISSING-KEY-NEXT:     [
+// CHECK-MISSING-KEY-NEXT:       {
+// CHECK-MISSING-KEY-NEXT:         "@": 0
+// CHECK-MISSING-KEY-NEXT:       },
+// CHECK-MISSING-KEY-NEXT:       1
+// CHECK-MISSING-KEY-NEXT:     ]
+// CHECK-MISSING-KEY-NEXT:   ]
+// CHECK-MISSING-KEY-NEXT: } but expected an Object with a key UnsafeBuffers
 
 // RUN: not clang-ssaf-format -type=tu %S/Inputs/tu-summary-bad-element.json 
2>&1 \
 // RUN: | FileCheck %s --check-prefix=CHECK-BAD-ELEMENT

>From 39fbbba0f8753a295c32cb17ad82308722d0655d Mon Sep 17 00:00:00 2001
From: Ziqing Luo <[email protected]>
Date: Mon, 13 Apr 2026 14:33:15 -0700
Subject: [PATCH 7/7] fix format

---
 .../Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h          | 6 ------
 .../Analyses/EntityPointerLevel/EntityPointerLevel.cpp      | 3 ++-
 2 files changed, 2 insertions(+), 7 deletions(-)

diff --git 
a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
 
b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
index ea01a0f0b19f7..27bda9f773085 100644
--- 
a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
+++ 
b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
@@ -14,14 +14,8 @@
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
 
 namespace clang::ssaf {
-<<<<<<< users/ziqingluo/PR-172429193-pre-2
 /// An UnsafeBufferUsageEntitySummary contains a set of EntityPointerLevels
 /// extracted from unsafe buffer pointers contributed by an entity.
-=======
-
-/// An UnsafeBufferUsageEntitySummary is an immutable set of unsafe buffers, in
-/// the form of EntityPointerLevel.
->>>>>>> main
 class UnsafeBufferUsageEntitySummary final : public EntitySummary {
   const EntityPointerLevelSet UnsafeBuffers;
 
diff --git 
a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
 
b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
index 22151c2de1c84..7e4200467ce9e 100644
--- 
a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
+++ 
b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
@@ -27,7 +27,8 @@ template <typename NodeTy, typename... Ts>
 llvm::Error makeErrAtNode(ASTContext &Ctx, const NodeTy &N, StringRef Fmt,
                           const Ts &...Args) {
   std::string LocStr = N.getBeginLoc().printToString(Ctx.getSourceManager());
-  return llvm::createStringError((Fmt + " at %s").str().c_str(), Args..., 
LocStr.c_str());
+  return llvm::createStringError((Fmt + " at %s").str().c_str(), Args...,
+                                 LocStr.c_str());
 }
 
 llvm::Error makeEntityNameErr(ASTContext &Ctx, const NamedDecl &D) {

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to