https://github.com/ziqingluo-90 created 
https://github.com/llvm/llvm-project/pull/191518

The first commit simply reverts the reversion.
The second commit fixes the issue by removing the name qualifier on the class.

>From 4e2adbf269f557d7e6fad0e8bf37b9af80f82c9c Mon Sep 17 00:00:00 2001
From: Ziqing Luo <[email protected]>
Date: Fri, 10 Apr 2026 13:43:11 -0700
Subject: [PATCH 1/2] Revert "Revert "[NFC][SSAF] Move EntityPointerLevel to a
 separate folder" (#191481)"

This reverts commit 2ae4ddd720a9702e8aac6094c3930bfeac8fc325.
---
 .../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 b6d0b59cf51daea221705010587bff871819db58 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <[email protected]>
Date: Fri, 10 Apr 2026 13:45:17 -0700
Subject: [PATCH 2/2] Remove name qualifiers on a class, using namespace
 instead. This should fix the bot failure:
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

    FAILED: 
clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp:61:5:
 error: qualified name does not name a class before ‘:’ token
       61 |     : ConstStmtVisitor<EntityPointerLevelTranslator,
          |     ^
---
 .../Analyses/EntityPointerLevel/EntityPointerLevel.cpp        | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git 
a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
 
b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
index 49a135b13877a..d7b8aebef55aa 100644
--- 
a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
+++ 
b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
@@ -38,6 +38,7 @@ static llvm::Error makeCreateEntityNameError(const NamedDecl 
*FailedDecl,
       FailedDecl->getNameAsString().c_str(), LocStr.c_str());
 }
 
+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
@@ -57,7 +58,7 @@ static llvm::Error makeCreateEntityNameError(const NamedDecl 
*FailedDecl,
 //   Translate(arr[5])             -> {(arr, 2)}
 //   Translate(cond ? p1[5] : p2)  -> {(p1, 2), (p2, 1)}
 //   Translate(&arr[5])            -> {(arr, 1)}
-class ssaf::EntityPointerLevelTranslator
+class EntityPointerLevelTranslator
     : ConstStmtVisitor<EntityPointerLevelTranslator,
                        Expected<EntityPointerLevelSet>> {
   friend class StmtVisitorBase;
@@ -229,6 +230,7 @@ class ssaf::EntityPointerLevelTranslator
     return Visit(S->getSourceExpr());
   }
 };
+} // namespace clang::ssaf
 
 Expected<EntityPointerLevelSet> clang::ssaf::translateEntityPointerLevel(
     const Expr *E, ASTContext &Ctx,

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

Reply via email to