[llvm-branch-commits] [clang] [SSAF][WPA] Add no-op PointerFlow and UnsafeBufferUsage analysis (PR #193089)
Ziqing Luo via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed Apr 22 11:52:25 PDT 2026
https://github.com/ziqingluo-90 updated https://github.com/llvm/llvm-project/pull/193089
>From 4e2adbf269f557d7e6fad0e8bf37b9af80f82c9c Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Fri, 10 Apr 2026 13:43:11 -0700
Subject: [PATCH 1/5] 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 <ziqing_luo at apple.com>
Date: Fri, 10 Apr 2026 13:45:17 -0700
Subject: [PATCH 2/5] 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,
>From 915984c69b277228cecac6cb345624939ce7daef Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Fri, 17 Apr 2026 12:22:03 -0700
Subject: [PATCH 3/5] [SSAF][WPA] Add no-op PointerFlow and UnsafeBufferUsage
analysis
We need no-op PointerFlow and UnsafeBufferUsage analyses for the
analysis that depends on their summary data.
Refactored PointerFlow and UnsafeBufferUsage serialization for code
sharing.
rdar://174874942
---
.../EntityPointerLevelFormat.h | 9 ++
.../PointerFlow/PointerFlowAnalysis.h | 40 ++++++
.../Analyses/PointerFlow/PointerFlowFormat.h | 35 +++++
.../UnsafeBufferUsageAnalysis.h | 40 ++++++
.../SSAFBuiltinForceLinker.h | 8 ++
.../Analyses/CMakeLists.txt | 4 +-
.../EntityPointerLevel/EntityPointerLevel.cpp | 26 ++++
.../Analyses/PointerFlow/PointerFlow.cpp | 95 +++++++------
.../PointerFlow/PointerFlowAnalysis.cpp | 116 ++++++++++++++++
.../PointerFlow/PointerFlowExtractor.cpp | 2 +-
.../Analyses/SSAFAnalysesCommon.cpp | 12 ++
.../Analyses/SSAFAnalysesCommon.h | 11 +-
.../UnsafeBufferUsage/UnsafeBufferUsage.cpp | 24 +---
.../UnsafeBufferUsageAnalysis.cpp | 121 +++++++++++++++++
.../UnsafeBufferUsageExtractor.cpp | 2 +-
.../Inputs/wpa-result-bad-edges.json | 30 ++++
.../PointerFlow/Inputs/wpa-result-bad-id.json | 14 ++
.../PointerFlow/Inputs/wpa-result-empty.json | 11 ++
.../PointerFlow/Inputs/wpa-result-no-key.json | 11 ++
.../Inputs/wpa-result-odd-count.json | 29 ++++
.../PointerFlow/Inputs/wpa-result.json | 128 ++++++++++++++++++
.../PointerFlow/tu-summary-serialization.test | 7 +-
.../PointerFlow/wpa-result-serialization.test | 32 +++++
.../Inputs/wpa-result-bad-epls.json | 30 ++++
.../Inputs/wpa-result-bad-id.json | 14 ++
.../Inputs/wpa-result-empty.json | 11 ++
.../Inputs/wpa-result-no-key.json | 11 ++
.../Inputs/wpa-result-odd-count.json | 29 ++++
.../UnsafeBufferUsage/Inputs/wpa-result.json | 86 ++++++++++++
.../tu-summary-serialization.test | 11 +-
.../wpa-result-serialization.test | 32 +++++
31 files changed, 951 insertions(+), 80 deletions(-)
create mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowAnalysis.h
create mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowFormat.h
create mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageAnalysis.h
create mode 100644 clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowAnalysis.cpp
create mode 100644 clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageAnalysis.cpp
create mode 100644 clang/test/Analysis/Scalable/PointerFlow/Inputs/wpa-result-bad-edges.json
create mode 100644 clang/test/Analysis/Scalable/PointerFlow/Inputs/wpa-result-bad-id.json
create mode 100644 clang/test/Analysis/Scalable/PointerFlow/Inputs/wpa-result-empty.json
create mode 100644 clang/test/Analysis/Scalable/PointerFlow/Inputs/wpa-result-no-key.json
create mode 100644 clang/test/Analysis/Scalable/PointerFlow/Inputs/wpa-result-odd-count.json
create mode 100644 clang/test/Analysis/Scalable/PointerFlow/Inputs/wpa-result.json
create mode 100644 clang/test/Analysis/Scalable/PointerFlow/wpa-result-serialization.test
create mode 100644 clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/wpa-result-bad-epls.json
create mode 100644 clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/wpa-result-bad-id.json
create mode 100644 clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/wpa-result-empty.json
create mode 100644 clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/wpa-result-no-key.json
create mode 100644 clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/wpa-result-odd-count.json
create mode 100644 clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/wpa-result.json
create mode 100644 clang/test/Analysis/Scalable/UnsafeBufferUsage/wpa-result-serialization.test
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
index 7b125edb1fb7f..13ecd880001e6 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
@@ -11,6 +11,7 @@
#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
+#include "llvm/ADT/iterator_range.h"
namespace clang::ssaf {
llvm::json::Value
@@ -20,6 +21,14 @@ entityPointerLevelToJSON(const EntityPointerLevel &EPL,
Expected<EntityPointerLevel>
entityPointerLevelFromJSON(const llvm::json::Value &EPLData,
JSONFormat::EntityIdFromJSONFn EntityIdFromJSON);
+
+llvm::json::Array entityPointerLevelSetToJSON(
+ llvm::iterator_range<EntityPointerLevelSet::const_iterator> EPLs,
+ JSONFormat::EntityIdToJSONFn EntityId2JSON);
+
+Expected<EntityPointerLevelSet>
+entityPointerLevelSetFromJSON(const llvm::json::Array &EPLsData,
+ JSONFormat::EntityIdFromJSONFn EntityIdFromJSON);
} // namespace clang::ssaf
#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_ENTITYPOINTERLEVELFORMAT_H
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowAnalysis.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowAnalysis.h
new file mode 100644
index 0000000000000..85694c779adf4
--- /dev/null
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowAnalysis.h
@@ -0,0 +1,40 @@
+//===- PointerFlowAnalysis.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
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines PointerFlowAnalysisResult, the whole-program analysis result type
+// for PointerFlowAnalysis.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_POINTERFLOW_POINTERFLOWANALYSIS_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_POINTERFLOW_POINTERFLOWANALYSIS_H
+
+#include "clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisName.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisResult.h"
+#include "llvm/ADT/StringRef.h"
+#include <map>
+
+namespace clang::ssaf {
+
+inline constexpr llvm::StringLiteral PointerFlowAnalysisResultName =
+ "PointerFlowAnalysisResult";
+
+struct PointerFlowAnalysisResult final : AnalysisResult {
+ static AnalysisName analysisName() {
+ return AnalysisName(PointerFlowAnalysisResultName.str());
+ }
+
+ /// Whole-program map from EntityIds to their EdgeSets.
+ std::map<EntityId, EdgeSet> Edges;
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_POINTERFLOW_POINTERFLOWANALYSIS_H
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowFormat.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowFormat.h
new file mode 100644
index 0000000000000..86bf1de4aaea2
--- /dev/null
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowFormat.h
@@ -0,0 +1,35 @@
+//===- PointerFlowFormat.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
+//
+//===----------------------------------------------------------------------===//
+//
+// JSON serialization helpers for EdgeSet (PointerFlow edge maps).
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_POINTERFLOW_POINTERFLOWFORMAT_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_POINTERFLOW_POINTERFLOWFORMAT_H
+
+#include "clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
+#include "llvm/ADT/iterator_range.h"
+
+namespace clang::ssaf {
+
+/// Serialize an EdgeSet as an array of arrays of EntityPointerLevels:
+/// [ [lhs, rhs, rhs, ...], [lhs, rhs, rhs, ...], ... ]
+llvm::json::Array
+edgeSetToJSON(llvm::iterator_range<EdgeSet::const_iterator> Edges,
+ JSONFormat::EntityIdToJSONFn IdToJSON);
+
+/// Deserialize an EdgeSet from the array format produced by `edgeSetToJSON`.
+llvm::Expected<EdgeSet>
+edgeSetFromJSON(const llvm::json::Array &EdgesData,
+ JSONFormat::EntityIdFromJSONFn IdFromJSON);
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_POINTERFLOW_POINTERFLOWFORMAT_H
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageAnalysis.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageAnalysis.h
new file mode 100644
index 0000000000000..aba566498b44b
--- /dev/null
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageAnalysis.h
@@ -0,0 +1,40 @@
+//===- UnsafeBufferUsageAnalysis.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
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines UnsafeBufferUsageAnalysisResult, the whole-program analysis result
+// type for UnsafeBufferUsageAnalysis.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEANALYSIS_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEANALYSIS_H
+
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisName.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisResult.h"
+#include "llvm/ADT/StringRef.h"
+#include <map>
+
+namespace clang::ssaf {
+
+inline constexpr llvm::StringLiteral UnsafeBufferUsageAnalysisResultName =
+ "UnsafeBufferUsageAnalysisResult";
+
+struct UnsafeBufferUsageAnalysisResult final : AnalysisResult {
+ static AnalysisName analysisName() {
+ return AnalysisName(UnsafeBufferUsageAnalysisResultName.str());
+ }
+
+ /// Whole-program set of unsafe buffer pointers:
+ std::map<EntityId, EntityPointerLevelSet> UnsafeBuffers;
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEANALYSIS_H
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h b/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
index 8419ad23619f7..26b1fe4a47840 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
@@ -45,6 +45,10 @@ extern volatile int UnsafeBufferUsageTUSummaryExtractorAnchorSource;
UnsafeBufferUsageTUSummaryExtractorAnchorDestination =
UnsafeBufferUsageTUSummaryExtractorAnchorSource;
+extern volatile int UnsafeBufferUsageAnalysisAnchorSource;
+[[maybe_unused]] static int UnsafeBufferUsageAnalysisAnchorDestination =
+ UnsafeBufferUsageAnalysisAnchorSource;
+
// This anchor is used to force the linker to link the PointerFlow
// JSONFormat registration:
extern volatile int PointerFlowSSAFJSONFormatAnchorSource;
@@ -57,6 +61,10 @@ extern volatile int PointerFlowTUSummaryExtractorAnchorSource;
[[maybe_unused]] static int PointerFlowTUSummaryExtractorAnchorDestination =
PointerFlowTUSummaryExtractorAnchorSource;
+extern volatile int PointerFlowAnalysisAnchorSource;
+[[maybe_unused]] static int PointerFlowAnalysisAnchorDestination =
+ PointerFlowAnalysisAnchorSource;
+
// This anchor is used to force the linker to link the CallGraphExtractor.
extern volatile int CallGraphExtractorAnchorSource;
[[maybe_unused]] static int CallGraphExtractorAnchorDestination =
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
index 798d06a4c9107..5f898ae10dc26 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
@@ -7,9 +7,11 @@ add_clang_library(clangScalableStaticAnalysisFrameworkAnalyses
CallGraph/CallGraphJSONFormat.cpp
EntityPointerLevel/EntityPointerLevel.cpp
PointerFlow/PointerFlow.cpp
- PointerFlow/PointerFlowExtractor.cpp
+ PointerFlow/PointerFlowAnalysis.cpp
+ PointerFlow/PointerFlowExtractor.cpp
SSAFAnalysesCommon.cpp
UnsafeBufferUsage/UnsafeBufferUsage.cpp
+ UnsafeBufferUsage/UnsafeBufferUsageAnalysis.cpp
UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
LINK_LIBS
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
index 044f02ee25a24..fb47fd76241f0 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
@@ -302,3 +302,29 @@ Expected<EntityPointerLevel> clang::ssaf::entityPointerLevelFromJSON(
return buildEntityPointerLevel(*Id, *PtrLv);
}
+
+llvm::json::Array clang::ssaf::entityPointerLevelSetToJSON(
+ llvm::iterator_range<EntityPointerLevelSet::const_iterator> EPLs,
+ JSONFormat::EntityIdToJSONFn EntityId2JSON) {
+ llvm::json::Array Result;
+
+ for (const auto &EPL : EPLs)
+ Result.push_back(entityPointerLevelToJSON(EPL, EntityId2JSON));
+ return Result;
+}
+
+Expected<EntityPointerLevelSet> clang::ssaf::entityPointerLevelSetFromJSON(
+ const llvm::json::Array &EPLsData,
+ JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) {
+ EntityPointerLevelSet EPLs;
+
+ for (const auto &EltData : EPLsData) {
+ llvm::Expected<EntityPointerLevel> EPL =
+ entityPointerLevelFromJSON(EltData, EntityIdFromJSON);
+
+ if (!EPL)
+ return EPL.takeError();
+ EPLs.insert(*EPL);
+ }
+ return EPLs;
+}
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.cpp
index dc347f4dc4086..ce45f4167016f 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.cpp
@@ -8,10 +8,9 @@
#include "clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h"
#include "SSAFAnalysesCommon.h"
-#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowFormat.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
-#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/JSON.h"
@@ -36,51 +35,27 @@ ssaf::getEdges(const PointerFlowEntitySummary &Sum) {
return Sum.Edges;
}
-// Writes the 'Edges' map as an array of array of EntityPointerLevels:
-// Array [
-// Array [ [lhs-node], [rhs-node], [rhs-node], ...]
-// Array [ [lhs-node], [rhs-node], [rhs-node], ...]
-// ...
-// ]
-static llvm::json::Object
-summaryToJSON(const EntitySummary &ES,
- JSONFormat::EntityIdToJSONFn EntityId2JSON) {
+Array ssaf::edgeSetToJSON(
+ llvm::iterator_range<EdgeSet::const_iterator> Edges,
+ JSONFormat::EntityIdToJSONFn EntityId2JSON) {
Array EdgesData;
- for (const auto &Entry :
- getEdges(static_cast<const PointerFlowEntitySummary &>(ES))) {
- Array EdgesEntryData;
- EntityPointerLevel LHS = Entry.first;
-
- EdgesEntryData.push_back(entityPointerLevelToJSON(LHS, EntityId2JSON));
- // Add to nodes:
- for (const auto &RHS : Entry.second)
- EdgesEntryData.push_back(entityPointerLevelToJSON(RHS, EntityId2JSON));
- EdgesData.push_back(Value(std::move(EdgesEntryData)));
+ for (const auto &[LHS, RHSSet] : Edges) {
+ Array EdgeEntry;
+ EdgeEntry.push_back(entityPointerLevelToJSON(LHS, EntityId2JSON));
+ for (const auto &RHS : RHSSet)
+ EdgeEntry.push_back(entityPointerLevelToJSON(RHS, EntityId2JSON));
+ EdgesData.push_back(Value(std::move(EdgeEntry)));
}
-
- Object Data;
-
- Data[PointerFlowKey] = Value(std::move(EdgesData));
- return Data;
+ return EdgesData;
}
-static llvm::Expected<std::unique_ptr<EntitySummary>>
-summaryFromJSON(const Object &Data, EntityIdTable &,
- JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) {
- const Value *EdgesData = Data.get(PointerFlowKey);
-
- if (!EdgesData)
- return makeSawButExpectedError(
- Object(Data), "a JSON object with the key: %s", PointerFlowKey);
-
+llvm::Expected<EdgeSet>
+ssaf::edgeSetFromJSON(const Array &EdgesData,
+ JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) {
EdgeSet Edges;
- const auto *EdgesDataAsArr = EdgesData->getAsArray();
- if (!EdgesDataAsArr)
- return makeSawButExpectedError(
- *EdgesData, "a JSON array of array of EntityPointerLevels");
- for (const auto &EdgesEntryData : *EdgesDataAsArr) {
+ for (const auto &EdgesEntryData : EdgesData) {
const auto *EPLArray = EdgesEntryData.getAsArray();
if (!EPLArray || EPLArray->size() <= 1)
@@ -101,8 +76,46 @@ summaryFromJSON(const Object &Data, EntityIdTable &,
Edges[*SrcEPL].insert(*EPL);
}
}
+ return Edges;
+}
+
+// Writes the 'Edges' map as an array of array of EntityPointerLevels:
+// Array [
+// Array [ [lhs-node], [rhs-node], [rhs-node], ...]
+// Array [ [lhs-node], [rhs-node], [rhs-node], ...]
+// ...
+// ]
+static llvm::json::Object
+summaryToJSON(const EntitySummary &ES,
+ JSONFormat::EntityIdToJSONFn EntityId2JSON) {
+ Object Data;
+ Data[PointerFlowKey] = Value(
+ edgeSetToJSON(getEdges(static_cast<const PointerFlowEntitySummary &>(ES)),
+ EntityId2JSON));
+ return Data;
+}
+
+static llvm::Expected<std::unique_ptr<EntitySummary>>
+summaryFromJSON(const Object &Data, EntityIdTable &,
+ JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) {
+ const Value *EdgesData = Data.get(PointerFlowKey);
+
+ if (!EdgesData)
+ return makeSawButExpectedError(Data, "a JSON object with the key: %s",
+ PointerFlowKey);
+
+ const auto *EdgesDataAsArr = EdgesData->getAsArray();
+
+ if (!EdgesDataAsArr)
+ return makeSawButExpectedError(
+ *EdgesData, "a JSON array of array of EntityPointerLevels");
+
+ auto Edges = edgeSetFromJSON(*EdgesDataAsArr, EntityIdFromJSON);
+
+ if (!Edges)
+ return Edges.takeError();
return std::make_unique<PointerFlowEntitySummary>(
- buildPointerFlowEntitySummary(std::move(Edges)));
+ buildPointerFlowEntitySummary(std::move(*Edges)));
}
struct PointerFlowJSONFormatInfo : JSONFormat::FormatInfo {
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowAnalysis.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowAnalysis.cpp
new file mode 100644
index 0000000000000..c6f2f6ddc7288
--- /dev/null
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowAnalysis.cpp
@@ -0,0 +1,116 @@
+//===- PointerFlowAnalysis.cpp - WPA for PointerFlow ----------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+// PointerFlowAnalysis is a noop analysis.
+//
+// PointerFlowAnalysisResult is a map from EntityIds to
+// EdgeSets.
+//===----------------------------------------------------------------------===//
+
+#include "SSAFAnalysesCommon.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowAnalysis.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowFormat.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisRegistry.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/SummaryAnalysis.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/JSON.h"
+#include <memory>
+
+using namespace clang::ssaf;
+using namespace llvm;
+
+namespace {
+
+// Serialized as a flat array of alternating [EntityId, EdgesArray, ...] pairs.
+json::Object
+serializePointerFlowAnalysisResult(const PointerFlowAnalysisResult &R,
+ JSONFormat::EntityIdToJSONFn IdToJSON) {
+ json::Array Content;
+
+ for (const auto &[Id, EntityEdges] : R.Edges) {
+ Content.push_back(IdToJSON(Id));
+ Content.push_back(json::Value(edgeSetToJSON(EntityEdges, IdToJSON)));
+ }
+
+ json::Object Result;
+
+ Result[PointerFlowAnalysisResultName] = std::move(Content);
+ return Result;
+}
+
+Expected<std::unique_ptr<AnalysisResult>> deserializePointerFlowAnalysisResult(
+ const json::Object &Obj, JSONFormat::EntityIdFromJSONFn IdFromJSON) {
+ const json::Array *Content = Obj.getArray(PointerFlowAnalysisResultName);
+
+ if (!Content)
+ return makeSawButExpectedError(Obj, "an object with a key %s",
+ PointerFlowAnalysisResultName.data());
+
+ if (Content->size() % 2 != 0)
+ return makeSawButExpectedError(
+ *Content, "an even number of elements, got %lu",
+ (unsigned long)Content->size());
+
+ std::map<EntityId, EdgeSet> Edges;
+
+ for (size_t I = 0; I < Content->size(); I += 2) {
+ const json::Object *IdData = (*Content)[I].getAsObject();
+
+ if (!IdData)
+ return makeSawButExpectedError((*Content)[I],
+ "an object representing EntityId");
+
+ auto Id = IdFromJSON(*IdData);
+
+ if (!Id)
+ return Id.takeError();
+
+ const json::Array *EdgesData = (*Content)[I + 1].getAsArray();
+
+ if (!EdgesData)
+ return makeSawButExpectedError((*Content)[I + 1],
+ "an array of arrays representing EdgeSet");
+
+ auto EntityEdges = edgeSetFromJSON(*EdgesData, IdFromJSON);
+
+ if (!EntityEdges)
+ return EntityEdges.takeError();
+ Edges[*Id] = std::move(*EntityEdges);
+ }
+
+ auto Ret = std::make_unique<PointerFlowAnalysisResult>();
+
+ Ret->Edges = std::move(Edges);
+ return Ret;
+}
+
+JSONFormat::AnalysisResultRegistry::Add<PointerFlowAnalysisResult>
+ RegisterPointerFlowResultForJSON(serializePointerFlowAnalysisResult,
+ deserializePointerFlowAnalysisResult);
+
+class PointerFlowAnalysis final
+ : public SummaryAnalysis<PointerFlowAnalysisResult,
+ PointerFlowEntitySummary> {
+public:
+ llvm::Error add(EntityId Id,
+ const PointerFlowEntitySummary &Summary) override {
+ auto EdgesOfEntity = getEdges(Summary);
+
+ result().Edges[Id] = EdgeSet(EdgesOfEntity.begin(), EdgesOfEntity.end());
+ return llvm::Error::success();
+ }
+};
+
+AnalysisRegistry::Add<PointerFlowAnalysis>
+ RegisterPointerFlowAnalysis("Whole-program pointer flow analysis");
+
+} // namespace
+
+// NOLINTNEXTLINE(misc-use-internal-linkage)
+volatile int PointerFlowAnalysisAnchorSource = 0;
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowExtractor.cpp
index 8b892c266281c..40c4bc9ed3677 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowExtractor.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowExtractor.cpp
@@ -326,4 +326,4 @@ volatile int PointerFlowTUSummaryExtractorAnchorSource = 0;
static TUSummaryExtractorRegistry::Add<PointerFlowTUSummaryExtractor>
RegisterExtractor(PointerFlowEntitySummary::Name,
- "The TUSummaryExtractor for pointer assignments");
+ "Extract pointer flow information");
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.cpp
index 774e7e94ac67a..cbd8ac92c5eaf 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.cpp
@@ -10,6 +10,18 @@
using namespace clang;
+std::string describeJSONValue(const llvm::json::Value &V) {
+ return llvm::formatv("{0:2}", V).str();
+}
+
+std::string describeJSONValue(const llvm::json::Array &A) {
+ return llvm::formatv("array of size {0}", A.size()).str();
+}
+
+std::string describeJSONValue(const llvm::json::Object &O) {
+ return llvm::formatv("an object of {0} key(s)", O.size()).str();
+}
+
namespace {
// Traverses the AST and finds contributors.
class ContributorFinder : public DynamicRecursiveASTVisitor {
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
index e6759c1fb6e39..c798789fd12a8 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
@@ -16,6 +16,10 @@
#include "clang/AST/Decl.h"
#include "llvm/Support/JSON.h"
+std::string describeJSONValue(const llvm::json::Value &V);
+std::string describeJSONValue(const llvm::json::Array &A);
+std::string describeJSONValue(const llvm::json::Object &O);
+
template <typename NodeTy, typename... Ts>
llvm::Error makeErrAtNode(clang::ASTContext &Ctx, const NodeTy *N,
llvm::StringRef Fmt, const Ts &...Args) {
@@ -24,12 +28,11 @@ llvm::Error makeErrAtNode(clang::ASTContext &Ctx, const NodeTy *N,
LocStr.c_str());
}
-template <typename... Ts>
-llvm::Error makeSawButExpectedError(const llvm::json::Value &Saw,
- llvm::StringRef Expected,
+template <typename JSONTy, typename... Ts>
+llvm::Error makeSawButExpectedError(const JSONTy &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();
+ std::string SawStr = describeJSONValue(Saw);
return llvm::createStringError(Fmt.c_str(), SawStr.c_str(), ExpectedArgs...);
}
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
index ea5d2297b9836..e884484f9c729 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
@@ -36,11 +36,8 @@ ssaf::getUnsafeBuffers(const UnsafeBufferUsageEntitySummary &S) {
static Object serialize(const EntitySummary &S,
JSONFormat::EntityIdToJSONFn Fn) {
const auto &SS = static_cast<const UnsafeBufferUsageEntitySummary &>(S);
- Array UnsafeBuffersData;
-
- for (const auto &EPL : getUnsafeBuffers(SS))
- UnsafeBuffersData.push_back(entityPointerLevelToJSON(EPL, Fn));
- return Object{{SummarySerializationKey.data(), std::move(UnsafeBuffersData)}};
+ return Object{{SummarySerializationKey.data(),
+ entityPointerLevelSetToJSON(getUnsafeBuffers(SS), Fn)}};
}
static llvm::Expected<std::unique_ptr<EntitySummary>>
@@ -49,21 +46,14 @@ deserializeImpl(const Object &Data, JSONFormat::EntityIdFromJSONFn Fn) {
Data.getArray(SummarySerializationKey.data());
if (!UnsafeBuffersData)
- return makeSawButExpectedError(Object(Data), "an Object with a key %s",
+ return makeSawButExpectedError(Data, "an Object with a key %s",
SummarySerializationKey.data());
- EntityPointerLevelSet EPLs;
-
- for (const auto &EltData : *UnsafeBuffersData) {
- llvm::Expected<EntityPointerLevel> EPL =
- entityPointerLevelFromJSON(EltData, Fn);
-
- if (!EPL)
- return EPL.takeError();
- EPLs.insert(*EPL);
- }
+ auto EPLs = entityPointerLevelSetFromJSON(*UnsafeBuffersData, Fn);
+ if (!EPLs)
+ return EPLs.takeError();
return std::make_unique<UnsafeBufferUsageEntitySummary>(
- buildUnsafeBufferUsageEntitySummary(std::move(EPLs)));
+ buildUnsafeBufferUsageEntitySummary(std::move(*EPLs)));
}
static llvm::Expected<std::unique_ptr<EntitySummary>>
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageAnalysis.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageAnalysis.cpp
new file mode 100644
index 0000000000000..b856074ce988c
--- /dev/null
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageAnalysis.cpp
@@ -0,0 +1,121 @@
+//===- UnsafeBufferUsageAnalysis.cpp - WPA for UnsafeBufferUsage ----------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+// UnsafeBufferUsageAnalysis is a noop analysis.
+//
+// UnsafeBufferUsageAnalysisResult is a map from EntityIds to
+// EntityPointerLevelSets
+//===----------------------------------------------------------------------===//
+
+#include "SSAFAnalysesCommon.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageAnalysis.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisRegistry.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/SummaryAnalysis.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/JSON.h"
+#include <memory>
+
+using namespace clang::ssaf;
+using namespace llvm;
+
+namespace {
+
+json::Object serializeUnsafeBufferUsageAnalysisResult(
+ const UnsafeBufferUsageAnalysisResult &R,
+ JSONFormat::EntityIdToJSONFn IdToJSON) {
+ json::Array Content;
+
+ // Flat key-value pairs into an array of values:
+ for (auto &[Id, EPLs] : R.UnsafeBuffers) {
+ Content.push_back(IdToJSON(Id));
+ Content.push_back(entityPointerLevelSetToJSON(EPLs, IdToJSON));
+ }
+
+ json::Object Result;
+
+ Result[UnsafeBufferUsageAnalysisResultName] = std::move(Content);
+ return Result;
+}
+
+Expected<std::unique_ptr<AnalysisResult>>
+deserializeUnsafeBufferUsageAnalysisResult(
+ const json::Object &Obj, JSONFormat::EntityIdFromJSONFn IdFromJSON) {
+ const json::Array *Content = Obj.getArray(UnsafeBufferUsageAnalysisResultName);
+
+ if (!Content)
+ return makeSawButExpectedError(Obj, "an object with a key %s",
+ UnsafeBufferUsageAnalysisResultName.data());
+
+ if (Content->size() % 2 != 0)
+ return makeSawButExpectedError(
+ *Content, "an even number of elements, got %lu",
+ (unsigned long)Content->size());
+
+ std::map<EntityId, EntityPointerLevelSet> UnsafeBuffers;
+
+ for (size_t I = 0; I < Content->size(); I += 2) {
+ const json::Object *IdData = (*Content)[I].getAsObject();
+
+ if (!IdData)
+ return makeSawButExpectedError((*Content)[I],
+ "an object representing EntityId");
+
+ auto Id = IdFromJSON(*IdData);
+
+ if (!Id)
+ return Id.takeError();
+
+ const json::Array *EPLsData = (*Content)[I + 1].getAsArray();
+
+ if (!EPLsData)
+ return makeSawButExpectedError(
+ (*Content)[I + 1], "an array representing EntityPointerLevelSet");
+
+ auto EPLs = entityPointerLevelSetFromJSON(*EPLsData, IdFromJSON);
+
+ if (!EPLs)
+ return EPLs.takeError();
+ UnsafeBuffers[*Id] = std::move(*EPLs);
+ }
+
+ auto Ret = std::make_unique<UnsafeBufferUsageAnalysisResult>();
+
+ Ret->UnsafeBuffers = std::move(UnsafeBuffers);
+ return Ret;
+}
+
+JSONFormat::AnalysisResultRegistry::Add<UnsafeBufferUsageAnalysisResult>
+ RegisterUnsafeBufferUsageResultForJSON(
+ serializeUnsafeBufferUsageAnalysisResult,
+ deserializeUnsafeBufferUsageAnalysisResult);
+
+class UnsafeBufferUsageAnalysis final
+ : public SummaryAnalysis<UnsafeBufferUsageAnalysisResult,
+ UnsafeBufferUsageEntitySummary> {
+public:
+ llvm::Error add(EntityId Id,
+ const UnsafeBufferUsageEntitySummary &Summary) override {
+ auto UnsafeBuffersOfEntity = getUnsafeBuffers(Summary);
+
+ result().UnsafeBuffers[Id] = EntityPointerLevelSet(
+ UnsafeBuffersOfEntity.begin(), UnsafeBuffersOfEntity.end());
+ return llvm::Error::success();
+ }
+};
+
+AnalysisRegistry::Add<UnsafeBufferUsageAnalysis>
+ RegisterUnsafeBufferUsageAnalysis(
+ "Whole-program unsafe buffer usage analysis");
+
+} // namespace
+
+// NOLINTNEXTLINE(misc-use-internal-linkage)
+volatile int UnsafeBufferUsageAnalysisAnchorSource = 0;
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
index 78996bea040d8..9d1ce7589384e 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
@@ -104,4 +104,4 @@ volatile int UnsafeBufferUsageTUSummaryExtractorAnchorSource = 0;
static clang::ssaf::TUSummaryExtractorRegistry::Add<
UnsafeBufferUsageTUSummaryExtractor>
RegisterExtractor(UnsafeBufferUsageEntitySummary::Name,
- "The TUSummaryExtractor for unsafe buffer pointers");
+ "Extract unsafe buffer pointers");
diff --git a/clang/test/Analysis/Scalable/PointerFlow/Inputs/wpa-result-bad-edges.json b/clang/test/Analysis/Scalable/PointerFlow/Inputs/wpa-result-bad-edges.json
new file mode 100644
index 0000000000000..cd9084d5a69bd
--- /dev/null
+++ b/clang/test/Analysis/Scalable/PointerFlow/Inputs/wpa-result-bad-edges.json
@@ -0,0 +1,30 @@
+{
+ "id_table": [
+ {
+ "id": 0,
+ "name": {
+ "namespace": [
+ {
+ "kind": "LinkUnit",
+ "name": "test.exe"
+ }
+ ],
+ "suffix": "",
+ "usr": "c:@F at foo#"
+ }
+ }
+ ],
+ "results": [
+ {
+ "analysis_name": "PointerFlowAnalysisResult",
+ "result": {
+ "PointerFlowAnalysisResult": [
+ {
+ "@": 0
+ },
+ "not-an-array"
+ ]
+ }
+ }
+ ]
+}
diff --git a/clang/test/Analysis/Scalable/PointerFlow/Inputs/wpa-result-bad-id.json b/clang/test/Analysis/Scalable/PointerFlow/Inputs/wpa-result-bad-id.json
new file mode 100644
index 0000000000000..08102111e224d
--- /dev/null
+++ b/clang/test/Analysis/Scalable/PointerFlow/Inputs/wpa-result-bad-id.json
@@ -0,0 +1,14 @@
+{
+ "id_table": [],
+ "results": [
+ {
+ "analysis_name": "PointerFlowAnalysisResult",
+ "result": {
+ "PointerFlowAnalysisResult": [
+ "not-an-object",
+ []
+ ]
+ }
+ }
+ ]
+}
diff --git a/clang/test/Analysis/Scalable/PointerFlow/Inputs/wpa-result-empty.json b/clang/test/Analysis/Scalable/PointerFlow/Inputs/wpa-result-empty.json
new file mode 100644
index 0000000000000..2eed81bc8e990
--- /dev/null
+++ b/clang/test/Analysis/Scalable/PointerFlow/Inputs/wpa-result-empty.json
@@ -0,0 +1,11 @@
+{
+ "id_table": [],
+ "results": [
+ {
+ "analysis_name": "PointerFlowAnalysisResult",
+ "result": {
+ "PointerFlowAnalysisResult": []
+ }
+ }
+ ]
+}
diff --git a/clang/test/Analysis/Scalable/PointerFlow/Inputs/wpa-result-no-key.json b/clang/test/Analysis/Scalable/PointerFlow/Inputs/wpa-result-no-key.json
new file mode 100644
index 0000000000000..32bda55463253
--- /dev/null
+++ b/clang/test/Analysis/Scalable/PointerFlow/Inputs/wpa-result-no-key.json
@@ -0,0 +1,11 @@
+{
+ "id_table": [],
+ "results": [
+ {
+ "analysis_name": "PointerFlowAnalysisResult",
+ "result": {
+ "WrongKey": []
+ }
+ }
+ ]
+}
diff --git a/clang/test/Analysis/Scalable/PointerFlow/Inputs/wpa-result-odd-count.json b/clang/test/Analysis/Scalable/PointerFlow/Inputs/wpa-result-odd-count.json
new file mode 100644
index 0000000000000..32c36f549509a
--- /dev/null
+++ b/clang/test/Analysis/Scalable/PointerFlow/Inputs/wpa-result-odd-count.json
@@ -0,0 +1,29 @@
+{
+ "id_table": [
+ {
+ "id": 0,
+ "name": {
+ "namespace": [
+ {
+ "kind": "LinkUnit",
+ "name": "test.exe"
+ }
+ ],
+ "suffix": "",
+ "usr": "c:@F at foo#"
+ }
+ }
+ ],
+ "results": [
+ {
+ "analysis_name": "PointerFlowAnalysisResult",
+ "result": {
+ "PointerFlowAnalysisResult": [
+ {
+ "@": 0
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/clang/test/Analysis/Scalable/PointerFlow/Inputs/wpa-result.json b/clang/test/Analysis/Scalable/PointerFlow/Inputs/wpa-result.json
new file mode 100644
index 0000000000000..bd5b215d9ad9a
--- /dev/null
+++ b/clang/test/Analysis/Scalable/PointerFlow/Inputs/wpa-result.json
@@ -0,0 +1,128 @@
+{
+ "id_table": [
+ {
+ "id": 0,
+ "name": {
+ "namespace": [
+ {
+ "kind": "LinkUnit",
+ "name": "test.exe"
+ }
+ ],
+ "suffix": "",
+ "usr": "c:@F at foo#"
+ }
+ },
+ {
+ "id": 1,
+ "name": {
+ "namespace": [
+ {
+ "kind": "LinkUnit",
+ "name": "test.exe"
+ }
+ ],
+ "suffix": "1",
+ "usr": "c:@F at foo#"
+ }
+ },
+ {
+ "id": 2,
+ "name": {
+ "namespace": [
+ {
+ "kind": "LinkUnit",
+ "name": "test.exe"
+ }
+ ],
+ "suffix": "2",
+ "usr": "c:@F at foo#"
+ }
+ }
+ ],
+ "results": [
+ {
+ "analysis_name": "PointerFlowAnalysisResult",
+ "result": {
+ "PointerFlowAnalysisResult": [
+ {
+ "@": 0
+ },
+ [
+ [
+ [
+ {
+ "@": 1
+ },
+ 1
+ ],
+ [
+ {
+ "@": 0
+ },
+ 2
+ ],
+ [
+ {
+ "@": 2
+ },
+ 1
+ ],
+ [
+ {
+ "@": 2
+ },
+ 2
+ ]
+ ],
+ [
+ [
+ {
+ "@": 2
+ },
+ 1
+ ],
+ [
+ {
+ "@": 1
+ },
+ 2
+ ]
+ ]
+ ],
+ {
+ "@": 1
+ },
+ [
+ [
+ [
+ {
+ "@": 0
+ },
+ 1
+ ],
+ [
+ {
+ "@": 1
+ },
+ 1
+ ],
+ [
+ {
+ "@": 2
+ },
+ 1
+ ],
+ [
+ {
+ "@": 2
+ },
+ 3
+ ]
+ ]
+ ]
+ ]
+ }
+ }
+ ]
+}
diff --git a/clang/test/Analysis/Scalable/PointerFlow/tu-summary-serialization.test b/clang/test/Analysis/Scalable/PointerFlow/tu-summary-serialization.test
index c36e740350d45..34d38bb7a6200 100644
--- a/clang/test/Analysis/Scalable/PointerFlow/tu-summary-serialization.test
+++ b/clang/test/Analysis/Scalable/PointerFlow/tu-summary-serialization.test
@@ -5,11 +5,8 @@
// Negative tests:
// RUN: not clang-ssaf-format -type=tu %S/Inputs/tu-summary-no-key.json 2>&1 \
-// RUN: | FileCheck %s --check-prefix=CHECK-NO-KEY
-// CHECK-NO-KEY: saw {
-// CHECK-NO-KEY: "NoPointerFlow": [
-// CHECK-NO-KEY: [
-// CHECK-NO-KEY: } but expected a JSON object with the key: PointerFlow
+// RUN: | FileCheck %s --check-prefix=CHECK-MISSING-KEY
+// CHECK-MISSING-KEY: saw an object of 1 key(s) but expected a JSON object with the key: PointerFlow
// RUN: not clang-ssaf-format -type=tu %S/Inputs/tu-summary-bad-summary.json 2>&1 \
// RUN: | FileCheck %s --check-prefix=CHECK-BAD-SUMMARY
diff --git a/clang/test/Analysis/Scalable/PointerFlow/wpa-result-serialization.test b/clang/test/Analysis/Scalable/PointerFlow/wpa-result-serialization.test
new file mode 100644
index 0000000000000..fec997b3a173e
--- /dev/null
+++ b/clang/test/Analysis/Scalable/PointerFlow/wpa-result-serialization.test
@@ -0,0 +1,32 @@
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+
+// Round-trip tests:
+
+// RUN: clang-ssaf-format --type wpa %S/Inputs/wpa-result.json -o %t/wpa-result.json
+// RUN: diff %S/Inputs/wpa-result.json %t/wpa-result.json
+
+// RUN: clang-ssaf-format --type wpa %S/Inputs/wpa-result-empty.json -o %t/wpa-result-empty.json
+// RUN: diff %S/Inputs/wpa-result-empty.json %t/wpa-result-empty.json
+
+// Negative tests:
+
+// Missing the PointerFlowAnalysisResult key in the result object.
+// RUN: not clang-ssaf-format --type wpa %S/Inputs/wpa-result-no-key.json 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-MISSING-KEY
+// CHECK-MISSING-KEY: saw an object of 1 key(s) but expected an object with a key PointerFlowAnalysisResult
+
+// Odd number of elements in the flat array.
+// RUN: not clang-ssaf-format --type wpa %S/Inputs/wpa-result-odd-count.json 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-ODD-COUNT
+// CHECK-ODD-COUNT: saw array of size 1 but expected an even number of elements, got 1
+
+// EntityId slot is not an object.
+// RUN: not clang-ssaf-format --type wpa %S/Inputs/wpa-result-bad-id.json 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-BAD-ID
+// CHECK-BAD-ID: saw "not-an-object" but expected an object representing EntityId
+
+// EdgeSet slot is not an array.
+// RUN: not clang-ssaf-format --type wpa %S/Inputs/wpa-result-bad-edges.json 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-BAD-EDGES
+// CHECK-BAD-EDGES: saw "not-an-array" but expected an array of arrays representing EdgeSet
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/wpa-result-bad-epls.json b/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/wpa-result-bad-epls.json
new file mode 100644
index 0000000000000..9f2be6121ff05
--- /dev/null
+++ b/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/wpa-result-bad-epls.json
@@ -0,0 +1,30 @@
+{
+ "id_table": [
+ {
+ "id": 0,
+ "name": {
+ "namespace": [
+ {
+ "kind": "LinkUnit",
+ "name": "test.exe"
+ }
+ ],
+ "suffix": "",
+ "usr": "c:@F at foo#"
+ }
+ }
+ ],
+ "results": [
+ {
+ "analysis_name": "UnsafeBufferUsageAnalysisResult",
+ "result": {
+ "UnsafeBufferUsageAnalysisResult": [
+ {
+ "@": 0
+ },
+ "not-an-array"
+ ]
+ }
+ }
+ ]
+}
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/wpa-result-bad-id.json b/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/wpa-result-bad-id.json
new file mode 100644
index 0000000000000..58f1fabc2d16a
--- /dev/null
+++ b/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/wpa-result-bad-id.json
@@ -0,0 +1,14 @@
+{
+ "id_table": [],
+ "results": [
+ {
+ "analysis_name": "UnsafeBufferUsageAnalysisResult",
+ "result": {
+ "UnsafeBufferUsageAnalysisResult": [
+ "not-an-object",
+ []
+ ]
+ }
+ }
+ ]
+}
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/wpa-result-empty.json b/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/wpa-result-empty.json
new file mode 100644
index 0000000000000..89a58332cfd17
--- /dev/null
+++ b/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/wpa-result-empty.json
@@ -0,0 +1,11 @@
+{
+ "id_table": [],
+ "results": [
+ {
+ "analysis_name": "UnsafeBufferUsageAnalysisResult",
+ "result": {
+ "UnsafeBufferUsageAnalysisResult": []
+ }
+ }
+ ]
+}
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/wpa-result-no-key.json b/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/wpa-result-no-key.json
new file mode 100644
index 0000000000000..7d68ef8998871
--- /dev/null
+++ b/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/wpa-result-no-key.json
@@ -0,0 +1,11 @@
+{
+ "id_table": [],
+ "results": [
+ {
+ "analysis_name": "UnsafeBufferUsageAnalysisResult",
+ "result": {
+ "WrongKey": []
+ }
+ }
+ ]
+}
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/wpa-result-odd-count.json b/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/wpa-result-odd-count.json
new file mode 100644
index 0000000000000..12725357b5a30
--- /dev/null
+++ b/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/wpa-result-odd-count.json
@@ -0,0 +1,29 @@
+{
+ "id_table": [
+ {
+ "id": 0,
+ "name": {
+ "namespace": [
+ {
+ "kind": "LinkUnit",
+ "name": "test.exe"
+ }
+ ],
+ "suffix": "",
+ "usr": "c:@F at foo#"
+ }
+ }
+ ],
+ "results": [
+ {
+ "analysis_name": "UnsafeBufferUsageAnalysisResult",
+ "result": {
+ "UnsafeBufferUsageAnalysisResult": [
+ {
+ "@": 0
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/wpa-result.json b/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/wpa-result.json
new file mode 100644
index 0000000000000..a5b8c1cc9df7b
--- /dev/null
+++ b/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/wpa-result.json
@@ -0,0 +1,86 @@
+{
+ "id_table": [
+ {
+ "id": 0,
+ "name": {
+ "namespace": [
+ {
+ "kind": "LinkUnit",
+ "name": "test.exe"
+ }
+ ],
+ "suffix": "",
+ "usr": "c:@F at foo#"
+ }
+ },
+ {
+ "id": 1,
+ "name": {
+ "namespace": [
+ {
+ "kind": "LinkUnit",
+ "name": "test.exe"
+ }
+ ],
+ "suffix": "1",
+ "usr": "c:@F at foo#"
+ }
+ },
+ {
+ "id": 2,
+ "name": {
+ "namespace": [
+ {
+ "kind": "LinkUnit",
+ "name": "test.exe"
+ }
+ ],
+ "suffix": "2",
+ "usr": "c:@F at foo#"
+ }
+ }
+ ],
+ "results": [
+ {
+ "analysis_name": "UnsafeBufferUsageAnalysisResult",
+ "result": {
+ "UnsafeBufferUsageAnalysisResult": [
+ {
+ "@": 0
+ },
+ [
+ [
+ {
+ "@": 1
+ },
+ 1
+ ],
+ [
+ {
+ "@": 2
+ },
+ 2
+ ]
+ ],
+ {
+ "@": 1
+ },
+ [
+ [
+ {
+ "@": 0
+ },
+ 1
+ ],
+ [
+ {
+ "@": 0
+ },
+ 3
+ ]
+ ]
+ ]
+ }
+ }
+ ]
+}
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test
index ff13aa7f8ca27..7eced926e3e56 100644
--- a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test
+++ b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test
@@ -6,16 +6,7 @@
// RUN: not clang-ssaf-format -type=tu %S/Inputs/tu-summary-no-key.json 2>&1 \
// RUN: | FileCheck %s --check-prefix=CHECK-MISSING-KEY
-// CHECK-MISSING-KEY: saw {
-// CHECK-MISSING-KEY-NEXT: "NotUnsafeBuffers": [
-// CHECK-MISSING-KEY-NEXT: [
-// CHECK-MISSING-KEY-NEXT: {
-// CHECK-MISSING-KEY-NEXT: "@": 0
-// CHECK-MISSING-KEY-NEXT: },
-// CHECK-MISSING-KEY-NEXT: 1
-// CHECK-MISSING-KEY-NEXT: ]
-// CHECK-MISSING-KEY-NEXT: ]
-// CHECK-MISSING-KEY-NEXT: } but expected an Object with a key UnsafeBuffers
+// CHECK-MISSING-KEY: saw an object of 1 key(s) 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
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/wpa-result-serialization.test b/clang/test/Analysis/Scalable/UnsafeBufferUsage/wpa-result-serialization.test
new file mode 100644
index 0000000000000..ce60aa8b50ee2
--- /dev/null
+++ b/clang/test/Analysis/Scalable/UnsafeBufferUsage/wpa-result-serialization.test
@@ -0,0 +1,32 @@
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+
+// Round-trip tests:
+
+// RUN: clang-ssaf-format --type wpa %S/Inputs/wpa-result.json -o %t/wpa-result.json
+// RUN: diff %S/Inputs/wpa-result.json %t/wpa-result.json
+
+// RUN: clang-ssaf-format --type wpa %S/Inputs/wpa-result-empty.json -o %t/wpa-result-empty.json
+// RUN: diff %S/Inputs/wpa-result-empty.json %t/wpa-result-empty.json
+
+// Negative tests:
+
+// Missing the UnsafeBufferUsageAnalysisResult key in the result object.
+// RUN: not clang-ssaf-format --type wpa %S/Inputs/wpa-result-no-key.json 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-MISSING-KEY
+// CHECK-MISSING-KEY: saw an object of 1 key(s) but expected an object with a key UnsafeBufferUsageAnalysisResult
+
+// Odd number of elements in the flat array.
+// RUN: not clang-ssaf-format --type wpa %S/Inputs/wpa-result-odd-count.json 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-ODD-COUNT
+// CHECK-ODD-COUNT: saw array of size 1 but expected an even number of elements, got 1
+
+// EntityId slot is not an object.
+// RUN: not clang-ssaf-format --type wpa %S/Inputs/wpa-result-bad-id.json 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-BAD-ID
+// CHECK-BAD-ID: saw "not-an-object" but expected an object representing EntityId
+
+// EntityPointerLevelSet slot is not an array.
+// RUN: not clang-ssaf-format --type wpa %S/Inputs/wpa-result-bad-epls.json 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-BAD-EPLS
+// CHECK-BAD-EPLS: saw "not-an-array" but expected an array representing EntityPointerLevelSet
>From a07fb84d47f79a0e015ea66ce103519143fdd6ff Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Wed, 22 Apr 2026 11:45:13 -0700
Subject: [PATCH 4/5] fix clang-format
---
.../UnsafeBufferUsage/UnsafeBufferUsageAnalysis.cpp | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageAnalysis.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageAnalysis.cpp
index b856074ce988c..1f367720de30f 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageAnalysis.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageAnalysis.cpp
@@ -11,11 +11,11 @@
// EntityPointerLevelSets
//===----------------------------------------------------------------------===//
+#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageAnalysis.h"
#include "SSAFAnalysesCommon.h"
#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h"
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
-#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageAnalysis.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisRegistry.h"
#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/SummaryAnalysis.h"
@@ -48,16 +48,17 @@ json::Object serializeUnsafeBufferUsageAnalysisResult(
Expected<std::unique_ptr<AnalysisResult>>
deserializeUnsafeBufferUsageAnalysisResult(
const json::Object &Obj, JSONFormat::EntityIdFromJSONFn IdFromJSON) {
- const json::Array *Content = Obj.getArray(UnsafeBufferUsageAnalysisResultName);
+ const json::Array *Content =
+ Obj.getArray(UnsafeBufferUsageAnalysisResultName);
if (!Content)
return makeSawButExpectedError(Obj, "an object with a key %s",
UnsafeBufferUsageAnalysisResultName.data());
if (Content->size() % 2 != 0)
- return makeSawButExpectedError(
- *Content, "an even number of elements, got %lu",
- (unsigned long)Content->size());
+ return makeSawButExpectedError(*Content,
+ "an even number of elements, got %lu",
+ (unsigned long)Content->size());
std::map<EntityId, EntityPointerLevelSet> UnsafeBuffers;
>From 54759df3236809bfc97e3fb727414439ed2eb885 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Wed, 22 Apr 2026 11:52:03 -0700
Subject: [PATCH 5/5] Change 'result()' to 'getResult()'
---
.../Analyses/PointerFlow/PointerFlowAnalysis.cpp | 2 +-
.../Analyses/UnsafeBufferUsage/UnsafeBufferUsageAnalysis.cpp | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowAnalysis.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowAnalysis.cpp
index c6f2f6ddc7288..82061b49b1929 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowAnalysis.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowAnalysis.cpp
@@ -102,7 +102,7 @@ class PointerFlowAnalysis final
const PointerFlowEntitySummary &Summary) override {
auto EdgesOfEntity = getEdges(Summary);
- result().Edges[Id] = EdgeSet(EdgesOfEntity.begin(), EdgesOfEntity.end());
+ getResult().Edges[Id] = EdgeSet(EdgesOfEntity.begin(), EdgesOfEntity.end());
return llvm::Error::success();
}
};
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageAnalysis.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageAnalysis.cpp
index 1f367720de30f..8860cf781c856 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageAnalysis.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageAnalysis.cpp
@@ -106,7 +106,7 @@ class UnsafeBufferUsageAnalysis final
const UnsafeBufferUsageEntitySummary &Summary) override {
auto UnsafeBuffersOfEntity = getUnsafeBuffers(Summary);
- result().UnsafeBuffers[Id] = EntityPointerLevelSet(
+ getResult().UnsafeBuffers[Id] = EntityPointerLevelSet(
UnsafeBuffersOfEntity.begin(), UnsafeBuffersOfEntity.end());
return llvm::Error::success();
}
More information about the llvm-branch-commits
mailing list