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

Ziqing Luo via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Wed Apr 1 23:27:57 PDT 2026


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

>From 6d7c95700123106b893280bfe4879223c20731e9 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 1/3] [NFC][SSAF][UnsafeBufferUsage] Separate
 EntityPointerLevel and UnsafeBufferUsage

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

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

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

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

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

---
 .../EntityPointerLevel/EntityPointerLevel.cpp | 32 ++++++-------------
 1 file changed, 10 insertions(+), 22 deletions(-)

diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
index b5bf17530f9f2..38635d7d87d22 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
@@ -1,4 +1,4 @@
-//===----------------- EntityPointerLevel.cpp -------------------*- C++ -*-===//
+//===- EntityPointerLevel.cpp ----------------------------------*- C++ -*-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -10,13 +10,12 @@
 #include "clang/AST/DeclCXX.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
-#include "llvm/Support/JSON.h"
 #include <optional>
 
-namespace {
 using namespace clang;
+
 template <typename NodeTy, typename... Ts>
-static inline llvm::Error strErrAtNode(ASTContext &Ctx, const NodeTy &N,
+static inline llvm::Error makeErrAtNode(ASTContext &Ctx, const NodeTy &N,
                                        StringRef Fmt, const Ts &...Args) {
   std::string LocStr = N.getBeginLoc().printToString(Ctx.getSourceManager());
   llvm::SmallVector<char> FmtData;
@@ -25,9 +24,9 @@ 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,
+static inline llvm::Error makeEntityNameErr(ASTContext &Ctx,
                                            const NamedDecl &D) {
-  return strErrAtNode(Ctx, D, "failed to create entity name for %s",
+  return makeErrAtNode(Ctx, D, "failed to create entity name for %s",
                       D.getNameAsString().data());
 }
 
@@ -37,17 +36,6 @@ static bool hasPtrOrArrType(const DeclOrExpr &E) {
          llvm::isa<ArrayType>(E.getType().getCanonicalType());
 }
 
-template <typename... Ts>
-static inline llvm::Error makeErrorSawButExpected(const llvm::json::Value &Saw,
-                                                  llvm::StringRef Expected,
-                                                  const Ts &...ExpectedArgs) {
-  return llvm::createStringError(
-      ("saw %s but expected " + Expected).str().c_str(),
-      llvm::formatv("{0:2}", Saw).str().data(), Expected.data(),
-      ExpectedArgs...);
-}
-} // namespace
-
 namespace clang::ssaf {
 // Translate a pointer type expression 'E' to a (set of) EntityPointerLevel(s)
 // associated with the declared type of the base address of `E`. If the base
@@ -75,7 +63,7 @@ class EntityPointerLevelTranslator
 
   // Fallback method for all unsupported expression kind:
   llvm::Error fallback(const Stmt *E) {
-    return strErrAtNode(Ctx, *E,
+    return makeErrAtNode(Ctx, *E,
                         "attempt to translate %s to EntityPointerLevels",
                         E->getStmtClassName());
   }
@@ -108,7 +96,7 @@ class EntityPointerLevelTranslator
   Expected<EntityPointerLevelSet> translate(const Expr *E) { return Visit(E); }
   Expected<EntityPointerLevel> translate(const NamedDecl *D, bool IsRet) {
     if (IsRet && !isa<FunctionDecl>(D))
-      return strErrAtNode(
+      return makeErrAtNode(
           Ctx, *D,
           "attempt to call getEntityNameForReturn on a NamedDecl of %s kind",
           D->getDeclKindName());
@@ -118,7 +106,7 @@ class EntityPointerLevelTranslator
               : getEntityName(D);
     if (EN)
       return createEntityPointerLevelFor(*EN);
-    return entityNameErrFor(Ctx, *D);
+    return makeEntityNameErr(Ctx, *D);
   }
 
   static EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E) {
@@ -241,14 +229,14 @@ class EntityPointerLevelTranslator
   Expected<EntityPointerLevelSet> VisitDeclRefExpr(const DeclRefExpr *E) {
     if (auto EntityName = getEntityName(E->getDecl()))
       return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
-    return entityNameErrFor(Ctx, *E->getDecl());
+    return makeEntityNameErr(Ctx, *E->getDecl());
   }
 
   // Translate({., ->}f) -> {(MemberDecl, 1)}
   Expected<EntityPointerLevelSet> VisitMemberExpr(const MemberExpr *E) {
     if (auto EntityName = getEntityName(E->getMemberDecl()))
       return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
-    return entityNameErrFor(Ctx, *E->getMemberDecl());
+    return makeEntityNameErr(Ctx, *E->getMemberDecl());
   }
 
   // Translate(`DefaultArg`) -> Translate(`DefaultArg->getExpr()`)



More information about the llvm-branch-commits mailing list